SignalR客户端升级后Windows Server 2012 R2上HTTP/2握手失败问题排查
我有基于.NET 6的SignalR服务器(Hub)和C#客户端,服务器使用Let's Encrypt证书。将SignalR客户端从6.0.10升级至7.0.12后,一台Windows Server 2012 R2客户端电脑在调用StartAsync方法时抛出异常:
An HTTP/2 connection could not be established because the server did not complete the HTTP/2 handshake
客户端代码如下:
_hubConnection = new HubConnectionBuilder().WithUrl($"{_setting.HubUrl}/hubs/tariff") .AddJsonProtocol(options => options.PayloadSerializerOptions.PropertyNamingPolicy = null).Build(); await _hubConnection.StartAsync(); // <-- 抛出HttpRequestException异常
本地客户端测试无异常,需解决该问题,同时询问是否有强制禁用HTTP/2的方案。
堆栈跟踪关键信息:
System.Net.Http.HttpRequestException: An HTTP/2 connection could not be established because the server did not complete the HTTP/2 handshake. at System.Net.Http.HttpConnectionPool.ReturnHttp2Connection(Http2Connection connection, Boolean isNewConnection) ... at Microsoft.AspNetCore.SignalR.Client.HubConnection.StartAsync(CancellationToken cancellationToken)
1. 强制禁用HTTP/2(针对Windows Server 2012 R2的核心解决方法)
Windows Server 2012 R2原生对HTTP/2支持有限,升级后的SignalR 7.x客户端默认优先尝试HTTP/2连接,导致握手失败。可通过配置HttpClient强制使用HTTP/1.1:
修改客户端代码如下:
_hubConnection = new HubConnectionBuilder() .WithUrl($"{_setting.HubUrl}/hubs/tariff", options => { options.HttpMessageHandlerFactory = _ => new HttpClientHandler { // 强制使用HTTP/1.1 DefaultRequestVersion = HttpVersion.Version11, MaxResponseContentBufferSize = 1024 * 1024 * 10, // 可根据需求调整缓存大小 AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate }; }) .AddJsonProtocol(options => options.PayloadSerializerOptions.PropertyNamingPolicy = null) .Build(); await _hubConnection.StartAsync();
注:如果不需要跳过证书验证,不要添加
ServerCertificateCustomValidationCallback相关代码,需确保Windows Server 2012 R2信任Let's Encrypt的根证书。
2. 更新Windows Server 2012 R2的根证书
Let's Encrypt的ISRG Root X1根证书在Windows Server 2012 R2中默认可能未更新,会导致SSL握手异常间接引发HTTP/2握手失败。手动更新步骤:
- 获取Let's Encrypt官方最新的
ISRG Root X1根证书 - 运行
certmgr.msc,将证书导入至「受信任的根证书颁发机构」
3. 服务器端可选配置(限制HTTP版本)
若希望服务器端统一限制HTTP版本,可在SignalR服务器的Program.cs中配置Kestrel强制使用HTTP/1.1(会影响所有客户端):
builder.WebHost.ConfigureKestrel(options => { options.ListenAnyIP(5001, listenOptions => { listenOptions.UseHttps("your-certificate.pfx", "cert-password"); listenOptions.Protocols = HttpProtocols.Http1; }); });
4. 其他排查点
- 确认Windows Server 2012 R2已安装.NET 6或更高版本的运行时(SignalR 7.x客户端依赖该环境)
- 检查服务器端SignalR Hub的跨域、路由配置是否有变更
- 使用命令行工具测试服务器的HTTP/2支持:
curl -v --http2 https://your-server-url/hubs/tariff/negotiate
内容的提问来源于stack exchange,提问作者Aries

