0

GETおよびHEADリクエストを処理する単純なHTTP/1.1準拠のマルチスレッドWebサーバーを実装しました。Webサーバーを介してリクエストを行うと、機能しますが、設定した12秒のタイムアウト後にSocketTimeoutExceptionが発生します。

WebサーバーをEclipseで実行し、ブラウザーをlocalhost:portnumberに移動してから、ファイルをローカルで開こうとして、Webサーバーをテストしています。タイムアウト値がない場合は、存在しないファイルを読み取る要求は返されませんが、404NotFoundエラーが返されるはずです。

私が受け取るSocketTimeoutExceptionsの数は、リクエストを処理するために開かれたソケットの数と同じです。どういうわけかこの例外を処理する必要があると思いますが、どこで、どのように処理するのかわかりません。これを処理する方法の例や説明は非常に役立ちます。

私のコードは短いウェブサーバーコンポーネントに分割され、その後にリクエストを処理するための個別のThreadHandlerクラスが続きます。新しいクライアントソケットを作成するときは、新しいスレッドを使用してリクエストを処理します。必要に応じてThreadHandlerクラスを提供できますが、それははるかに長くなります。

WebServerコンポーネントは次のとおりです。

public class MyWebServer
{
    public static void main(String[] args) throws Exception
    {

        int port = 5000;
        String rootpath = "~/Documents/MockWebServerDocument/";

        if(rootpath.startsWith("~" + File.separator))
        {
            rootpath = System.getProperty("user.home") + rootpath.substring(1);
        }

        File testFile = new File(rootpath);

        //If the provided rootpath doesn't exist, or isn't a directory, exit
        if(!testFile.exists() || !testFile.isDirectory())
        {
            System.out.println("The provided rootpath either does not exist, or is not a directory. Exiting!");
            System.exit(1);
        }

        //Create the server socket
        ServerSocket serverSocket = new ServerSocket(port);

        //We want to process requests indefinitely, listen for connections inside an infinite loop
        while(true)
        {
            //The server socket waits for a client to connect and make a request. The timeout ensures that
            //a read request does not block for more than the allotted period of time. 
            Socket connectionSocket = serverSocket.accept();
            connectionSocket.setSoTimeout(12*1000);

            //When a connection is received, we want to create a new HandleRequest object
            //We pass the newly created socket as an argument to HandleRequest's constructor
            HandleThreads request = new HandleThreads(connectionSocket, rootpath);

            //Create thread for the request
            Thread requestThread = new Thread(request);

            System.out.println("Starting New Thread");

            //Start thread
            requestThread.start();
        }
    }

}

ThreadHandlerクラス内で、ソケットから要求を読み取り、要求を解析して、ソケットを介して適切な応答で応答します。持続的接続を実装したので、各ソケットは、リクエストに「Connection:close」トークンがリクエスト内に含まれている場合にのみ閉じられます。ただし、これが適切に発生しているかどうかはわかりません。特に、存在しないファイルを開こうとした場合、404NotFoundエラーが返されるはずです。

誰かがこれらの例外を処理する方法について何か考えを持っていますか?スレッドを閉じるために何かをする必要がありますか?

どんな助けでも大歓迎です。

編集:これは、run()のtrycatchステートメント内から呼び出すhandleRequest()です。

