.NET如何建立与指定HTTP服务的持久长连接?
实现.NET长连接读取流式HTTP响应的正确方式
这个场景是典型的HTTP流式响应+持久连接:Chrome能正常处理是因为它在收到响应头后就会持续监听响应流,不会等待整个响应完成。在.NET里要复刻这个行为,核心就是要避开那些会等待响应完全结束的API,直接操作响应流。下面给你两种主流实现方案:
现代.NET(Core/.NET 5+)推荐方案:HttpClient流式读取
这是当前.NET官方推荐的方式,代码简洁且性能更优:
using System; using System.Net.Http; using System.Threading; using System.Threading.Tasks; using System.IO; class Program { static async Task Main(string[] args) { var targetUrl = "https://srs-ds.i.daimler.com/STARCDS/services/Server?action=connect&id=GS0034837&version=1.4"; // 创建取消令牌,支持手动终止连接(比如Ctrl+C) using var cts = new CancellationTokenSource(); Console.CancelKeyPress += (sender, e) => { e.Cancel = true; cts.Cancel(); Console.WriteLine("\n正在终止连接..."); }; // 建议全局单例复用HttpClient,避免Socket资源耗尽 using var httpClient = new HttpClient { // 设置无限超时,匹配服务5分钟保活的间隔 Timeout = Timeout.InfiniteTimeSpan }; try { // 关键:使用ResponseHeadersRead选项,收到响应头就返回,不等待全量内容 using var response = await httpClient.GetAsync( targetUrl, HttpCompletionOption.ResponseHeadersRead, cts.Token ); response.EnsureSuccessStatusCode(); // 验证请求成功 // 获取响应流并持续读取 using var stream = await response.Content.ReadAsStreamAsync(cts.Token); using var reader = new StreamReader(stream); Console.WriteLine("已建立连接,开始监听响应...(按Ctrl+C终止)"); // 循环读取直到连接关闭或手动取消 while (!reader.EndOfStream && !cts.Token.IsCancellationRequested) { var message = await reader.ReadLineAsync(cts.Token); if (!string.IsNullOrEmpty(message)) { Console.WriteLine($"收到消息: {message}"); } } } catch (OperationCanceledException) { Console.WriteLine("连接已被手动终止"); } catch (Exception ex) { Console.WriteLine($"发生错误: {ex.Message}"); } } }
核心注意点:
HttpCompletionOption.ResponseHeadersRead:这是实现流式读取的关键,它告诉HttpClient不要等待整个响应下载完成,拿到响应头就返回控制权。- 超时设置:默认HttpClient超时是100秒,远短于服务5分钟的保活间隔,必须设置为
Timeout.InfiniteTimeSpan或者足够长的时间。 - HttpClient复用:不要每次请求都新建HttpClient,建议全局单例使用,避免频繁创建导致的Socket资源耗尽问题。
.NET Framework 兼容方案:HttpWebRequest实现
如果是老版本.NET Framework项目,可以用HttpWebRequest来实现:
using System; using System.IO; using System.Net; using System.Threading; using System.Threading.Tasks; class Program { static async Task Main(string[] args) { var targetUrl = "https://srs-ds.i.daimler.com/STARCDS/services/Server?action=connect&id=GS0034837&version=1.4"; using var cts = new CancellationTokenSource(); Console.CancelKeyPress += (sender, e) => { e.Cancel = true; cts.Cancel(); Console.WriteLine("\n正在终止连接..."); }; try { var request = (HttpWebRequest)WebRequest.Create(targetUrl); request.KeepAlive = true; // 启用持久连接(默认已开启) request.Timeout = Timeout.Infinite; request.ReadWriteTimeout = Timeout.Infinite; using var response = (HttpWebResponse)await request.GetResponseAsync(); using var stream = response.GetResponseStream(); using var reader = new StreamReader(stream); Console.WriteLine("已建立连接,开始监听响应...(按Ctrl+C终止)"); while (!reader.EndOfStream && !cts.Token.IsCancellationRequested) { var message = await reader.ReadLineAsync(cts.Token); if (!string.IsNullOrEmpty(message)) { Console.WriteLine($"收到消息: {message}"); } } } catch (OperationCanceledException) { Console.WriteLine("连接已被手动终止"); } catch (Exception ex) { Console.WriteLine($"发生错误: {ex.Message}"); } } }
为什么你之前的尝试可能失败?
大概率是用了GetStringAsync、ReadAsStringAsync这类API——它们会强制等待整个响应完成才返回结果,但你的服务连接不会主动终止,所以会一直卡住。只要切换到直接读取响应流的方式,就能和Chrome保持一样的行为。
内容的提问来源于stack exchange,提问作者Leonardo Perez
相关产品推荐
相关产品推荐

