.NET Core中Socket连接半小时至两小时后无日志断开,求永久稳定方案
解决.NET Core Socket连接无日志自动断开问题,实现永久稳定连接
现有代码的核心问题
- 单连接仅处理一次数据:当前代码在接受连接后仅调用一次
Receive,之后直接回到监听循环,既无法接收设备后续发送的数据,也无法感知连接是否存活。 - 同步阻塞+无异常捕获:同步
Receive会阻塞线程,且未处理Socket操作可能抛出的SocketException等异常,一旦出现异常会直接终止整个监听流程,且无日志记录异常原因。 - 未启用TCP保活机制:长时间无数据传输时,中间网络设备(路由器、防火墙)会主动回收空闲连接,导致连接无预警断开。
- 无心跳交互逻辑:未与设备约定心跳机制,无法主动检测连接状态,也无法维持连接活跃。
修复方案与代码修改
1. 重构连接处理逻辑:异步独立处理每个连接
将每个连接的数据监听逻辑抽离为独立异步任务,避免阻塞主监听循环,同时持续处理该连接的后续数据:
public async Task StartListening() { _logService.CreateLogCmd($"StartListening get ip&&port: {ServerIP} {Port}", "StartListening"); var tcp = new TcpListener(IPAddress.Parse(ServerIP), Port); tcp.Start(); _logService.CreateLogCmd($"tcpStart ip&&port: {ServerIP} {Port}", "StartListening"); try { while (true) { var socket = await tcp.AcceptSocketAsync(); _logService.CreateLogCmd($"AcceptSocket from {socket.RemoteEndPoint}", "StartListening"); // 为每个连接启动独立异步任务处理数据,不阻塞监听循环 _ = HandleClientConnectionAsync(socket); } } catch (Exception ex) { _logService.CreateLogCmd($"Listening failed: {ex.Message}", "StartListening"); tcp.Stop(); } } private async Task HandleClientConnectionAsync(Socket socket) { var remoteEndpoint = socket.RemoteEndPoint.ToString(); _logService.CreateLogCmd($"Start handling connection: {remoteEndpoint}", "HandleClientConnection"); // 启用TCP KeepAlive,系统自动发送心跳维持连接 socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true); // 配置KeepAlive参数(根据实际网络调整) const int keepAliveTime = 60000; // 1分钟无数据则开始发送心跳 const int keepAliveInterval = 10000; // 每10秒发送一次心跳 byte[] keepAliveBytes = BitConverter.GetBytes(1); keepAliveBytes = keepAliveBytes.Concat(BitConverter.GetBytes(keepAliveTime)).ToArray(); keepAliveBytes = keepAliveBytes.Concat(BitConverter.GetBytes(keepAliveInterval)).ToArray(); socket.IOControl(IOControlCode.KeepAliveValues, keepAliveBytes, null); socket.ReceiveBufferSize = MAXBUFFERSIZE; socket.SendBufferSize = MAXBUFFERSIZE; byte[] buffer = new byte[MAXBUFFERSIZE]; try { while (socket.Connected) { // 异步接收数据,避免阻塞 int receivedBytes = await socket.ReceiveAsync(buffer, SocketFlags.None); if (receivedBytes == 0) { // 收到0字节表示连接已断开 _logService.CreateLogCmd($"Connection closed by client: {remoteEndpoint}", "HandleClientConnection"); break; } // 处理接收到的数据(截取实际收到的字节数,避免多余空数据) byte[] actualData = new byte[receivedBytes]; Array.Copy(buffer, actualData, receivedBytes); Analysis(actualData, socket); } } catch (SocketException ex) { _logService.CreateLogCmd($"Socket error on {remoteEndpoint}: {ex.Message} (ErrorCode: {ex.ErrorCode})", "HandleClientConnection"); } catch (Exception ex) { _logService.CreateLogCmd($"Unexpected error on {remoteEndpoint}: {ex.Message}", "HandleClientConnection"); } finally { // 确保Socket资源被释放 if (socket.Connected) { try { socket.Shutdown(SocketShutdown.Both); } catch { } } socket.Close(); socket.Dispose(); _logService.CreateLogCmd($"Connection disposed: {remoteEndpoint}", "HandleClientConnection"); } }
2. 实现心跳交互(可选,根据设备协议调整)
如果设备支持,约定心跳包格式,定期发送/接收心跳以主动检测连接状态:
private async Task SendHeartbeatAsync(Socket socket) { byte[] heartbeatPacket = GetHeartbeatPacket(); // 根据设备协议生成心跳包 await socket.SendAsync(heartbeatPacket, SocketFlags.None); _logService.CreateLogCmd("Heartbeat sent", "SendHeartbeat"); }
3. 关键配置说明
- TCP KeepAlive:通过
SetSocketOption和IOControl启用系统级TCP保活,让操作系统自动发送心跳包,避免中间网络设备断开空闲连接。 - 异步操作:使用
ReceiveAsync替代同步Receive,避免线程阻塞,提升程序并发能力。 - 异常处理:捕获
SocketException等异常,记录详细错误信息,确保单个连接的异常不会影响整个监听服务。 - 资源释放:在
finally块中关闭并释放Socket资源,避免资源泄漏。
额外排查方向
- 检查设备端是否有连接超时逻辑:确认设备是否会主动断开空闲连接,如有则需要同步心跳机制。
- 排查网络环境:检查服务器与设备之间的防火墙、路由器是否有空闲连接回收策略,必要时调整超时时间。
- 日志增强:在
Analysis方法和Socket操作的关键节点添加更详细的日志,便于定位问题。
内容的提问来源于stack exchange,提问作者Soroush Gorgin Pour
相关产品推荐
相关产品推荐

