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

C#/Unity3D中TCP/IP客户端-服务器连接的线程更新问题求助

解决Unity客户端与聊天机器人服务器的连接重连问题

看起来你遇到的核心问题是服务器每次回复后就主动断开连接,而原来的代码在Update循环里反复调用连接逻辑,导致不断创建新线程,还没法正确管理线程的生命周期。我来给你梳理下修改思路,直接上可运行的改进代码:

核心问题分析

  • 原代码在Update里每次都调用ConnectToTcpServer("open"),这会导致每帧都新建一个监听线程,瞬间就会有大量线程同时运行,肯定出问题。
  • 没有跟踪连接状态,不知道当前是否已经连接成功,也没法判断什么时候需要重新连接。
  • 用Abort()终止线程是非常不推荐的方式,容易导致资源泄漏,应该用更优雅的方式终止线程。

修改后的代码

using System;
using System.Collections;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using UnityEngine;

public class UnityClient2 : MonoBehaviour
{
    private TcpClient _socketConnection;
    private Thread _clientReceiveThread;
    // 新增状态变量,跟踪连接和线程状态
    private bool _isConnected = false;
    private bool _isTryingReconnect = false;
    private readonly object _stateLock = new object(); // 线程安全锁

    void Start()
    {
        // 启动时只尝试连接一次
        AttemptServerConnection();
    }

    void Update()
    {
        // 只处理输入逻辑,不再在这里反复触发连接
        if (Input.GetKeyDown(KeyCode.Space))
        {
            // 发送消息前先确保连接正常
            SendMessageToServer();
        }

        // 连接断开时自动重试(加延迟避免频繁重试)
        if (!_isConnected && !_isTryingReconnect)
        {
            StartCoroutine(ReconnectAfterDelay(2f));
        }
    }

    /// <summary>
    /// 延迟重试连接的协程,避免短时间内频繁重试
    /// </summary>
    private IEnumerator ReconnectAfterDelay(float delay)
    {
        _isTryingReconnect = true;
        yield return new WaitForSeconds(delay);
        AttemptServerConnection();
        _isTryingReconnect = false;
    }

    /// <summary>
    /// 尝试与服务器建立连接,带状态检查避免重复创建线程
    /// </summary>
    private void AttemptServerConnection()
    {
        lock (_stateLock)
        {
            // 已经连接或线程还在运行时,直接返回
            if (_isConnected || (_clientReceiveThread != null && _clientReceiveThread.IsAlive))
            {
                return;
            }
        }

        try
        {
            _socketConnection = new TcpClient("127.0.0.1", 1024);
            _isConnected = true;

            // 仅在需要时创建监听线程
            _clientReceiveThread = new Thread(ListenForServerMessages);
            _clientReceiveThread.IsBackground = true;
            _clientReceiveThread.Start();
            Debug.Log("成功连接到聊天机器人服务器");
        }
        catch (SocketException socketException)
        {
            Debug.Log("连接服务器失败: " + socketException.Message);
            _isConnected = false;
        }
    }

    /// <summary>
    /// 后台监听线程,处理服务器消息,连接断开后自动更新状态
    /// </summary>
    private void ListenForServerMessages()
    {
        try
        {
            byte[] buffer = new byte[1024];
            using (NetworkStream stream = _socketConnection.GetStream())
            {
                int bytesRead;
                // 服务器关闭连接时,stream.Read会返回0,退出循环
                while (_isConnected && (bytesRead = stream.Read(buffer, 0, buffer.Length)) != 0)
                {
                    var incomingData = new byte[bytesRead];
                    Array.Copy(buffer, 0, incomingData, 0, bytesRead);
                    string serverReply = Encoding.ASCII.GetString(incomingData);
                    Debug.Log("收到服务器回复: " + serverReply);
                }
            }
        }
        catch (SocketException socketException)
        {
            Debug.Log("Socket异常: " + socketException.Message);
        }
        finally
        {
            // 无论正常退出还是异常,都标记连接断开
            lock (_stateLock)
            {
                _isConnected = false;
            }
            // 清理连接资源
            _socketConnection?.Close();
            _socketConnection = null;
            Debug.Log("与服务器的连接已断开");
        }
    }

    /// <summary>
    /// 发送消息到服务器,发送前确保连接正常
    /// </summary>
    private void SendMessageToServer()
    {
        lock (_stateLock)
        {
            if (!_isConnected || _socketConnection == null)
            {
                Debug.Log("未连接到服务器,先尝试重连");
                AttemptServerConnection();
                // 重连失败则直接返回
                if (!_isConnected) return;
            }
        }

        try
        {
            NetworkStream stream = _socketConnection.GetStream();
            if (stream.CanWrite)
            {
                // 按照ChatScript要求构造null终止的消息
                string clientMessage = "guest" + char.MinValue + "" + char.MinValue + "" + char.MinValue;
                byte[] messageBytes = Encoding.ASCII.GetBytes(clientMessage);
                stream.Write(messageBytes, 0, messageBytes.Length);
                Debug.Log("消息已发送到服务器");
            }
        }
        catch (SocketException socketException)
        {
            Debug.Log("发送消息异常: " + socketException.Message);
            lock (_stateLock)
            {
                _isConnected = false;
            }
        }
    }

    /// <summary>
    /// 场景销毁时清理资源,避免内存泄漏
    /// </summary>
    private void OnDestroy()
    {
        lock (_stateLock)
        {
            _isConnected = false;
        }
        // 优雅终止线程
        if (_clientReceiveThread != null && _clientReceiveThread.IsAlive)
        {
            _clientReceiveThread.Join(1000); // 等待1秒让线程自然结束
            if (_clientReceiveThread.IsAlive)
            {
                _clientReceiveThread.Abort(); // 万不得已才使用Abort
            }
        }
        _socketConnection?.Close();
    }
}

关键修改点说明

  1. 状态变量与线程安全:新增_isConnected、_isTryingReconnect和_stateLock,用来跟踪连接状态,避免多线程下的竞态条件,确保操作线程安全。
  2. 移除Update中的重复连接:只在启动时尝试一次连接,之后如果连接断开,通过协程延迟重试(避免短时间内频繁重试服务器)。
  3. 优雅的线程终止:在监听线程中通过_isConnected标志位控制循环,当服务器断开连接时自动退出线程,不再用Abort()这种暴力终止方式。
  4. 发送消息前检查连接:发送消息时会先验证连接状态,如果断开会自动尝试重连,再执行发送逻辑。
  5. 资源清理:在OnDestroy方法中正确关闭连接和终止线程,避免场景切换时的资源泄漏。

这样修改后,你的客户端就能正确处理服务器断开连接的情况,自动重连,而且不会再出现多线程重复创建的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 04:08:56