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

如何在Kestrel中配置回调检测客户端连接关闭(.NET6 gRPC场景)

Kestrel客户端连接关闭检测实现方案

.NET 6 下可以通过自定义Kestrel连接中间件的方式直接获取TCP连接的生命周期事件,无需依赖Kestrel内部未公开的API,具体实现如下:

步骤1:修改Kestrel配置,注入自定义连接中间件

直接在你现有的ConfigureEndpointDefaults配置块中添加连接中间件逻辑即可:

var hostBuilder = webHostBuilder.ConfigureKestrel(kestrelServerOptions =>
{
    kestrelServerOptions.ConfigureHttpsDefaults(httpsConnectionAdapterOptions => 
        httpsConnectionAdapterOptions.SslProtocols = SslProtocols.Tls12);

    var certPath = Path.Combine(builder.Environment.ContentRootPath, "Certs", $"{environment}.pfx");

    kestrelServerOptions.ConfigureEndpointDefaults(listenOptions =>
    {
        listenOptions.UseHttps(certPath, password);
        logger.Debug($"Using {certPath} as the cert file.");
        logger.Debug("Configuring host to use HTTP/2 protocol.");
        listenOptions.Protocols = HttpProtocols.Http2;

        // 新增:注入自定义连接中间件,监听连接全生命周期
        listenOptions.Use(async (connectionContext, next) =>
        {
            // 连接建立时触发
            logger.Debug($"客户端连接已建立,连接ID:{connectionContext.ConnectionId},远程地址:{connectionContext.RemoteEndPoint}");

            try
            {
                // 执行后续中间件逻辑,等待连接处理完成
                await next();
                // 连接正常关闭时触发
                logger.Debug($"客户端连接已正常关闭,连接ID:{connectionContext.ConnectionId}");
            }
            catch (IOException ex) when (ex.InnerException is SocketException se 
                && se.SocketErrorCode is SocketErrorCode.ConnectionReset or SocketErrorCode.ConnectionAborted)
            {
                // 客户端强制断开连接时触发
                logger.Debug($"客户端连接已重置,连接ID:{connectionContext.ConnectionId},错误码:{se.SocketErrorCode}");
            }
            catch (Exception ex)
            {
                logger.Error(ex, $"客户端连接处理异常,连接ID:{connectionContext.ConnectionId}");
                throw;
            }
            finally
            {
                // 无论连接正常/异常关闭,最终都会触发此处逻辑
                logger.Debug($"客户端连接资源已释放,连接ID:{connectionContext.ConnectionId}");
            }
        });
    });

    logger.Debug("Reading config values for the server name and port.");
    var port = builder.Configuration.GetValue<int>("AppSettings:OperationsServerPort");
    var address = IPAddress.Parse("0.0.0.0");

    if (address != null)
    {
        logger.Debug($"Host will listen at https://{address}:{port}");
        kestrelServerOptions.Listen(address, port);
    }
    else
    {
        logger.Error("DNS address for service host cannot be determined!  Exiting...");
        Environment.Exit(-1);
    }
});

步骤2:gRPC服务请求级别的断开检测(可选,针对单个调用场景)

如果只需要在具体的gRPC服务方法中检测当前调用对应的客户端连接是否断开,可以直接监听HttpContext.RequestAborted取消令牌:

public override async Task<TestResponse> TestCall(TestRequest request, ServerCallContext context)
{
    var httpContext = context.GetHttpContext();
    // 注册取消回调,客户端断开时触发
    using var cancelReg = httpContext.RequestAborted.Register(() =>
    {
        logger.Debug($"gRPC调用 {context.Method} 已取消,客户端连接断开");
    });

    // 你的业务逻辑,也可以直接将令牌传入异步方法实现自动取消
    await Task.Delay(10000, httpContext.RequestAborted);
    return new TestResponse();
}

注意事项

  • Microsoft.AspNetCore.Server.Kestrel.Connections、Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets下的类型多为内部API,无官方兼容性承诺,不建议在生产代码中直接引用
  • 自定义连接中间件拿到的是传输层TCP连接的生命周期事件,和上层协议无关,HTTP/2多路复用场景下,单个TCP连接会承载多个gRPC调用,TCP连接关闭时所有对应调用都会被取消

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 06:36:06