Unity与Python双向TCP通信实现咨询:双套接字可行性?
Unity与Python实现TCP双向通信方案
核心结论
完全可以使用两个独立的Socket分别处理收发,也能同时创建并连接不同端口的套接字。这种方案逻辑清晰,能有效避免单端口通信的粘包、同步阻塞问题,是实现双向实时通信的可靠选择。
具体实现方案
1. 端口规划
给收发逻辑分配不同端口,避免冲突:
- Unity → Python(位置数据发送):端口
55000 - Python → Unity(计算数据发送):端口
55001
2. Unity端修改
优化后的发送脚本(TCPSendPipe.cs)
修复原脚本重复创建流对象的问题,使用二进制传输提升效率:
using System; using System.IO; using System.Net.Sockets; using UnityEngine; public class TCPSendPipe : MonoBehaviour { public string Host = "localhost"; public int Port = 55000; private TcpClient _tcpClient; private NetworkStream _networkStream; private BinaryWriter _binaryWriter; public GameObject robot; void Start() { robot = GameObject.Find("robot"); _tcpClient = new TcpClient(); SetupConnection(); } void Update() { if (!_tcpClient.Connected) { SetupConnection(); return; } SendRobotPosition(); } private bool SetupConnection() { try { _tcpClient.Connect(Host, Port); _networkStream = _tcpClient.GetStream(); _binaryWriter = new BinaryWriter(_networkStream); Debug.Log("发送Socket连接成功"); return true; } catch (Exception e) { Debug.Log("发送Socket错误: " + e); return false; } } private void SendRobotPosition() { Vector3 robPos = robot.transform.position; byte[] sendBytes = new byte[12]; Buffer.BlockCopy(BitConverter.GetBytes(robPos.x), 0, sendBytes, 0, 4); Buffer.BlockCopy(BitConverter.GetBytes(robPos.y), 0, sendBytes, 4, 4); Buffer.BlockCopy(BitConverter.GetBytes(robPos.z), 0, sendBytes, 8, 4); _binaryWriter.Write(sendBytes); _binaryWriter.Flush(); } private void OnApplicationQuit() { _binaryWriter?.Close(); _networkStream?.Close(); if (_tcpClient != null && _tcpClient.Connected) _tcpClient.Close(); } }
优化后的接收脚本(TCPListenPipe.cs)
修改端口为55001,使用二进制读取替代字符串解析,避免阻塞:
using System; using System.IO; using System.Net; using System.Net.Sockets; using UnityEngine; public class TCPListenPipe : MonoBehaviour { public int Port = 55001; private TcpListener _tcpListener; private TcpClient _connectedClient; private NetworkStream _networkStream; private BinaryReader _binaryReader; void Awake() { _tcpListener = new TcpListener(IPAddress.Loopback, Port); _tcpListener.Start(); Debug.Log("监听Socket已启动,端口: " + Port); } void Update() { if (_connectedClient == null) { if (_tcpListener.Pending()) { _connectedClient = _tcpListener.AcceptTcpClient(); _networkStream = _connectedClient.GetStream(); _binaryReader = new BinaryReader(_networkStream); Debug.Log("接收Socket已连接"); } return; } if (_networkStream.DataAvailable) { try { float receivedData = _binaryReader.ReadSingle(); Debug.Log("收到Python数据: " + receivedData); } catch (EndOfStreamException) { Debug.Log("连接已断开"); CleanupConnection(); } } } private void CleanupConnection() { _binaryReader?.Close(); _networkStream?.Close(); _connectedClient?.Close(); _connectedClient = null; } private void OnApplicationQuit() { CleanupConnection(); _tcpListener.Stop(); } }
3. Python端修改
使用多线程分离收发逻辑,同时处理两个端口的通信:
import socket import struct import numpy as np import threading import time class DualComm: def __init__(self): self.RECV_PORT = 55000 # 接收Unity数据的端口 self.SEND_PORT = 55001 # 发送数据到Unity的端口 self.TCP_IP = '127.0.0.1' self.recv_conn = None self.send_socket = None self.running = True def _recv_handler(self): recv_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) recv_sock.bind((self.TCP_IP, self.RECV_PORT)) recv_sock.listen(1) print(f"等待Unity连接到端口 {self.RECV_PORT}...") self.recv_conn, addr = recv_sock.accept() print(f"Unity已连接,地址: {addr}") while self.running: try: byte_data = self.recv_conn.recv(12) if not byte_data: break pos_data = np.frombuffer(byte_data, dtype=np.float32) print(f"收到Unity位置数据: {pos_data}") # 此处可添加数据处理逻辑 except Exception as e: print(f"接收错误: {e}") break self.recv_conn.close() recv_sock.close() def _send_handler(self): time.sleep(2) # 等待Unity启动监听 while self.running: try: if not self.send_socket or self.send_socket._closed: self.send_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.send_socket.connect((self.TCP_IP, self.SEND_PORT)) print(f"已连接到Unity端口 {self.SEND_PORT}") # 示例:发送动态变化的浮点数据 cable_length = np.sin(time.time()) * 10 + 20 send_data = struct.pack('f', cable_length) self.send_socket.sendall(send_data) print(f"发送数据到Unity: {cable_length:.3f}") time.sleep(0.1) except ConnectionRefusedError: print("Unity监听未启动,重试连接...") time.sleep(1) except Exception as e: print(f"发送错误: {e}") self.send_socket = None time.sleep(1) self.send_socket.close() def start(self): recv_thread = threading.Thread(target=self._recv_handler) send_thread = threading.Thread(target=self._send_handler) recv_thread.start() send_thread.start() recv_thread.join() send_thread.join() def stop(self): self.running = False if __name__ == "__main__": comm = DualComm() try: comm.start() except KeyboardInterrupt: print("程序终止") comm.stop()
使用步骤
- 在Unity场景中同时挂载
TCPSendPipe和TCPListenPipe脚本 - 启动Unity游戏
- 运行Python脚本,自动建立双向连接并开始实时数据收发
关键优化点
- 采用二进制传输替代字符串,避免解析错误,提升传输效率
- 缓存流对象,避免每帧重复创建资源
- Python端用多线程分离收发逻辑,避免单线程阻塞
- 添加异常处理与重连逻辑,提升通信稳定性
内容的提问来源于stack exchange,提问作者Rohit
相关产品推荐
相关产品推荐

