为什么我的服务器套接字在读取http post请求的主体时挂起?

时间:2015-11-28 14:14:53

标签: java sockets http post inputstream

警告:这是一个自我回答的问题。我最近遇到了这个问题,发现它很棘手,虽然听起来很简单。我决定发布一个完整的问题和答案,为未来的访问者提供代码示例。

<小时/> 我有一个ServerSocket,它侦听端口12005上的连接。它以最简单的方式实现:

ServerSocket ss = new ServerSocket(12005);
while(true) {
    executorService.submit(new SocketProcessor(ss.accept());
}

此处SocketProcessor只是一个Runnable,用于处理传入的连接:

@Override
public void run() {
    try {
       String msg = IOUtils.toString(s.getInputStream());
       // process msg here
    } finally {
        s.close();
    }
}     

问题是执行在IOUtils.toString(InputStream is)方法上挂起。我认为标准解决方案会更好,所以我用

替换它
BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream()));
String line;
while ((line = in.readLine()) != null) {
    System.out.println(line);
}

输出类似于:

POST / HTTP/1.1
Host: http://localhost:12005
Content-Type: application/json
//other headers
                  <--- empty line
                  <--- here it hangs

如果我在调用者端中止连接,我可以在输出中看到一个新行 - 实际上它是http正文:

{"name":"alex", "hobby":"football"}  

我尝试了两种不同的请求发送方实现,当我尝试读取http主体时它们都挂起了。这意味着问题正好在我的服务器套接字中。
这个实现从教程中出了什么问题?

1 个答案:

答案 0 :(得分:0)

我很幸运能在reddit论坛找到解释。

简而言之,http post消息的结构如下

start-line CRLF 
header-field-1 CRLF
header-field-2 CRLF
header-field-N CRLF
CRLF  
message-body

正如您所看到的,邮件正文后面没有换行符号。因为这个readLine()方法不能像我们预期的那样工作。我猜Apache IOUtils.toString()方法面临同样的问题。要解决此问题,建议使用以下解决方案:

  1. 找到Content-Length标头并读取其值N
  2. 在标题部分结束后读取确切的N个字节
  3. 以下是示例代码:

    BufferedReader br = new BufferedReader(new InputStreamReader(is));
    boolean headersFinished = false;
    int contentLength = -1;
    
    while (!headersFinished) {
         String line = br.readLine();
         headersFinished = line.isEmpty();
    
         if (line.startsWith("Content-Length:")) {
             String cl = line.substring("Content-Length:".length()).trim();
             contentLength = Integer.parseInt(cl);
         }
    }
    
    // validate contentLength value
    char[] buf = new char[contentLength];  //<-- http body is here
    br.read(buf);