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

如何有效跟踪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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.16 09:18:05