//This method handles any requests received through the client socket
    private void handleRequest() throws Exception
    {
        //Create outputStream to send data to client socket
        DataOutputStream outToClient = new DataOutputStream(clientsocket.getOutputStream());
        //Create BufferedReader to read data in from client socket
        BufferedReader inFromClient = new BufferedReader(new InputStreamReader(clientsocket.getInputStream()));
        //Create SimpleDateFormat object to match date format expected by HTTP
        SimpleDateFormat HTTPDateFormat = new SimpleDateFormat("EEE MMM d hh:mm:ss zzz yyyy");

        //Keep running while the socket is open
        while(clientsocket.isConnected())
        {

                String line = null;
                //HashMap to record relevant header lines as they are read
                HashMap<String,String> requestLines = new HashMap<String,String>();
                String ifModSince = null;
                Date ifModifiedSince = null;
                Date lastModifiedDate = null;

                //Keep reading the request lines until the end of the request is signalled by a blank line
                while ((line = inFromClient.readLine()).length() != 0) 
                {
                    //To capture the request line
                    if(!line.contains(":"))
                    {
                        requestLines.put("Request", line);
                    }

                    //To capture the connection status
                    if(line.startsWith("Connection:"))
                    {
                        int index = line.indexOf(':');
                        String connectionStatus = line.substring(index + 2);
                        requestLines.put("Connection", connectionStatus);
                    }

                    //To capture the if-modified-since date, if present in the request
                    if(line.startsWith("If-Modified-Since"))
                    {
                        int index = line.indexOf(':');
                        ifModSince = line.substring(index + 2);
                        requestLines.put("If-Modified-Since", ifModSince);
                    }

                    System.out.println(line);

                }

                //Get the request line from the HashMap
                String requestLine = (String)requestLines.get("Request");
                //Create Stringtokenizer to help separate components of the request line
                StringTokenizer tokens = new StringTokenizer(requestLine);

                //If there are not 3 distinct components in the request line, then the request does
                //not follow expected syntax and we should return a 400 Bad Request error
                if(tokens.countTokens() != 3)
                {
                    outToClient.writeBytes("HTTP/1.1 400 Bad Request"+CRLF);
                    outToClient.writeBytes("Content-Type: text/html"+CRLF);
                    outToClient.writeBytes("Server: BCServer/1.0"+CRLF);
                    outToClient.writeBytes("Connection: keep-alive"+CRLF);
                    outToClient.writeBytes(CRLF);
                    outToClient.writeBytes("<html><head></head><body>Error 400 - Bad Request</body></html>"+CRLF);

                    outToClient.flush();
                }
                else
                {
                    //Get the specific request, whether "GET", "HEAD" or unknown
                    String command = tokens.nextToken();
                    //Get the filename from the request
                    String filename = tokens.nextToken();

                    //Tidy up the recovered filename. This method can also tidy up absolute
                    //URI requests
                    filename = cleanUpFilename(filename);

                    //If the third token does not equal HTTP/1.1, then the request does 
                    //not follow expected syntax and we should return a 404 Bad Request Error
                    if(!(tokens.nextElement().equals("HTTP/1.1")))
                    {
                        outToClient.writeBytes("HTTP/1.1 400 Bad Request"+CRLF);
                        outToClient.writeBytes("Content-Type: text/html"+CRLF);
                        outToClient.writeBytes("Server: BCServer/1.0"+CRLF);
                        outToClient.writeBytes("Connection: keep-alive"+CRLF);
                        outToClient.writeBytes(CRLF);
                        outToClient.writeBytes("<html><head></head><body>Error 400 - Bad Request</body></html>"+CRLF);
                        outToClient.flush();                    
                    }
                    else
                    {
                        //Add the supplied rootpath to the recovered filename
                        String fullFilepath = rootpath + filename;

                        //Create a new file using the full filepathname
                        File file = new File(fullFilepath);

                        //If the created file is a directory then we look to return index.html
                        if(file.isDirectory())
                        {
                            //Add index.html to the supplied rootpath
                            fullFilepath = rootpath + "index.html";

                            //Check to see if index.html exists. If not, then return Error 404: Not Found
                            if(!new File(fullFilepath).exists())
                            {
                                outToClient.writeBytes("HTTP/1.1 404 Not Found"+CRLF);
                                outToClient.writeBytes("Content-Type: text/html"+CRLF);
                                outToClient.writeBytes("Server: BCServer/1.0"+CRLF);
                                outToClient.writeBytes("Connection: keep-alive"+CRLF);
                                outToClient.writeBytes(CRLF);
                                outToClient.writeBytes("<html><head></head><body>Error 404 - index.html was not found</body></html>"+CRLF);
                                outToClient.flush();
                            }
                        }
                        //If the created file simply does not exist then we need to return Error 404: Not Found
                        else if(!file.exists())
                        {
                            System.out.println("File Doesn't Exist!");
                            outToClient.writeBytes("HTTP/1.1 404 Not Found"+CRLF);
                            outToClient.writeBytes("Content-Type: text/html"+CRLF);
                            outToClient.writeBytes("Server: BCServer/1.0"+CRLF);
                            outToClient.writeBytes("Connection: keep-alive"+CRLF);
                            outToClient.writeBytes(CRLF);
                            outToClient.writeBytes("<html><head></head><body>Error 404 - " + filename + " was not found</body></html>"+CRLF);
                            outToClient.flush();

                        }
                        //Otherwise, we have a well formed request, and we should use the specific command to
                        //help us decide how to respond
                        else
                        {
                            //Get the number of bytes in the file
                            int numOfBytes=(int)file.length();

                            //If we are given a GET request
                            if(command.equals("GET"))
                            {
                                //Open a file input stream using the full file pathname
                                FileInputStream inFile = new FileInputStream(fullFilepath);

                                //Create an array of bytes to hold the data from the file
                                byte[] fileinBytes = new byte[numOfBytes];

                                //We now check the If-Modified-Since date (if present) against the file's
                                //last modified date. If the file has not been modified, then return 304: Not Modified
                                if(ifModSince != null)
                                {
                                    //Put the string version of If-Modified-Since data into the HTTPDate Format
                                    try
                                    {
                                        ifModifiedSince = HTTPDateFormat.parse(ifModSince);
                                    }
                                    catch(ParseException e)
                                    {
                                        e.printStackTrace();
                                    }

                                    //We now need to do a bit of rearranging to get the last modified date of the file
                                    //in the correct HTTP Date Format to allow us to directly compare two date object

                                    //1. Create a new Date using the epoch time from file.lastModified()
                                    lastModifiedDate = new Date(file.lastModified());
                                    //2. Create a string version, formatted into our correct format
                                    String lastMod = HTTPDateFormat.format(lastModifiedDate);

                                    lastModifiedDate = new Date();
                                    //3. Finally, parse this string version into a Date object we can use in a comparison
                                    try
                                    {
                                        lastModifiedDate = HTTPDateFormat.parse(lastMod);
                                    }
                                    catch (ParseException e)
                                    {
                                        e.printStackTrace();
                                    }

                                    //Comparing the last modified date to the "If-Modified Since" date, if the last modified
                                    //date is before modified since date, return Status Code 304: Not Modified
                                    if((ifModifiedSince != null) && (lastModifiedDate.compareTo(ifModifiedSince) <= 0))
                                    {
                                        System.out.println("Not Modified!");
                                        //Write the header to the output stream 
                                        outToClient.writeBytes("HTTP/1.1 304 Not Modified"+CRLF);
                                        outToClient.writeBytes("Date: " + HTTPDateFormat.format(new Date())+CRLF);
                                        outToClient.writeBytes("Server: BCServer/1.0"+CRLF);
                                        outToClient.writeBytes("Last-Modified: " + lastModifiedDate+CRLF);
                                        outToClient.writeBytes("Content-Length: " + (int)file.length()+CRLF);
                                        outToClient.writeBytes(CRLF);
                                    }                                   

                                }
                                else
                                {
                                    //Read in the data from the file using the input stream and store in the byte array
                                    inFile.read(fileinBytes);

                                    //Write the header to the output stream 
                                    outToClient.writeBytes("HTTP/1.1 200 OK"+CRLF);
                                    outToClient.writeBytes("Date: " + HTTPDateFormat.format(new Date())+CRLF);
                                    outToClient.writeBytes("Server: BCServer/1.0"+CRLF);
                                    outToClient.writeBytes("Connection: keep-alive"+CRLF);
                                    outToClient.writeBytes("Last-Modified: " + HTTPDateFormat.format(file.lastModified())+CRLF);
                                    outToClient.writeBytes("Content-Length: " + numOfBytes +CRLF);
                                    outToClient.writeBytes(CRLF);

                                    //Write the file
                                    outToClient.write(fileinBytes,0,numOfBytes);
                                    outToClient.flush();                                    
                                }

                            }   
                            //If we are given a HEAD request
                            else if(command.equals("HEAD"))
                            {
                                //Write the header to the output stream 
                                outToClient.writeBytes("HTTP/1.1 200 OK"+CRLF);
                                outToClient.writeBytes("Date: " + HTTPDateFormat.format(new Date())+CRLF);
                                outToClient.writeBytes("Server: BCServer/1.0"+CRLF);
                                outToClient.writeBytes("Connection: keep-alive"+CRLF);
                                outToClient.writeBytes("Last-Modified: " + HTTPDateFormat.format(file.lastModified())+CRLF);
                                outToClient.writeBytes("Content-Length: " + numOfBytes +CRLF);
                                outToClient.writeBytes(CRLF);

                                outToClient.flush();
                            }
                            //If the command is neither GET or HEAD, then this type of request has
                            //not been implemented. In this case, we must return Error 501: Not Implemented
                            else
                            {
                                //Print the header and error information            
                                outToClient.writeBytes("HTTP/1.1 501 Not Implemented"+CRLF);
                                outToClient.writeBytes("Date: " + HTTPDateFormat.format(new Date())+CRLF);
                                outToClient.writeBytes("Server: BCServer/1.0"+CRLF);
                                outToClient.writeBytes("Connection: keep-alive"+CRLF);
                                outToClient.writeBytes("Content-Type: text/html"+CRLF);
                                outToClient.writeBytes(CRLF);
                                outToClient.writeBytes("<html><head></head><body> Desired Action Not Implemented </body></html>"+CRLF);

                                outToClient.flush();

                            }
                        }                                               
                    }
                }

                //Get the connection status for this request from the HashMap
                String connect = (String)requestLines.get("Connection");
                //If the connection status is not "keep alive" then we must close the socket
                if(!connect.equals("keep-alive"))
                {
                    // Close streams and socket.
                    outToClient.close();
                    inFromClient.close();
                    clientsocket.close();                   
                }
                //Otherwise, we can continue using this socket
                //else
                //{                 
                    //continue;
                //}
            }
    }   
4

1 に答える 1

1

読み取りタイムアウトを設定する理由は、ピアがデータを送信するのを待つために費やす準備ができている時間に上限を設けるためです。その制限がどうあるべきか、読み取りを再試行する準備ができている頻度(もしあれば:おそらく1つのタイムアウトで十分です)、および長すぎる長さを知っているのはあなただけですが、ある時点でそれを決定して閉じるだけです接続。ほとんどのHTTPサーバーはこれを構成可能にしているため、ユーザーが決定できます。

于 2013-03-01T01:37:24.327 に答える