You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

java.net.http.HttpClient POST请求服务端读取永久阻塞问题

问题根因

代码存在两处核心错误,最终导致客户端与服务端形成死锁:

  • 客户端错误:手动设置Transfer-Encoding: chunked破坏了HttpClient的默认行为
    JDK 11+内置的java.net.http.HttpClient会自动管理传输层相关的协议头,你使用的是固定长度的字节数组作为请求体,客户端默认会自动添加Content-Length头使用定长传输。你手动强制指定chunked编码后,客户端不会自动生成chunked协议要求的0长度终止块,服务端会认为还有后续请求数据没发完。
  • 服务端错误:读取逻辑完全不符合HTTP协议规范
    你直接调用InputStream.readAllBytes()读取Socket流,这个方法的逻辑是一直读到输入流结束(也就是TCP连接被对端关闭)才会返回,根本不会按HTTP协议规则判断请求体是否读取完成:不管是定长传输读到Content-Length声明的字节数就停止,还是chunked传输读到终止块就停止,当前逻辑都会无限制等待连接关闭。而HTTP/1.1默认启用长连接,客户端发完请求后不会断开连接,会一直等待服务端返回响应,最终形成死锁:客户端等服务端回响应,服务端等客户端关连接,两边永久阻塞。
修复方案

客户端修改

直接删除手动设置Transfer-Encoding头的代码即可,所有传输编码相关的头交给HttpClient自动处理,不需要手动干预。修正后的请求构造代码如下:

final Builder builder = HttpRequest.newBuilder();
builder.uri(uri);
builder.version(Version.HTTP_1_1);
// 删除 builder.setHeader("Transfer-Encoding", "chunked"); 这行
builder.setHeader("Content-Type", "application/ipp");
builder.setHeader("Accept-Encoding", "gzip,deflate");
builder.POST(publisher);

如果确实需要使用chunked分块传输,不要手动设置该头,使用流式的BodyPublisher实现,客户端会自动启用chunked编码并生成符合规范的分块结构。

服务端修改

不要直接对原始Socket流调用readAllBytes(),需要严格按照HTTP/1.1协议规范解析请求:

  1. 先读取并解析请求行、请求头,拿到Content-Length或Transfer-Encoding字段值
  2. 如果是定长传输,精确读取Content-Length指定长度的字节作为请求体
  3. 如果是分块传输,按chunked协议规则逐块读取,直到碰到0长度的终止块
  4. 读完请求后立刻按照HTTP协议格式返回响应,再根据Connection头决定是关闭连接还是保持连接等待下一个请求

如果只是临时测试想快速验证逻辑,可以给客户端请求加上Connection: close头,服务端读完请求、返回响应后主动关闭连接,但这只是权宜之计,正式场景必须按协议规范解析请求。

内容的提问来源于stack exchange,提问作者Dave The Dane

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.03 03:24:26