.NET Framework 4.7.2的Grpc.Net.Client连接.NET6 gRPC服务失败
.NET Framework 4.7.2 gRPC客户端连接.NET6 HTTP/2服务端失败解决方案
问题复现
基于.NET Framework 4.7.2的Grpc.Net.Client客户端连接.NET6 gRPC服务端(HTTP/2-only)时失败:
- 服务端报错:
HTTP/2 over TLS was not negotiated on an HTTP/2-only endpoint. - 客户端报错:
WinHttpException: Error 12030 calling WinHttpWriteData, 'The connection with the server was terminated abnormally'
核心原因
- HTTP/2协商不完整:.NET Framework的
WinHttpHandler仅设置request.Version = 2.0不足以触发HTTP/2 ALPN协商,且缺少gRPC必需的TE: trailers请求头。 - 证书信任问题:若服务端使用自签名HTTPS证书,.NET Framework默认不信任该证书,会导致SSL握手失败,直接中断连接。
解决方案
1. 修复自定义HttpHandler,强制HTTP/2协商
修改Http2CustomHandler,补充HTTP/2配置和gRPC必需头:
public class Http2CustomHandler : WinHttpHandler { protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { request.Version = new Version("2.0"); // 添加gRPC要求的TE头,用于接收trailers request.Headers.Add("TE", "trailers"); return base.SendAsync(request, cancellationToken); } protected override void InitializeHandle(SafeWinHttpHandle handle) { base.InitializeHandle(handle); // 通过WinHttp原生选项强制启用HTTP/2(WINHTTP_OPTION_ENABLE_HTTP2 = 12) int enableHttp2 = 1; WinHttpSetOption(handle, 12, ref enableHttp2, sizeof(int)); } [DllImport("winhttp.dll", CharSet = CharSet.Unicode, SetLastError = true)] private static extern bool WinHttpSetOption(SafeWinHttpHandle hInternet, int dwOption, ref int lpBuffer, int dwBufferLength); }
2. 处理自签名证书信任(开发环境)
如果服务端用的是自签名证书,需要让客户端信任它:
- 方法一(推荐):将服务端证书导出,安装到客户端机器的
受信任的根证书颁发机构存储区。 - 方法二(仅开发用):代码中跳过证书验证(生产环境绝对禁止):
// 替换原Credentials配置 Credentials = new Grpc.Core.SslCredentials(new Grpc.Core.SslCertificateCredentials( (chain, errors) => true // 强制信任所有证书,仅限开发调试 ))
3. 环境检查
确保客户端运行在**Windows 10 1607+或Windows Server 2016+**系统上,这些版本原生支持HTTP/2的ALPN协商,是.NET Framework使用HTTP/2的前提。
最终客户端代码
static void Main(string[] args) { using (var channel = GrpcChannel.ForAddress("https://localhost:7193", new GrpcChannelOptions { HttpHandler = new Http2CustomHandler(), // 开发环境临时证书信任配置,生产环境替换为合法证书逻辑 Credentials = new Grpc.Core.SslCredentials(new Grpc.Core.SslCertificateCredentials((chain, errors) => true)) })) { var client = new Greeter.GreeterClient(channel); var reply = client.SayHelloAsync(new HelloRequest { Name = "GreeterClient" }).GetAwaiter().GetResult(); Console.WriteLine("Greeting: " + reply.Message); Console.WriteLine("Press any key to exit..."); Console.ReadKey(); } }
内容的提问来源于stack exchange,提问作者user3121159
相关产品推荐
相关产品推荐

