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

如何获取Java Socket中可读取的字节数?HTTP请求读取咨询

解决HTTP/1.1请求分块读取的方案

在Java中实现遵循RFC2616的HTTP/1.1服务器时,不要依赖Socket的available()方法——这个方法仅返回当前输入缓冲区中可立即读取的字节数,无法准确获取整个请求的总长度,完全不符合HTTP协议规范要求。正确的做法是严格按照HTTP协议的结构逐步解析请求:

核心思路:按HTTP协议规则分段读取

HTTP/1.1请求由请求行、请求头部、请求体(可选)三部分组成,各部分通过特定规则界定边界,我们可以据此分段读取:

  1. 读取请求行与头部
    请求行和每个头部字段都以\r\n结尾,头部与请求体之间以**空行(\r\n\r\n)**分隔。可以用BufferedReader逐行读取,直到遇到空行为止。注意:若头部字段值过长被折行,需按照RFC2616规则拼接(折行以空格或制表符开头)。

示例代码片段:

BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
StringBuilder headersBuilder = new StringBuilder();
String line;

// 读取请求行
line = reader.readLine();
headersBuilder.append(line).append("\r\n");

// 读取头部字段,直到空行
while ((line = reader.readLine()) != null && !line.isEmpty()) {
    headersBuilder.append(line).append("\r\n");
}
// 此时headersBuilder中包含完整的请求行和头部
  1. 处理请求体
    根据头部字段判断请求体的读取规则:
  • 存在Content-Length字段:直接读取指定长度的字节
    String contentLengthStr = extractHeader(headersBuilder.toString(), "Content-Length");
    if (contentLengthStr != null) {
        int contentLength = Integer.parseInt(contentLengthStr);
        byte[] bodyBytes = new byte[contentLength];
        InputStream inputStream = socket.getInputStream();
        int totalRead = 0;
        int read;
    
        // 循环读取直到获取全部指定字节
        while (totalRead < contentLength && (read = inputStream.read(bodyBytes, totalRead, contentLength - totalRead)) != -1) {
            totalRead += read;
        }
        // bodyBytes中即为完整请求体
    }
    
  • Transfer-Encoding: chunked:按分块编码规则读取,每个块以十六进制长度(后跟\r\n)开头,接着是块内容(后跟\r\n),直到遇到长度为0的块
    InputStream inputStream = socket.getInputStream();
    StringBuilder bodyBuilder = new StringBuilder();
    BufferedReader chunkReader = new BufferedReader(new InputStreamReader(inputStream));
    String chunkSizeStr;
    
    while ((chunkSizeStr = chunkReader.readLine()) != null) {
        // 忽略分块中的扩展字段
        int semicolonIndex = chunkSizeStr.indexOf(';');
        if (semicolonIndex != -1) {
            chunkSizeStr = chunkSizeStr.substring(0, semicolonIndex);
        }
        int chunkSize = Integer.parseInt(chunkSizeStr.trim(), 16);
        if (chunkSize == 0) {
            break; // 分块结束
        }
    
        // 读取块内容
        char[] chunkContent = new char[chunkSize];
        chunkReader.read(chunkContent, 0, chunkSize);
        bodyBuilder.append(chunkContent);
        // 跳过块末尾的\r\n
        chunkReader.readLine();
    }
    // bodyBuilder中即为完整请求体
    
  • 无请求体的请求(如GET、HEAD):空行之后请求即结束,无需读取额外内容

关键注意事项

  • 永远不要假设请求会一次性发送完成,必须通过循环读取直到满足协议定义的结束条件
  • InputStream.read()方法可能返回部分字节,必须循环读取直到获取所需的全部数据
  • 处理头部时要注意字段名的大小写不敏感(HTTP头部字段名是大小写无关的)

内容的提问来源于stack exchange,提问作者Tak_fortell

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 23:50:32