我正在尝试用Java实现一个简单的http服务器,以了解http服务器如何工作。现在,我可以从任何浏览器发送请求并接收正确的响应,但是,当我尝试模拟来自邮递员的请求时,它总是会引发java.net.SocketException: Broken pipe (Write failed)
异常。
我的http服务器非常简单:收到请求后,将消息回显给发件人。
实现如下,Source Code
package com.ont.http;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class SingleThreadHttpServer implements HttpServer {
public void run(int port) throws IOException {
ServerSocket serverSocket = new ServerSocket(port);
try {
while (true) {
Socket socket = serverSocket.accept();
BufferedReader inBufferReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
StringBuilder stringBuffer = new StringBuilder();
String inputLine;
while ((inputLine = inBufferReader.readLine()) != null && !inputLine.equals("")) {
stringBuffer.append(inputLine);
stringBuffer.append("\r\n");
}
System.out.println(stringBuffer.toString());
OutputStream outStream = socket.getOutputStream();
BufferedReader bufferedReader = new BufferedReader(new StringReader("A Message from server."));
// Header should be ended with '\r\n' at each line.
outStream.write("HTTP/1.1 200 OK\r\n".getBytes());
outStream.write("Main: OneServer 0.1\r\n".getBytes());
outStream.write("Content-length: 22\r\n".getBytes()); // if text/plain the length is required
outStream.write("Content-Type: text/plain\r\n".getBytes());
// An empty line is required after the header
outStream.write("\r\n".getBytes());
String line;
while ((line = bufferedReader.readLine()) != null) {
outStream.write(line.getBytes());
}
inBufferReader.close();
bufferedReader.close();
outStream.flush();
outStream.close(); // Socket will close automatically once output stream is closed.
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
从Postman发送的任何类型的请求都将立即触发该异常,并且debug告知套接字连接在outStream.write(...)
期间丢失。如果我是从浏览器发送的,则永远不会触发该问题。
我不明白的是,不应对Postman进行任何设置,因为当我尝试模拟对tomcat的请求时,它会像处理魅力一样处理一切,我的东西一定出了问题代码,但我不知道在哪里以及为什么。
任何帮助或提示都将不胜感激,谢谢。
-----更新----
从Postman控制台中,我可以看到此异常,
Error: read ECONNRESET
Request Headers:
accept:"text/plain"
cache-control:"no-cache"
如果我只剩下outStream.write("HTTP/1.1 200 OK\r\n".getBytes())
这一行,并删除所有其他outStream.write...
,则浏览器和邮递员的工作就像一个咒语。这真的拉着我的头发,但是为什么呢?
答案 0 :(得分:1)
我能够重现该问题并确定其根本原因。对于所有localhost
URL,邮递员两次连接到服务器。第一个连接只是为了检查服务器的可达性,它会立即关闭,而无需等待服务器的响应。如果服务器可达,它将再次连接并发送第二个请求并显示第二个请求的响应。
如果您已发布服务器,则在关闭来自Postman的第一个连接时会崩溃。由于Postman关闭连接时没有等待第一个连接的完整响应,因此服务器中的多个outStream.write()
调用将引发异常(java.net.SocketException: Broken pipe (Write failed)
),从而导致服务器崩溃。因此,邮递员发送的第二个请求将不会得到任何响应。
我认为这是Postman App的问题,应解决。但是,另一方面,也应在服务器端正确处理此类错误,以防止崩溃。如果客户端破坏了发送响应的服务器中间的连接(即使用任何浏览器发出请求,发送非常大的响应或在后续两次发送之间保持延迟),则不仅Postman应用程序而且使用其他工具发送的请求也会使服务器崩溃。 outStream.write()
调用,在收到完整响应之前关闭浏览器选项卡,这将使您的服务器崩溃。
请参阅下面发布的修改后的服务器代码,该代码在这种情况下将很好地工作:
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class SingleThreadHttpServer implements HttpServer {
public void run(int port) throws IOException {
ServerSocket serverSocket = new ServerSocket(port);
try {
while (true) {
Socket socket = serverSocket.accept();
BufferedReader inBufferReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
StringBuilder stringBuffer = new StringBuilder();
String inputLine;
while ((inputLine = inBufferReader.readLine()) != null && !inputLine.equals("")) {
stringBuffer.append(inputLine);
stringBuffer.append("\r\n");
}
System.out.println(stringBuffer.toString());
OutputStream outStream = socket.getOutputStream();
BufferedReader bufferedReader = new BufferedReader(new StringReader("A Message from server."));
try {
// Header should be ended with '\r\n' at each line
outStream.write("HTTP/1.1 200 OK\r\n".getBytes());
//outStream.write("Main: OneServer 0.1\r\n".getBytes());
outStream.write("Content-Length: 22\r\n".getBytes()); // if text/plain the length is required
outStream.write("Content-Type: text/plain\r\n".getBytes());
//outStream.write("Connection: close\r\n".getBytes());
// An empty line is required after the header
outStream.write("\r\n".getBytes());
String line;
while ((line = bufferedReader.readLine()) != null) {
outStream.write(line.getBytes());
}
outStream.flush();
outStream.close(); // Socket will close automatically once output stream is closed.
} catch (SocketException e) {
// Handle the case where client closed the connection while server was writing to it
socket.close();
}
bufferedReader.close();
inBufferReader.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
答案 1 :(得分:0)
该异常表示您在尝试写入连接时,另一端已经关闭了该连接。
这只是猜测,但Postman可能不喜欢收到的答复,只是放弃了。浏览器可能仍会尽力而为地接受您的操作,但这并不一定意味着响应正确。我不是一个人了解整个http规范,所以我无法立即告诉您是否是这种情况。要继续,也许您可以看一下Fiddler之类的工具,查看基本请求后通常从http服务器返回的内容,然后查看是否缺少某些内容。
我可以看到,内容长度是硬编码的,尽管我不希望这样会给您带来异常。