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

如何判断TcpClient连接断开并实现自动重连(求代码示例)

解决TCP客户端自动重连的可靠方案

这问题我太熟了!TcpClient.Connected确实是个坑——它只记录上次IO操作时的连接状态,完全没法实时反映当前连接是否正常。比如对方突然断电、网络断了,你的客户端Connected还是true,直到你尝试发数据才会触发异常更新状态。下面是我实践下来最靠谱的几个方案:

1. 心跳包机制(首选方案)

这是工业级TCP客户端的标准操作:定期发送一个极小的"心跳"探测包,服务端收到后回复确认。一来能检测连接是否存活,二来能防止中间网络设备(比如路由器)把长时间空闲的连接主动断开。

代码示例:带心跳+自动重连的TCP客户端

public class TcpClientWrapper
{
    private TcpClient _client;
    private readonly string _host;
    private readonly int _port;
    private readonly CancellationTokenSource _cts = new CancellationTokenSource();
    private readonly TimeSpan _heartbeatInterval = TimeSpan.FromSeconds(10);

    public TcpClientWrapper(string host, int port)
    {
        _host = host;
        _port = port;
    }

    // 初始化连接+启动心跳
    public async Task StartAsync()
    {
        await ReconnectAsync();
        _ = Task.Run(HeartbeatLoopAsync, _cts.Token);
        _ = Task.Run(MessageReadingLoopAsync, _cts.Token);
    }

    // 重连逻辑
    private async Task ReconnectAsync()
    {
        // 先清理旧连接
        _client?.Close();
        _client?.Dispose();

        while (!_cts.Token.IsCancellationRequested)
        {
            try
            {
                Console.WriteLine("尝试重连...");
                _client = new TcpClient();
                await _client.ConnectAsync(_host, _port);
                Console.WriteLine("连接成功!");
                break;
            }
            catch (SocketException)
            {
                // 连接失败,等待2秒重试
                await Task.Delay(TimeSpan.FromSeconds(2), _cts.Token);
            }
        }
    }

    // 心跳循环
    private async Task HeartbeatLoopAsync()
    {
        while (!_cts.Token.IsCancellationRequested)
        {
            try
            {
                if (_client.Connected)
                {
                    // 发送心跳包(这里用一个字节的标识,比如0x00)
                    await _client.GetStream().WriteAsync(new byte[] { 0x00 }, 0, 1, _cts.Token);
                }
            }
            catch (IOException)
            {
                // 发送失败,说明连接断了,触发重连
                await ReconnectAsync();
            }
            await Task.Delay(_heartbeatInterval, _cts.Token);
        }
    }

    // 消息读取循环(整合你提供的ReadMessageAsync)
    private async Task MessageReadingLoopAsync()
    {
        while (!_cts.Token.IsCancellationRequested)
        {
            try
            {
                if (_client.Connected)
                {
                    var message = await _client.ReadMessageAsync();
                    // 处理收到的消息
                    Console.WriteLine($"收到消息:{message}");
                }
            }
            catch (IOException)
            {
                // 读取失败,连接已断开
                Console.WriteLine("连接断开,开始重连...");
                await ReconnectAsync();
            }
        }
    }

    // 你的ReadMessageAsync扩展方法,补充异常处理
    public static async Task<Message> ReadMessageAsync(this TcpClient client)
    {
        if (!client.Connected)
            throw new IOException("客户端未连接");

        var stream = client.GetStream();
        // 这里假设你的Message有固定长度的头部或者自定义的序列化逻辑
        // 示例:先读取长度,再读取内容
        var lengthBuffer = new byte[4];
        var bytesRead = await stream.ReadAsync(lengthBuffer, 0, 4);
        if (bytesRead == 0)
            throw new IOException("连接已关闭");

        var length = BitConverter.ToInt32(lengthBuffer, 0);
        var contentBuffer = new byte[length];
        bytesRead = await stream.ReadAsync(contentBuffer, 0, length);
        if (bytesRead != length)
            throw new IOException("消息不完整,连接可能异常");

        // 反序列化为Message对象
        return JsonSerializer.Deserialize<Message>(contentBuffer);
    }

    // 停止客户端
    public void Stop()
    {
        _cts.Cancel();
        _client?.Close();
        _client?.Dispose();
    }
}

2. 依赖IO操作的异常捕获(轻量方案)

如果不需要心跳,也可以直接在读写操作时捕获异常——只要连接断开,任何IO操作(读/写)都会抛出IOException或SocketException,这时候就触发重连逻辑。

这种方案适合连接状态变化不频繁、对实时性要求不高的场景,代码更简洁:

public async Task RunClientAsync(string host, int port)
{
    TcpClient client = null;
    var cts = new CancellationTokenSource();

    while (!cts.Token.IsCancellationRequested)
    {
        try
        {
            client = new TcpClient();
            await client.ConnectAsync(host, port);
            Console.WriteLine("连接成功");

            // 持续读取消息
            while (client.Connected)
            {
                var message = await client.ReadMessageAsync();
                // 处理消息
            }
        }
        catch (IOException)
        {
            Console.WriteLine("连接断开,准备重连");
            client?.Dispose();
            await Task.Delay(TimeSpan.FromSeconds(2));
        }
    }
}

关键注意点

  • 永远不要依赖TcpClient.Connected做连接判断:它只是本地缓存的状态,不是实时检测结果。
  • 重连时一定要清理旧连接:关闭旧的TcpClient和流,避免资源泄漏。
  • 用异步操作避免阻塞:所有的连接、读写、延迟都用异步方法,防止UI或主线程卡死。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 09:25:14