我正在编写一个简单的Web服务器,代码片段:
ServerSocket server = new ServerSocket(80);
Socket client=server.accept();
InputStream in=client.getInputStream();
OutputStream out=client.getOutputStream();
int val = -1;
while ((val = in.read()) != -1) {
System.out.print((char) val);
}
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter( out));
writer.write("HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=utf-8\r\n\r\nhello world!");
writer.close();
out.close();
in.close();
我在计算机上运行它,然后在Firefox中访问http://127.0.0.1。页面挂起,无法显示“hello world”。我认为问题发生在while ((val = in.read()) != -1)
,如何解决?
答案 0 :(得分:4)
HTTP(至少1.1版本)允许打开连接。然后请求以空行(即"\r\n\r\n"
)结束,如果它不是带有内容的POST或PUT请求。在此之后,客户端可以在同一连接上发送下一个请求。
所以你必须阅读输入,至少扫描你的空行。
编辑:为了澄清这一点,来自RFC 2616(定义HTTP 1.1)的一些引用。
部分 4.1,消息类型:
请求(第5节)和响应(第6节)消息使用通用 用于传输实体的RFC 822 [9]的消息格式(有效载荷 的消息)。两种类型的消息都包含一个起始行,即零 或更多标题字段(也称为“标题”),空行(即, CRLF之前没有任何内容的行)表示结束 标题字段,可能还有消息正文。
generic-message = start-line *(message-header CRLF) CRLF [ message-body ] start-line = Request-Line | Status-Line
因此,消息头和消息体由空行(起始行后面的第一行)分隔。
部分 4.3消息正文:
HTTP消息的消息体(如果有的话)用于携带 与请求或响应关联的实体主体。 [...]
邮件中允许邮件正文的规则有所不同 请求和回复。
请求中存在消息正文 包含Content-Length或Transfer-Encoding标头字段 请求的消息头。邮件正文不得包含在内 如果请求方法的规范请求(第5.1.1节) 不允许在请求中发送实体主体。服务器应该 在任何请求上读取和转发消息正文;如果是请求方法 不包含实体主体的定义语义,然后是 处理请求时,应该忽略message-body。
因此原则上,客户端必须只在方法允许时发送一个正文,但是服务器应该忽略多余的消息体,如果它们是在不支持它的方法上发送的。 Content-Length
或Transfer-Encoding
标题字段表示正文的存在。
第9节的小节定义了各个方法。
无论如何,无论客户端是否发送正文,以及它是否重新使用连接以进行下一个请求,通常都不会在读取响应之前关闭连接,否则您的服务器无法重新发送总的来说。因此,您在阅读请求时无法等待结束,但必须知道何时结束发送您的回复。
对于只处理获取请求的简单hello world服务器,您可以简单地说“直到第一个空行”。
对于真实服务器(即外部世界可见的服务器),您至少应该解析请求,忽略任何正文,并以不同于GET的方式处理HEAD(即不发送任何正文),并发送不支持的方法的错误响应。