Azure负载均衡下C# SocketAsyncEventArgs客户端断开检测问题
问题描述
实现TCP服务端与客户端连接时,遇到一个本地可复现但服务器(部署在Azure负载均衡器下)持续存在的问题:当客户端断开Wi-Fi后,服务端无法检测到TCP连接已断开,也就无法执行断开后的处理逻辑。
已尝试的方案均无效:
- 每5秒发送心跳消息,使用
SocketAsyncEventArgs实现,但SendAsync并未如文档所述在Socket关闭时抛出ObjectDisposedException,且SendAsync成功不代表数据送达,无法用于检测连接状态。代码如下:
_socketAsyncEvent = new SocketAsyncEventArgs(); _socketAsyncEvent.Completed += OnSendCompleted; public void SendHeartbeat() { byte[] message = ...; _socketAsyncEvent.SetBuffer(message, 0, message.Length); if (!_connection.Socket.SendAsync(_socketAsyncEvent)) { OnSendCompleted(this, _socketAsyncEvent); } } protected void OnSendCompleted(object? sender, SocketAsyncEventArgs e) { _socketAsyncEvent.SetBuffer(null, 0, 0); }
- 使用
!(Socket.Poll(100, SelectMode.SelectRead) && Socket.Available == 0)检测,返回结果始终为true,无法判定连接状态。 - 本地环境中可通过接收结果的
BytesTransferred为0判断连接关闭,但服务器上未出现该情况。
需要了解:在Azure负载均衡器部署环境下,还有哪些方法可以从服务端判断TCP连接已断开?
解决方案
针对Azure负载均衡器下的TCP连接检测问题,可尝试以下几种可靠方法:
启用系统级TCP Keep-Alive
底层TCP Keep-Alive是最稳定的连接检测方式,Azure负载均衡器会响应这些探测包。在C#中通过Socket.IOControl配置:const int SIO_KEEPALIVE_VALS = 0x98000004; byte[] keepAliveConfig = new byte[12]; // 开启Keep-Alive BitConverter.GetBytes(1).CopyTo(keepAliveConfig, 0); // 5秒后发起第一次探测 BitConverter.GetBytes(5000).CopyTo(keepAliveConfig, 4); // 每次探测间隔1秒 BitConverter.GetBytes(1000).CopyTo(keepAliveConfig, 8); _connection.Socket.IOControl(SIO_KEEPALIVE_VALS, keepAliveConfig, null);客户端断开后,系统会自动发送探测包,多次失败后标记Socket为断开状态,后续Send/Receive操作会抛出异常或返回0字节。
实现带响应校验的应用层心跳
单纯发送心跳包无法确认客户端存活,需增加客户端响应机制:- 服务端每5秒发送心跳请求包;
- 为每个连接维护心跳超时计数器,收到客户端响应时重置计数器;
- 若计数器超过阈值(如15秒,即3次心跳未响应),主动关闭Socket并执行断开处理逻辑。
这种应用层心跳不受底层TCP状态和负载均衡器的限制,可靠性更高。
检查SocketAsyncEventArgs的错误状态
在OnSendCompleted方法中,必须校验e.SocketError属性。即使SendAsync未抛出异常,连接断开时该属性会返回ConnectionReset、NotConnected等错误码:protected void OnSendCompleted(object? sender, SocketAsyncEventArgs e) { if (e.SocketError != SocketError.Success) { // 连接已断开,执行清理逻辑 HandleConnectionDisposal(); } _socketAsyncEvent.SetBuffer(null, 0, 0); }调整Azure负载均衡器的空闲超时
Azure负载均衡器默认TCP空闲超时为4分钟,若心跳间隔(5秒)小于该值,负载均衡器会保持连接,但客户端断开后可能不会立即通知服务端。可在Azure门户调整空闲超时:- 标准负载均衡器可设置为30秒至30分钟;
- 确保超时时间大于心跳间隔,同时让负载均衡器在连接空闲超时后主动关闭连接,服务端即可检测到断开状态。
内容的提问来源于stack exchange,提问作者Anna Melashkina
相关产品推荐
相关产品推荐

