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

.Net Core 3.1 gRPC客户端使用未加密HTTP2连接报PROTOCOL_ERROR错误

问题根源

AspNet Core 3.1在未加密的HTTP端口上同时启用HTTP/1.1和HTTP/2协议时,默认依赖Upgrade请求头完成协议协商,而gRPC客户端默认会直接发送HTTP/2连接前导码(preface),不会主动发起协议升级请求,导致服务端无法识别请求格式,返回PROTOCOL_ERROR错误。

解决方案

方案1:调整服务端端口配置(推荐)

将REST API和gRPC API拆分到不同的明文端口监听,避免协议协商冲突:

  • 给REST API分配单独端口,配置为仅支持Http1协议
  • 给gRPC API分配单独端口,配置为仅支持Http2协议

服务端Kestrel配置示例:

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.ConfigureKestrel(options =>
            {
                // REST API端口,仅支持HTTP1
                options.ListenAnyIP(16040, o => o.Protocols = HttpProtocols.Http1);
                // gRPC API端口,仅支持HTTP2
                options.ListenAnyIP(16050, o => o.Protocols = HttpProtocols.Http2);
                // 原有HTTPS端口保留
                options.ListenAnyIP(16150, o => o.UseHttps());
            });
            webBuilder.UseStartup<Startup>();
        });

调整后客户端原有代码无需修改,直接访问16050端口即可正常调用gRPC服务。

方案2:保留单端口混布,调整客户端配置

如果必须在同一个明文端口同时提供HTTP1的REST服务和HTTP2的gRPC服务,可显式配置客户端发起协议升级请求:

static async Task Main(string[] args)
{
    AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);

    var channel = GrpcChannel.ForAddress("http://localhost:16050", new GrpcChannelOptions
    {
        HttpVersion = new Version(2, 0),
        // 允许客户端通过Upgrade机制协商升级到HTTP/2
        HttpVersionPolicy = HttpVersionPolicy.RequestVersionOrLower
    });
    var client = new EventService.EventServiceClient(channel);
    var reply1 = await client.GetTrackEventAsync(new GetTrackEventRequest { Name = "01Austra14" });
}

该方案会产生额外的协议协商网络开销,仅适合开发测试场景使用,不推荐生产环境部署。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.21 20:36:03