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

Unity3D与Python TCP连接多次重启后挂起问题优化问询

Unity TCP服务端无法处理Python客户端多次重连的解决方案

我之前也碰到过类似的TCP连接资源泄漏问题,你的代码核心问题在于客户端断开后的资源清理不彻底,以及状态管理逻辑有漏洞,导致多次重连后Python端的connect操作因为资源占用或者服务端状态异常而挂起。下面是具体的分析和少量修改后的代码,能让你的程序优雅处理客户端断开与重连:

问题分析

  1. 当Python客户端强制关闭(比如直接终止进程)时,C#端没有正确捕获这种异常断开的情况,client对象仍处于非空状态,但实际连接已失效,后续的Receive操作会抛出异常,却没有彻底清理资源和重置状态。
  2. 旧代码中只要listener.Pending()就接受新连接,没有检查当前是否已有活跃连接,可能导致多个连接冲突,占用端口资源。
  3. 没有统一的断开处理逻辑,不同场景下的断开操作(主动断开、异常断开)没有一致的资源清理步骤,导致残留无效的socket对象。

修改后的C#代码

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Net;
using System.Net.Sockets;
using System.Linq;
using System;

public class Visualizer : MonoBehaviour 
{
    TcpListener listener;
    private Socket client = null;
    const int byteCount = 1000;
    private const int recvPort = 11000;
    static private bool receiverConnected;
    static private bool receiverReady;
    private byte[] bytes = new byte[byteCount];

    void Start() 
    {
        receiverConnected = false;
        receiverReady = false;
        IPAddress localAddr = IPAddress.Parse("127.0.0.1");
        listener = new TcpListener(localAddr, recvPort);
        listener.Start();
        Debug.Log("TCP Listener started on port " + recvPort);
    }

    void Update() 
    {
        // 仅当当前无活跃连接时,才接受新的客户端连接
        if (!receiverConnected && listener.Pending()) 
        {
            // 清理可能残留的旧socket资源
            if (client != null)
            {
                try { client.Close(); }
                catch {}
                client = null;
            }

            client = listener.AcceptSocket();
            receiverConnected = true;
            receiverReady = false;
            Debug.Log("New client connected");
        }

        if (receiverConnected && client != null) 
        {
            try 
            {
                // 先检查Socket是否可读,避免主线程阻塞
                if (client.Poll(0, SelectMode.SelectRead))
                {
                    int bytesRec = client.Receive(bytes);
                    if (bytesRec == 0) 
                    {
                        // 客户端主动发起断开
                        HandleClientDisconnect();
                    } 
                    else 
                    {
                        bytes = bytes.Take(bytesRec).ToArray();
                        receiverReady = true;
                        // 在这里处理接收到的数据
                        ProcessReceivedData();
                    }
                }
            } 
            catch (SocketException ex) 
            {
                Debug.Log($"Socket error occurred: {ex.Message}");
                HandleClientDisconnect();
            }
            catch (Exception e) 
            {
                Debug.Log($"Unexpected error receiving data: {e.Message}");
                HandleClientDisconnect();
            }
        }
    }

    // 统一处理客户端断开的逻辑,确保资源彻底清理
    private void HandleClientDisconnect()
    {
        if (client != null)
        {
            try
            {
                client.Shutdown(SocketShutdown.Both);
                client.Close();
            }
            catch {}
            client = null;
        }
        receiverConnected = false;
        receiverReady = false;
        Debug.Log("Client disconnected, waiting for new connections");
    }

    // 抽离数据处理逻辑,让代码更清晰
    private void ProcessReceivedData()
    {
        // 替换成你的可视化逻辑,比如解析字节数据、更新UI/3D物体
        string receivedString = System.Text.Encoding.UTF8.GetString(bytes);
        Debug.Log($"Received data: {receivedString}");
    }

    // 程序退出时清理所有TCP资源,避免端口占用
    void OnApplicationQuit()
    {
        HandleClientDisconnect();
        if (listener != null)
        {
            listener.Stop();
        }
        Debug.Log("TCP Listener stopped");
    }
}

关键修改点说明

  • 统一断开处理方法HandleClientDisconnect():把所有断开场景(主动断开、异常断开)的资源清理、状态重置逻辑集中到一起,确保每次断开都彻底释放socket资源,重置连接状态。
  • 限制单客户端连接:只有当前没有活跃客户端时才接受新连接,避免多个连接冲突,保证服务端状态清晰。
  • 非阻塞检查client.Poll():在调用Receive()前先检查socket是否有可读数据,防止Unity主线程因为等待数据而阻塞。
  • 精准捕获Socket异常:客户端强制断开时会触发SocketException,单独捕获可以更准确处理这类场景,避免异常扩散。
  • 退出时清理资源:在OnApplicationQuit()中关闭listener和客户端socket,防止Unity退出后端口仍被占用,影响下次启动。

Python端小优化(可选)

为了避免Python端连接时无限挂起,可以添加超时和错误捕获:

import socket

TCP_IP = '127.0.0.1'
TCP_PORT = 11000
BUFFER_SIZE = 1000

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 设置5秒连接超时,避免无限等待
s.settimeout(5)
try:
    s.connect((TCP_IP, TCP_PORT))
    print("Successfully connected to Unity server")
    # 这里添加你的数据发送逻辑
except socket.timeout:
    print("Connection timed out: Unity server may not be running or busy")
except ConnectionRefusedError:
    print("Connection refused: Unity server is not listening")

这样修改后,不管Python客户端是正常关闭还是强制终止,Unity端都能正确清理资源并随时准备接受新的连接请求,不会再出现Python连接挂起的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 04:01:00