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

C#/.NET5 单HTTP请求读取多个HTTP响应的实现方法

问题描述

我正在对接一个监听POST请求并返回数据的现有服务,该服务有多个已部署在生产现场的版本,不支持对服务端进行任何修改。
通过HTTP/1.1协议发送<Get_Data>类型的POST请求时,服务存在两类响应逻辑:

  • 场景1:请求数据量较小时,单次请求/接收周期即可完成处理,可直接从响应流获取预期返回值
  • 场景2:请求数据量过大无法即时返回时,服务会先以约4秒为间隔返回N个Progress_Info进度数据包,所有进度包发送完成后才会返回真实有效载荷
    通过WireShark抓包分析待复刻的现有C++程序通信流程,观测到中间进度数据包具备以下特征:
  • 响应头标记Content-Length: 102
  • 属于分片HTTP响应的中间分片(抓包标记如HTTP response 3/4)
  • 存在前序、后续响应帧的关联标记
    核心诉求:保持响应流处于打开状态,接收并丢弃中间的Progress_Info消息,直到获取并保留包含目标数据的最终响应。
    当前已实现的发送/接收代码如下:
public string Send_SOAP_Request_And_Receive_Response( string iInterface, string iRequestMessage )
{
   string oResponseText = string.Empty;
   try
   {
      HttpWebRequest request = Create_Message( MESSAGE_TYPE_POST, iInterface );
      byte[] bytes;
      bytes = System.Text.Encoding.ASCII.GetBytes( iRequestMessage );
      request.ContentLength = bytes.Length;
      Stream requestStream = request.GetRequestStream();
      requestStream.Write( bytes, 0, bytes.Length );
      HttpWebResponse response = ( HttpWebResponse )request.GetResponse();
      Stream responseStream = response.GetResponseStream();
      StreamReader reader = new StreamReader( responseStream );  
      if ( response.StatusCode == HttpStatusCode.OK )
      {
          oResponseText = reader.ReadToEnd();
          // MessageBox.Show( oResponseText );
      }
      // *NEED CODE TO CONTINUE READING THE STREAM UNTIL REAL MESSAGE IS SENT* //
      requestStream.Close();
   }
   catch (Exception ex)
   {
      Global.Display_Message( ex.Message );
   }
   return oResponseText;
}

解决方案

你现有代码无法读取后续响应的核心原因是:HttpWebRequest默认在读取完当前Content-Length标记的完整响应段后,会认为本次响应结束,不会主动等待同TCP连接上后续推送的响应段。按以下步骤修改即可,不需要调整服务端逻辑:

  1. 先调整HttpWebRequest的基础配置,关闭默认的自动连接释放、100-continue等可能导致连接提前断开的行为:
    // 在创建request对象后新增以下配置
    request.KeepAlive = true;
    request.ProtocolVersion = HttpVersion.Version11;
    request.ServicePoint.Expect100Continue = false;
    request.AllowReadStreamBuffering = false;
    request.ServicePoint.SetTcpKeepAlive(true, 1000, 1000);
    
  2. 替换原有单次ReadToEnd()的逻辑,改为循环逐段读取响应:每读完一段就判断是否为Progress_Info进度包,是则丢弃继续读取,否则作为最终结果返回。同时修正资源释放逻辑,避免连接泄漏。
    修正后的完整实现代码:
    public string Send_SOAP_Request_And_Receive_Response( string iInterface, string iRequestMessage )
    {
        string oResponseText = string.Empty;
        HttpWebRequest request = null;
        HttpWebResponse response = null;
        Stream requestStream = null;
        Stream responseStream = null;
        StreamReader reader = null;
        try
        {
            request = Create_Message(MESSAGE_TYPE_POST, iInterface);
            // 基础连接配置
            request.KeepAlive = true;
            request.ProtocolVersion = HttpVersion.Version11;
            request.ServicePoint.Expect100Continue = false;
            request.AllowReadStreamBuffering = false;
    
            byte[] bytes = Encoding.ASCII.GetBytes(iRequestMessage);
            request.ContentLength = bytes.Length;
            // 写入请求数据,写完立即关闭请求流
            requestStream = request.GetRequestStream();
            requestStream.Write(bytes, 0, bytes.Length);
            requestStream.Close();
            
            // 循环读取连续响应段
            while (true)
            {
                response = (HttpWebResponse)request.GetResponse();
                if (response.StatusCode != HttpStatusCode.OK)
                {
                    throw new Exception($"请求返回异常状态码:{response.StatusCode}");
                }
                responseStream = response.GetResponseStream();
                reader = new StreamReader(responseStream, Encoding.ASCII);
                string currentSegment = reader.ReadToEnd();
                
                // 释放当前段的读取资源,准备读取下一段
                reader.Close();
                responseStream.Close();
                response.Close();
    
                // 按抓包特征判断是否为进度包:固定长度102+包含Progress_Info标记
                if (currentSegment.Trim().Length == 102 && currentSegment.Contains("Progress_Info"))
                {
                    Thread.Sleep(200);
                    continue;
                }
                // 命中最终有效载荷,退出循环
                oResponseText = currentSegment;
                break;
            }
        }
        catch (Exception ex)
        {
            Global.Display_Message(ex.Message);
        }
        finally
        {
            // 统一释放所有资源
            reader?.Dispose();
            responseStream?.Dispose();
            response?.Dispose();
            requestStream?.Dispose();
            request?.ServicePoint?.CloseConnectionGroup(request.ConnectionGroupName);
        }
        return oResponseText;
    }
    
  3. 进度包判断条件可以根据你实际抓包的内容调整,比如进度包有固定的XML/JSON结构,就把判断条件写得更严格,避免误判包含Progress_Info字符串的有效载荷。

该实现可以同时兼容两种响应场景:小数据量请求时第一次读取就会拿到有效载荷,不会进入跳过逻辑;大数据量请求时会自动丢弃所有进度包,直到拿到最终数据。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 05:03:19