我是初学者学习套接字编程并尝试在Java中实现简单的类似Sinatra的库。 while循环以客户端套接字关闭或不关闭为止(!this.client.isClosed()),到目前为止,工作响应最小请求,如“GET / HTTP / 1.1”,直到客户端关闭连接并且循环进入无限远循环并采取所有cpu周期,我也曾尝试添加额外的条件检查可用性客户端的InputStream返回-1(这意味着关闭?),而(!this.client.isClosed()&&(reader.available ()!= -1))。
WebServer.java
public void start(){
try{
while(true){
// accept and add incoming socket connection to client bulks
this.clients.put(++requestNumber, this.server.accept());
// service client request in Service thread
Service service = new Service(this);
service.start();
}
}
catch(IOException ioe){
System.out.println(ioe.getMessage());
}
finally{
// TODO: close server socket here
}
}// end start
Service.java
@Override
public void run(){
while(!this.client.isClosed()){
System.out.println("read request");
// parse http request
this.parser.parse();
String method = this.parser.getMethod();
String path = this.parser.getPath();
String httpVer = this.parser.getHttpVersion();
System.out.println("[METHOD] : "+method);
System.out.println("[PATH] : "+path);
System.out.println("[HTTP VERSION] : "+httpVer);
// match request with specified handler in handlers bulk
for(RequestHandler handler:this.server.handlers){
if(handler.getMethod().equals(method)){
if(handler.getPath().equals(path)){
System.out.println("send response\n=============");
// serve request
handler.handle(this.reader, this.writer);
}
}
}// end for loop
// debug only
try{Thread.currentThread().sleep(3000);}catch(InterruptedException ie){}
}// end while loop
// not get executed after client disconnect
System.out.println("exit service thread");
// remove client socket in client sockets bulk
server.clients.remove(requestId);
}// end run
HttpParser.java
// start HttpParser
public class HttpParser{
private BufferedInputStream in;
private String initial;
private String header;
public HttpParser(BufferedInputStream in){
this.in = in;
}
public void parse(){
System.out.println("parse request");
// pattern to match both \r\n or \n
Pattern pattern = Pattern.compile("\r?\n", Pattern.DOTALL);
Matcher match = null;
Scanner scanner = null;
StringBuilder requestBuffer = new StringBuilder("");
String request = "";
int nread = 0, nlength = 0;
try{
scanner = new Scanner(in).useDelimiter(pattern);
while((in.available() != -1) && scanner.hasNext()){
// read line from request
String line = scanner.next();
// break while loop if find empty line "\r\n\r\n" or "\n\n"
if(line.isEmpty()){break;}
// append line to buffer
requestBuffer.append(line+"\r\n");
}
// get full request from buffer
request = requestBuffer.toString();
System.out.println("[REQUEST]\n"+request);
// get initial header from request
match = Pattern.compile("^.*?\r\n").matcher(request);
if(match.find()){
// assign initial header to initial member
this.initial = match.group();
System.out.println("[INITIAL HEADER]\n"+this.initial);
}
}
catch(IOException ioe){}
}// end parse
// get method from initial header
public String getMethod(){
Pattern pattern = Pattern.compile("(^.*?)\\s+");
Matcher match = pattern.matcher(this.initial);
if(match.find()){
return match.group(1);
}
return "GET";
}
// get path from initial header
public String getPath(){
Pattern pattern = Pattern.compile("^.*?\\s+(.+?)\\s+");
Matcher match = pattern.matcher(this.initial);
if(match.find()){
return match.group(1);
}
return "/";
}
// get http version from initial header
public String getHttpVersion(){
Pattern pattern = Pattern.compile("^.*?\\s+.+?\\s+HTTP/(\\d\\.\\d)\\s.*");
Matcher match = pattern.matcher(this.initial);
if(match.find()){
return match.group(1);
}
return "1.1";
}
}
// end HttpParser
主类Jciw.java
import java.io.*;
// start Jciw
public class Jciw{
public static void main(String[] args){
WebServer webServer = new WebServer(8000);
webServer.handle_request("GET", "/", new RootGetHandler());
webServer.handle_request("POST", "/", new RootPostHandler());
webServer.start();
}
// start RootGetHandler
public static class RootGetHandler extends RequestHandler{
@Override
public void handle(InputStream reader, OutputStream writer){
try{
writer.write(("Hello GET request\n").getBytes());
writer.flush();
}
catch(IOException ioe){
}
}
}
// end RootGetHandler
// start RootPostHandler
public static class RootPostHandler extends RequestHandler{
@Override
public void handle(InputStream reader, OutputStream writer){
try{
writer.write(("Hello POST request\n").getBytes());
writer.flush();
}
catch(IOException ioe){
}
}
}
// end RootPostHandler
}
// end Jciw
使用ant运行并从浏览器发出测试请求,记录:
run:
[java] read request
[java] parse request
[java] [REQUEST]
[java] GET / HTTP/1.1
[java] Host: localhost:8000
[java] User-Agent: Mozilla/5.0 (X11; Linux i686; rv:22.0) Gecko/20100101 Firefox/22.0 SeaMonkey/2.19
[java] Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
[java] Accept-Language: en-US,en;q=0.5
[java] Accept-Encoding: gzip, deflate
[java] Connection: keep-alive
[java]
[java] [INITIAL HEADER]
[java] GET / HTTP/1.1
[java]
[java] [METHOD] : GET
[java] [PATH] : /
[java] [HTTP VERSION] : 1.1
[java] send response
[java] =============
关闭标签页或断开与客户端的连接后,它会进入无限循环,而客户端套接字已关闭,而(!this.client.isClosed())应评估为false,服务线程永不退出,日志:
[java] read request
[java] parse request
[java] [REQUEST]
[java]
[java] [METHOD] : GET
[java] [PATH] : /
[java] [HTTP VERSION] : 1.1
[java] send response
[java] =============
[java] read request
[java] parse request
[java] [REQUEST]
[java]
[java] [METHOD] : GET
[java] [PATH] : /
[java] [HTTP VERSION] : 1.1
[java] send response
[java] =============
[java] read request
[java] parse request
[java] [REQUEST]
[java]
[java] [METHOD] : GET
[java] [PATH] : /
[java] [HTTP VERSION] : 1.1
[java] send response
[java] =============
[java] read request
[java] parse request
[java] [REQUEST]
[java]
[java] [METHOD] : GET
[java] [PATH] : /
[java] [HTTP VERSION] : 1.1
[java] send response
[java] =============
[java] read request
[java] parse request
[java] [REQUEST]
[java]
[java] [METHOD] : GET
[java] [PATH] : /
[java] [HTTP VERSION] : 1.1
[java] send response
[java] =============
[java] read request
[java] parse request
[java] [REQUEST]
[java]
[java] [METHOD] : GET
[java] [PATH] : /
[java] [HTTP VERSION] : 1.1
[java] send response
[java] =============
答案 0 :(得分:1)
您的循环条件不正确。 Socket.isClosed()
仅告诉您您是否已关闭套接字。要检测对等方的关闭,您必须检查您正在使用的任何读取方法的返回值:
read()
返回-1 readLine()
返回null
readObject()
和所有其他readXXX()
方法抛出EOFException.
当你得到任何这些时,你必须关闭套接字并纾困。
毫无疑问,你的写作会得到IOException: connection reset
,但是因为你忽略了所有IOExceptions
你没有看到它们。不要忽视IOExceptions
。只有一个对连接不致命,并且在读取时为SocketTimeoutException
。当你得到任何其他人时,你必须关闭套接字并纾困。
对(in.available() != -1)
的测试完全没有意义。它永远不会返回-1,并且它没有返回任何表示流结束的内容。
答案 1 :(得分:-1)
谢谢@EJP,解决了,我添加了辅助方法来检查while循环条件下的客户端连接
private boolean isClosed(){
int nread = 0;
try{
this.reader.mark(1);
nread = this.reader.read();
this.reader.reset();
}
catch(IOException ioe){
//TODO: when something goes wrong
}
return ( (nread != -1)?false:true );
}
...
while(!isClosed()){
...
}