如何有效跟踪TCP客户端的连接状态?当前实现相关代码如下
如何有效跟踪TCP客户端的连接状态?当前实现相关代码如下
我看了你的代码,发现当前的实现有个关键问题:你用IsListening来标记连接状态,但这个属性其实只能表示你启动了监听流程,没法真实反映TCP客户端是否真的和服务器保持连通——毕竟连接建立后可能因为网络波动、服务器下线等原因断开,这时候IsListening还是true,但实际已经没连接了。
要有效跟踪连接状态,我们需要把「是否在监听/尝试连接」和「当前是否真的连通」这两个状态分开,再加上心跳检测和重连逻辑,具体改进方案如下:
第一步:拆分状态属性
新增一个IsConnected属性专门跟踪真实连接状态,和原有的IsListening区分开,同时给IsConnected也添加状态变更事件:
private bool _isListening; public bool IsListening { get => _isListening; private set { if (_isListening != value) { _isListening = value; OnIsListeningChanged?.Invoke(_isListening); } } } private bool _isConnected; public bool IsConnected { get => _isConnected; private set { if (_isConnected != value) { _isConnected = value; OnIsConnectedChanged?.Invoke(_isConnected); } } } public event Action<bool>? OnIsListeningChanged; public event Action<bool>? OnIsConnectedChanged; private CancellationTokenSource? _cancellationTokenSource;
第二步:完善监听方法,添加连接检测与重连
原来的StartListeningAsync方法在连接成功后就直接释放了TcpClient,没法维持长连接。我们需要改成循环监听的模式,同时通过心跳包检测连接是否存活:
public static async Task StartListeningAsync() { if (IsListening) return; IsListening = true; _cancellationTokenSource = new CancellationTokenSource(); var token = _cancellationTokenSource.Token; var serverAddress = Settings.Default.tcpIpAddr ?? "10.23.5.181"; if (!int.TryParse(Settings.Default.tcpIpPort, out var port)) { throw new InvalidOperationException("Port is not configured correctly."); } InitializeChannel(); try { while (!token.IsCancellationRequested) { using (var tcpClient = new TcpClient()) { try { await tcpClient.ConnectAsync(serverAddress, port, token); Debug.WriteLine($"TCP CLIENT IS CONNECTED TO {serverAddress}:{port}"); IsConnected = true; // 获取网络流,用于数据读写和心跳检测 var networkStream = tcpClient.GetStream(); var buffer = new byte[1024]; while (!token.IsCancellationRequested && tcpClient.Connected) { // 用Task.WhenAny实现心跳超时检测:要么读取数据,要么到时间发心跳 var readTask = networkStream.ReadAsync(buffer, 0, buffer.Length, token); var completedTask = await Task.WhenAny(readTask, Task.Delay(5000, token)); if (completedTask == readTask) { var bytesRead = await readTask; if (bytesRead == 0) { // 读取到0字节说明对方主动关闭连接 break; } // 这里添加你的数据处理逻辑 HandleReceivedData(buffer, bytesRead); } else { // 发送心跳包,检测连接是否还存活 await SendHeartbeatAsync(networkStream, token); } } } catch (OperationCanceledException) { // 主动取消监听,直接跳出循环 break; } catch (IOException ex) { Debug.WriteLine($"Connection error: {ex.Message}"); } finally { // 无论什么原因断开,都更新连接状态 IsConnected = false; Debug.WriteLine("TCP CLIENT DISCONNECTED"); } } // 断开后等待3秒再重连,避免频繁重试占用资源 if (!token.IsCancellationRequested) { await Task.Delay(3000, token); } } } finally { // 监听结束后清理资源,更新状态 IsListening = false; _cancellationTokenSource?.Dispose(); _cancellationTokenSource = null; } } // 发送心跳包的辅助方法 private static async Task SendHeartbeatAsync(NetworkStream stream, CancellationToken token) { try { var heartbeatData = Encoding.ASCII.GetBytes("HEARTBEAT"); await stream.WriteAsync(heartbeatData, 0, heartbeatData.Length, token); await stream.FlushAsync(token); } catch (IOException) { // 发送失败说明连接已断开,抛出异常让外层处理 throw; } } // 处理接收到的数据的辅助方法 private static void HandleReceivedData(byte[] buffer, int bytesRead) { var receivedText = Encoding.ASCII.GetString(buffer, 0, bytesRead); Debug.WriteLine($"Received data: {receivedText}"); // 这里替换成你的实际数据处理逻辑 }
关键改进点说明
- 状态拆分:
IsListening绑定你的toggle按钮,表示是否开启了监听/重连流程;IsConnected用于展示真实的连接状态,比如UI上的在线指示灯。 - 心跳检测:因为
TcpClient.Connected属性不能实时反映连接状态(它只记录最后一次操作的结果),所以通过定期发送心跳包+读写操作来检测连接是否存活,这是TCP长连接状态检测的常用方案。 - 自动重连:当连接断开后,自动等待一段时间再尝试重连,直到用户主动取消监听。
- 异常处理:覆盖了主动取消、连接异常、对方关闭连接等场景,确保状态能正确更新。
备注:内容来源于stack exchange,提问作者federico
相关产品推荐
相关产品推荐

