GET 및 HEAD 요청을 처리하는 간단한 HTTP/1.1 호환 다중 스레드 웹 서버를 구현했습니다. 웹 서버를 통해 요청을하면 작동하지만 12 초의 시간 초과 후 SocketTimeoutException이 발생합니다.다중 스레드 Java 웹 서버 - java.net.SocketTimeoutException
Eclipse에서 실행하고 브라우저에서 localhost : portnumber로 이동 한 다음 로컬로 파일을 열어 웹 서버를 테스트하고 있습니다. 나는 그것이 없다면, 존재하지 않는 파일을 읽으려는 요청은 단순히 반환되지 않고, 404 찾을 수 없음 오류를 반환해야하기 때문에 시간 제한 값만 있습니다.
받은 SocketTimeoutExceptions 수는 요청을 처리하기 위해 열린 소켓 수와 같습니다. 나는이 예외를 어떻게 든 처리해야한다고 생각하지만, 어디서 어떻게해야하는지 잘 모르겠습니다. 이것을 처리하는 방법에 대한 예제 나 설명은 매우 유용합니다.
내 코드는 짧은 웹 서버 구성 요소와 분리 된 ThreadHandler 클래스로 분할되어 요청을 처리합니다. 새 클라이언트 소켓을 만들 때 새 스레드를 사용하여 요청을 처리합니다. 필요한 경우 ThreadHandler 클래스를 제공 할 수 있지만 훨씬 더 길다. 여기
은 웹 서버의 구성 요소입니다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"토큰이 포함되어 있으면 각 소켓 만 닫힙니다. 그러나, 내가 제대로 존재하지 않는 파일을 열려고 시도하고 404 Not Found Error를 반환해야하는 경우에 이것이 제대로 발생하는지 확신 할 수 없습니다.
누구나 이러한 예외를 처리하는 방법에 대한 아이디어가 있습니까? 스레드를 닫으려는 작업을해야합니까?
도움을 주시면 감사하겠습니다.
편집 :()
//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;
//}
}
}
잘못된 코드가 HandleThreads 클래스에있을 가능성이 큽니다. 실행 방법의 내용을 게시 할 수 있습니까? –
내 run 메서드는 예외를 처리 할 수 있도록 handleRequest()라는 다른 메서드를 호출합니다. 원래 상자에 코드를 게시합니다. 시간 내 줘서 고마워. – JCutz
요청할 때마다 연결을 닫아야합니다. 그래서 404 응답이 클라이언트에게 반환되지 않습니다. 그렇지 않으면 반환 한 내용 다음에 CRLF 문자열이 필요하다고 생각합니다. HTTPD 프로토콜을 읽어야합니다. – Gray