如何获取Java Socket中可读取的字节数?HTTP请求读取咨询
解决HTTP/1.1请求分块读取的方案
在Java中实现遵循RFC2616的HTTP/1.1服务器时,不要依赖Socket的available()方法——这个方法仅返回当前输入缓冲区中可立即读取的字节数,无法准确获取整个请求的总长度,完全不符合HTTP协议规范要求。正确的做法是严格按照HTTP协议的结构逐步解析请求:
核心思路:按HTTP协议规则分段读取
HTTP/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中包含完整的请求行和头部
- 处理请求体
根据头部字段判断请求体的读取规则:
- 存在
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
相关产品推荐
相关产品推荐

