Unity运行TCP代码无法连接Raspberry Pi问题求助
我仔细看过你的异步Socket代码了——在Visual Studio里能正常跑,但移植到Unity就失效,这种情况我遇到过好多次,基本都是Unity的线程模型、环境限制或者配置问题导致的,下面给你拆解问题点和适配Unity的修复方案:
核心问题分析
Unity的线程生命周期管理缺失
你原来的代码直接用独立线程启动监听,但没考虑Unity的对象生命周期——如果挂载脚本的GameObject被销毁或者场景切换,线程会变成野线程,而且Unity对后台线程的兼容性有要求,必须设置为后台线程才能随程序退出自动销毁。控制台输出与Unity日志的差异
你用的Console.WriteLine在Unity编辑器里虽然能看到,但打包后可能失效,换成Unity的Debug.Log/Debug.LogError才是正确的做法。网络权限与防火墙拦截
Unity默认可能限制网络访问,而且Windows防火墙大概率会拦截你的6670端口连接,这也是常见的“代码没问题但连不上”的原因。
适配Unity的完整代码
把代码改成MonoBehaviour脚本,挂载到场景中的任意GameObject上,就能正常运行了:
using System; using System.Globalization; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; using UnityEngine; // State object for reading client data asynchronously public class StateObject { // Client socket. public Socket workSocket = null; // Size of receive buffer. public const int BufferSize = 1024; // Receive buffer. public byte[] buffer = new byte[BufferSize]; // Received data string. public StringBuilder sb = new StringBuilder(); } public class UnityAsyncSocketListener : MonoBehaviour { // Thread signal. private static ManualResetEvent allDone = new ManualResetEvent(false); private Socket listener; private Thread listenThread; private bool isListening = true; // 优雅停止线程的标志位 [Header("Socket Settings")] public int port = 6670; void Start() { // 启动后台监听线程 listenThread = new Thread(StartListening); listenThread.IsBackground = true; listenThread.Start(); Debug.Log("Socket listener thread started"); } void OnDestroy() { // 优雅停止监听 isListening = false; allDone.Set(); // 唤醒等待的线程,让它退出循环 // 释放Socket资源 if (listener != null) { listener.Close(); listener.Dispose(); } // 释放事件资源 allDone.Dispose(); Debug.Log("Socket listener cleaned up properly"); } private void StartListening() { IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Any, port); Debug.Log($"Listening on {localEndPoint}"); listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); try { listener.Bind(localEndPoint); listener.Listen(100); while (isListening) { allDone.Reset(); Debug.Log("Waiting for a connection from Raspberry Pi..."); listener.BeginAccept(new AsyncCallback(AcceptCallback), listener); allDone.WaitOne(); // 等待连接建立 } } catch (Exception e) { Debug.LogError($"Socket initialization error: {e.Message}"); } } private void AcceptCallback(IAsyncResult ar) { allDone.Set(); // 通知主线程继续等待下一个连接 Socket listenerSocket = (Socket)ar.AsyncState; Socket handler = listenerSocket.EndAccept(ar); StateObject state = new StateObject(); state.workSocket = handler; handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReadCallback), state); Debug.Log("New client connected (Raspberry Pi)"); } private void ReadCallback(IAsyncResult ar) { StateObject state = (StateObject)ar.AsyncState; Socket handler = state.workSocket; int bytesRead = handler.EndReceive(ar); if (bytesRead <= 0) return; string stringRead = Encoding.ASCII.GetString(state.buffer, 0, bytesRead); state.sb.Append(stringRead); string content = state.sb.ToString(); if (content.Contains("<EOF>")) { Debug.Log($"Full data received: {content.TrimEnd("<EOF>".ToCharArray())}"); Send(handler, "Data received: " + content); state.sb.Clear(); // 重置缓冲区,准备接收下一次数据 } else { string timestamp = DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss.fff", CultureInfo.InvariantCulture); Debug.Log($"{timestamp} - Partial data: {stringRead}"); handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReadCallback), state); Send(handler, "boogieboogie"); } } private void Send(Socket handler, string data) { byte[] byteData = Encoding.ASCII.GetBytes(data); handler.BeginSend(byteData, 0, byteData.Length, 0, new AsyncCallback(SendCallback), handler); } private void SendCallback(IAsyncResult ar) { try { Socket handler = (Socket)ar.AsyncState; int bytesSent = handler.EndSend(ar); Debug.Log($"Sent {bytesSent} bytes to client"); } catch (Exception e) { Debug.LogError($"Send error: {e.Message}"); } } }
额外排查与配置步骤
树莓派连接IP确认
在Unity编辑器的Console里找到打印的Listening on xxx.xxx.xxx.xxx:6670,树莓派的Python代码必须用这个局域网IP连接,而不是127.0.0.1(那是本地回环,只能连本机)。防火墙设置
- 关闭Unity运行机器的Windows防火墙,或者添加入站规则:允许TCP端口6670的连接。
- 树莓派端也要确保没有防火墙拦截出站连接到这个端口。
Unity Player Settings配置
打开Edit > Project Settings > Player,找到Other Settings > Configuration > Internet Access,设置为Require——这一步很重要,打包后如果没开这个权限,网络连接会被完全拦截。线程停止优化
我在代码里加了isListening标志位,替代了粗暴的Thread.Abort(),这样线程会自然退出,避免资源泄漏。
内容的提问来源于stack exchange,提问作者cfkwok

