如何在C#中通过Socket完整读取HTTP网络请求?
问题分析与解决
你的代码逻辑是持续调用NetworkStream.Read()直到返回0,但这在HTTP请求场景下行不通——HTTP协议中,客户端发送完请求后通常不会立即关闭TCP连接(尤其是启用Keep-Alive时),所以Read()会一直阻塞,永远等不到返回0的信号,自然无法判断请求结束。
要正确读取HTTP请求,必须遵循HTTP协议规则来判断请求边界,核心思路是:
- 先读取请求头,直到遇到空行(
\r\n\r\n),这是请求头结束的标志 - 解析请求头中的
Content-Length字段,确定请求体的长度(如果有) - 根据
Content-Length读取对应长度的请求体内容
修改后的代码示例
IPHostEntry host = Dns.GetHostEntry("localhost"); IPAddress ipAddress = host.AddressList[0]; IPEndPoint localEndPoint = new IPEndPoint(ipAddress, port); Socket listener = new Socket(ipAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp); listener.Bind(localEndPoint); listener.Listen(1); Socket networkRequestHandler = listener.Accept(); NetworkStream myNetworkStream = new NetworkStream(networkRequestHandler); byte[] myReadBuffer = new byte[1024]; StringBuilder headerBuilder = new StringBuilder(); int numberOfBytesRead; // 读取请求头,直到检测到空行 while (true) { numberOfBytesRead = myNetworkStream.Read(myReadBuffer, 0, myReadBuffer.Length); if (numberOfBytesRead == 0) break; // 连接意外中断 headerBuilder.Append(Encoding.ASCII.GetString(myReadBuffer, 0, numberOfBytesRead)); if (headerBuilder.ToString().Contains("\r\n\r\n")) break; } string requestHeader = headerBuilder.ToString(); // 解析Content-Length字段 int contentLength = 0; var contentLengthMatch = System.Text.RegularExpressions.Regex.Match(requestHeader, @"Content-Length:\s*(\d+)"); if (contentLengthMatch.Success) { contentLength = int.Parse(contentLengthMatch.Groups[1].Value); } StringBuilder requestBodyBuilder = new StringBuilder(); // 读取指定长度的请求体 if (contentLength > 0) { int bytesReadTotal = 0; while (bytesReadTotal < contentLength) { int bytesToRead = Math.Min(myReadBuffer.Length, contentLength - bytesReadTotal); numberOfBytesRead = myNetworkStream.Read(myReadBuffer, 0, bytesToRead); if (numberOfBytesRead == 0) break; // 连接意外中断 requestBodyBuilder.Append(Encoding.ASCII.GetString(myReadBuffer, 0, numberOfBytesRead)); bytesReadTotal += numberOfBytesRead; } } // 拼接完整请求内容 string fullRequest = requestHeader + requestBodyBuilder.ToString(); Console.WriteLine(fullRequest);
额外说明
- 分块编码处理:上述代码仅支持带有
Content-Length的请求,如果遇到Transfer-Encoding: chunked的分块请求,需要额外解析分块格式(每个块以十六进制长度+\r\n开头,块内容后接\r\n,最后以0\r\n\r\n结束)。 - 编码问题:示例中用ASCII编码,实际HTTP请求可能使用UTF-8等编码,可根据请求头
Content-Type中的charset字段调整编码方式。 - 生产环境建议:手动基于Socket解析HTTP请求容易遗漏各种边界情况(如压缩、多部分表单、异常请求等),生产环境优先使用成熟的HTTP处理库(如ASP.NET相关组件)。
内容的提问来源于stack exchange,提问作者Mike Croteau
相关产品推荐
相关产品推荐

