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

Unity C# TCP客户端断开时监听线程抛出ThreadAbortException问题

Unity 基于Thread+TcpClient短连接通信的异常问题排查

业务实现背景

项目中采用Thread搭配TcpClient实现与TCP服务端的读写通信,设计逻辑如下:

  • 客户端写入操作触发时初始化TCP连接
  • 待服务端响应校验完成后立即执行TCP断开逻辑
  • 通过短连接复用机制保障多异步用户场景下的套接字可用性,单客户端调用间隔为5-10秒
    实际运行时,在线程释放阶段执行断开操作会抛出一系列错误。

初始问题:线程中止触发读写异常

触发场景

调用DisconnectFromTCPServer()执行clientReceiveThread.Abort()时,监听委托ListenForData()内using (NetworkStream ... )代码块的while ((length = stream.Read ... )逻辑抛出如下异常:

ThreadAbortException: Thread was being aborted.
System.ComponentModel.Win32Exception..ctor (System.Int32 error) (at <1720f652f31145af877fbb9e0d1ba65c>:0)
System.Net.Sockets.SocketException..ctor (System.Net.Sockets.SocketError socketError) (at <1720f652f31145af877fbb9e0d1ba65c>:0)
System.Net.Sockets.Socket.Receive (System.Byte[] buffer, System.Int32 offset, System.Int32 size, System.Net.Sockets.SocketFlags socketFlags) (at <1720f652f31145af877fbb9e0d1ba65c>:0)
System.Net.Sockets.NetworkStream.Read (System.Byte[] buffer, System.Int32 offset, System.Int32 size) (at <1720f652f31145af877fbb9e0d1ba65c>:0)
Rethrow as IOException: Unable to read data from the transport connection: Thread was being aborted..
System.Net.Sockets.NetworkStream.Read (System.Byte[] buffer, System.Int32 offset, System.Int32 size) (at <1720f652f31145af877fbb9e0d1ba65c>:0)

初步排查结论

DisconnectFromTCPServer()中设置的isAlive标志未被ListenForData()委托正确识别,是触发异常的直接原因。该逻辑在新增NewData校验逻辑(主线程调用public Move AIResponse()且NewData为true时将serverMessage置为null)前可正常运行。

初始实现代码

private void ConnectToTcpServer()
        {
            try
            {
                Debug.Log("CONNECTING TO SERVER...");
                clientReceiveThread = new Thread(new ThreadStart(ListenForData));
                clientReceiveThread.IsBackground = true;
                clientReceiveThread.Start();
                isAlive = true;
            }
            catch (Exception e)
            {
                Debug.Log("On client connect exception " + e);
            }
        }

        private void DisconnectFromTcpServer()
        {
            isAlive = false;

            if (socketConnection.Connected) ((IDisposable)socketConnection).Dispose();

            StartCoroutine(ServerDisconnectedCoroutine(() => !socketConnection.Connected));

            if (clientReceiveThread.IsAlive) clientReceiveThread.Abort();
            clientReceiveThread.Join();

            ServerConnected = false;
        }

        private IEnumerator ServerDisconnectedCoroutine(System.Func<bool> socketDisconnected)
        {
            yield return new WaitUntil(socketDisconnected);
        }

        public void ListenForData()
        {
            // SWITCH PORTS ON EACH CONNECT - BALANCE THE LOAD
            // WRAP IN COUNTER
            port = port == 12345 ? 12344 : 12345;

            try
            {
                socketConnection = new TcpClient("65.21.xxx.xxx", 12345);
                Byte[] bytes = new Byte[1024];

                Thread.Sleep(100);

                while (isAlive)
                {
                    // Test socket connection
                    if (!socketConnection.Connected)
                    {
                        Debug.Log("DISCONNECTED LISTEN");
                        serverTimeout();
                        return;
                    }
                    else
                        ServerConnected = true;

                    // Get a stream object for reading              
                    using (NetworkStream stream = socketConnection.GetStream())
                    {
                        int length;
                        // Read incoming stream into byte arrary.                   
                        while ((length = stream.Read(bytes, 0, bytes.Length)) != 0)
                        {
                            var incomingData = new byte[length];
                            Array.Copy(bytes, 0, incomingData, 0, length);
                            // Convert byte array to string message.                        
                            serverMessage = Encoding.UTF8.GetString(incomingData);
                            Debug.Log("server message received as: " + serverMessage);
                            // ORIGINAL NewData = true;
                        }

                        stream.Close();
                    }
                }
            }
            catch (SocketException socketException)
            {
                Debug.Log("Socket exception: " + socketException);
            }
            catch (ThreadAbortException threadException)
            {
                Debug.Log("ThradException: " + threadException);
            }
        }

        public bool IfNewData()
        {
            if (serverMessage != null && !NewData) aiDataFromServer.LoadString(serverMessage);
            if (aiDataFromServer.type != "Error") NewData = true;

            return NewData;
        }

        public Move AIResponse()
        {
            NewData = false;
            // ORIGINAL - serverMessage ! set to null
            serverMessage = null;

            DisconnectFromTcpServer();

            return aiDataFromServer.moveData;
        }

已尝试修复方案

  • 延迟Thread.Abort()调用时机
  • 主动关闭NetworkStream与套接字
    以上方案均无法阻止监听委托尝试读取数据,触发异常时控制台已打印服务端响应,后续无数据传输,初始阶段未定位到根因。

衍生问题:初始异常修复后出现非预期套接字断开

参考相关方案排查调整代码后,ThreadAbortException错误已修复,但代码调整导致SocketConnection出现异常行为:执行完stream.CanRead、stream.DataAvailable判断与stream.Close()后,循环回到顶层判定!serverConnection.Connected,触发Debug.Log("DISCONNECTED LISTEN")逻辑导致服务端重连。此时主线程已调用AIResponse(),连接与线程本就会被关闭,但暂未定位到流读取完成后套接字连接被主动关闭的原因。

修改后代码

public void ListenForData()
{
    // SWITCH PORTS ON EACH CONNECT - BALANCE THE LOAD
    // WRAP IN COUNTER
    port = port == 12345 ? 12344 : 12345;

    try
    {
        socketConnection = new TcpClient("65.21.xxx.xxx", 12345);
        Byte[] bytes = new Byte[1024];

        while (isAlive)
        {
            Debug.Log("IS ALIVE");

            // Test socket connection
            if (!socketConnection.Connected && connectionRequired)
            {
                Debug.Log("DISCONNECTED LISTEN");
                serverTimeout();
                return;
            }
            else
                ServerConnected = true;

            using (NetworkStream stream = socketConnection.GetStream())
            {
                if (stream.CanRead)
                {
                    int _length;

                    do
                    {
                        _length = stream.Read(bytes, 0, bytes.Length);
                        var _incomingData = new byte[_length];
                        Array.Copy(bytes, 0, _incomingData, 0, _length);
                        serverMessage = Encoding.UTF8.GetString(_incomingData);
                        Debug.Log("server message received as: " + serverMessage);

                        // ALLOW REFERENCES TO BE UPDATED
                        if (!stream.DataAvailable)
                            Thread.Sleep(1);
                    }
                    while (stream.DataAvailable);
                }

                stream.Close();

                Debug.Log("STREAM CLOSED");
            }
        }
    }
    catch (SocketException socketException)
    {
        Debug.Log("Socket exception: " + socketException);
    }
    catch (ThreadAbortException threadException)
    {
        Debug.Log("ThradException: " + threadException);
    }
}

该衍生问题虽不影响核心流程,但不符合预期,需要对应的原因分析与优化方案。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 05:24:28