Unity3D与Python TCP连接多次重启后挂起问题优化问询
Unity TCP服务端无法处理Python客户端多次重连的解决方案
我之前也碰到过类似的TCP连接资源泄漏问题,你的代码核心问题在于客户端断开后的资源清理不彻底,以及状态管理逻辑有漏洞,导致多次重连后Python端的connect操作因为资源占用或者服务端状态异常而挂起。下面是具体的分析和少量修改后的代码,能让你的程序优雅处理客户端断开与重连:
问题分析
- 当Python客户端强制关闭(比如直接终止进程)时,C#端没有正确捕获这种异常断开的情况,
client对象仍处于非空状态,但实际连接已失效,后续的Receive操作会抛出异常,却没有彻底清理资源和重置状态。 - 旧代码中只要
listener.Pending()就接受新连接,没有检查当前是否已有活跃连接,可能导致多个连接冲突,占用端口资源。 - 没有统一的断开处理逻辑,不同场景下的断开操作(主动断开、异常断开)没有一致的资源清理步骤,导致残留无效的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
相关产品推荐
相关产品推荐

