具有 Keep-Alive 能力的 HTTP 服务器
我正在尝试用 Java 创建一个能够提供保持活动连接的 http 服务器。我正在使用 com.sun.net.httpserver.HttpServer 类。
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
public class httpHandler implements HttpHandler {
private String resp = "<?xml version='1.0'?><root-node></root-node>";
private OutputStream os = null;
public void handle(HttpExchange t) throws IOException {
System.out.println("Handling message...");
java.io.InputStream is = t.getRequestBody();
System.out.println("Got request body. Reading request body...");
byte[] b = new byte[500];
is.read(b);
System.out.println("This is the request: " + new String(b));
String response = resp;
Headers header = t.getResponseHeaders();
header.add("Connection", "Keep-Alive");
header.add("Keep-Alive", "timeout=14 max=100");
header.add("Content-Type", "application/soap+xml");
t.sendResponseHeaders(200, response.length());
if(os == null) {
os = t.getResponseBody();
}
os.write(response.getBytes());
System.out.println("Done with exchange. Closing connection");
os.close();
}
public static void main(String[] args) {
HttpServer server = null;
try {
server = HttpServer.create(new InetSocketAddress(8080), 5);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
server.createContext("/", new httpHandler());
server.setExecutor(null); // creates a default executor
System.out.println("Starting server...");
server.start();
}
}
客户端不会关闭连接。服务器似乎在交换发生后直接关闭它。我尝试删除 os.close 行,但服务器将不会回复第二个请求。但它也没有关闭它。我有一种感觉,它涉及到在主代码中使用服务器对象做一些事情,但我不知道是什么。谷歌也没有出现太多。
这里有人有什么想法吗?任何帮助将不胜感激。
I'm trying to create a http server in Java which is capable of providing keep-alive connections. I'm using the com.sun.net.httpserver.HttpServer class.
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
public class httpHandler implements HttpHandler {
private String resp = "<?xml version='1.0'?><root-node></root-node>";
private OutputStream os = null;
public void handle(HttpExchange t) throws IOException {
System.out.println("Handling message...");
java.io.InputStream is = t.getRequestBody();
System.out.println("Got request body. Reading request body...");
byte[] b = new byte[500];
is.read(b);
System.out.println("This is the request: " + new String(b));
String response = resp;
Headers header = t.getResponseHeaders();
header.add("Connection", "Keep-Alive");
header.add("Keep-Alive", "timeout=14 max=100");
header.add("Content-Type", "application/soap+xml");
t.sendResponseHeaders(200, response.length());
if(os == null) {
os = t.getResponseBody();
}
os.write(response.getBytes());
System.out.println("Done with exchange. Closing connection");
os.close();
}
public static void main(String[] args) {
HttpServer server = null;
try {
server = HttpServer.create(new InetSocketAddress(8080), 5);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
server.createContext("/", new httpHandler());
server.setExecutor(null); // creates a default executor
System.out.println("Starting server...");
server.start();
}
}
The client does not close the connection. The server seems to close it instead directly after the exchange has occurred. I tried deleting the os.close line but then the server will not reply to the second request. But it doesn't close it either. I have a feeling it involves doing something in the main code with the server object but I have no idea what. Google isn't turning up much either.
Anyone here got any ideas? Any help would be much appreciated.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
看来问题是您没有从请求中耗尽所有数据。您应该继续执行
is.read()
直到它返回 -1,然后关闭它。由于您没有耗尽请求,因此仍然剩余字节。服务器不能简单地“跳转”到下一个请求;它不像磁盘,更像磁带。服务器必须读取(并丢弃)当前请求中的所有数据,然后才能到达下一个请求。
如果没有限制,可以用来攻击服务器;因此服务器只会尝试耗尽上限,默认情况下为 64K。您可能收到大于 64K 的请求。
通常处理程序应该首先读取整个请求。否则它怎么知道如何满足请求?
更严重的是,如果不先耗尽请求,可能会发生死锁。客户端通常很简单:它们写入请求,然后读取响应。如果服务器在读取所有请求之前写入响应,则客户端可能仍在写入请求。两者都在互相写信,但都没有阅读。如果缓冲区已满,那么我们就陷入了死锁,两者都在写入时被阻止。请注意,write() 没有超时!
It looks like the problem is you didn't drain all data from request. You should keep doing
is.read()
until it returns -1, then close it.Since you didn't drain the request, there are still bytes left. The server cannot simply "jump" to the next request; it's not like a disk, more like a tape. Server has to read(and discard) all data from current request, before the it can reach the next request.
Without a limit this can be used to attack the server; so the server would only attempt to drain up to a limit, which is 64K by default. You are probably receiving a request bigger than 64K.
Usually the handler should read the entire request first. Otherwise how does it know how to serve the request?
More seriously, if the request is not drained first, deadlock can happen. Clients are typically simple: they writes the request, then read the response. If the server writes the response before it reads all the request, the client could be still writing the request. Both are writing to each other but neither is reading. If buffers are full then we are in a deadlock, both are blocked on write. Note there's no timeout for write()!
为什么只有前一个为 null 时才获得 ResponseBody OutputStream?
每次都获取 OutputStream 可能会更好,因为您无法确定前一个是否与当前请求的相同。
Why do you get the ResponseBody OutputStream only if the previous was null?
Might be better to get the OutputStream each time as you cannot be sure if the previous one is identical to the one of current Request.
尝试调用
t.close()
,服务器会回复try to call
t.close()
, the server will reply客户端发送保持活动标头。如果这样做,HttpServer 不会关闭连接。您不必对此做任何事情。
It's the client that sends the keep-alive headers. If it does that, HttpServer doesn't close the connection. You don't have to do anything about it.