Unity 3D双编辑器基于TCP的客户端/服务器通信实现问询
Unity TCP 客户端/服务器实现(双编辑器数据传输)
我最近折腾了在两个Unity编辑器之间用TCP传输数据的简单方案,整理出了可用的客户端和服务器代码,分享给大家参考:
服务器端代码
这个脚本挂载到服务器端Unity场景的某个对象上,启动后会监听指定IP和端口的连接,并且每帧向客户端发送自身对象的位置信息:
using System; using System.Collections; using System.Collections.Generic; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; using UnityEngine; public class TCPTestServer : MonoBehaviour { #region private members /// <summary> /// TCPListener to listen for incomming TCP connection /// requests. /// </summary> private TcpListener tcpListener; /// <summary> /// Background thread for TcpServer workload. /// </summary> private Thread tcpListenerThread; /// <summary> /// Create handle to connected tcp client. /// </summary> private TcpClient connectedTcpClient; #endregion // Use this for initialization void Start () { // Start TcpServer background thread tcpListenerThread = new Thread (new ThreadStart(ListenForIncommingRequests)); tcpListenerThread.IsBackground = true; tcpListenerThread.Start(); } // Update is called once per frame void Update () { SendMessage(); } /// <summary> /// Runs in background TcpServerThread; Handles incomming TcpClient requests /// </summary> private void ListenForIncommingRequests () { try { // 注意:把"myip"替换成你的服务器IP,本地测试用127.0.0.1即可 tcpListener = new TcpListener(IPAddress.Parse("myip"), 65535); tcpListener.Start(); Debug.Log("Server is listening"); Byte[] bytes = new Byte[1024]; while (true) { using (connectedTcpClient = tcpListener.AcceptTcpClient()) { // Get a stream object for reading using (NetworkStream stream = connectedTcpClient.GetStream()) { int length; // Read incomming stream into byte arrary. while ((length = stream.Read(bytes, 0, bytes.Length)) != 0) { var incommingData = new byte[length]; Array.Copy(bytes, 0, incommingData, 0, length); // Convert byte array to string message. string clientMessage = Encoding.ASCII.GetString(incommingData); Debug.Log("client message received as: " + clientMessage); } } } } } catch (SocketException socketException) { Debug.Log("SocketException " + socketException.ToString()); } } /// <summary> /// Send message to client using socket connection. /// </summary> private void SendMessage() { if (connectedTcpClient == null) { return; } try { // Get a stream object for writing. NetworkStream stream = connectedTcpClient.GetStream(); if (stream.CanWrite) { string serverMessage = this.gameObject.transform.position.ToString(); // Convert string message to byte array. byte[] serverMessageAsByteArray = Encoding.ASCII.GetBytes(serverMessage); // Write byte array to socketConnection stream. stream.Write(serverMessageAsByteArray, 0, serverMessageAsByteArray.Length); Debug.Log("Server sent his message - should be received by client"); } } catch (SocketException socketException) { Debug.Log("Socket exception: " + socketException); } } void OnApplicationQuit() { // 补充:停止监听再终止线程,避免资源泄漏 tcpListener?.Stop(); tcpListenerThread.Abort(); connectedTcpClient?.Close(); } }
客户端代码
这个脚本挂载到客户端Unity场景的对象上,启动后会连接到服务器,按下空格键时向服务器发送测试消息,同时监听服务器发来的数据:
using System; using System.Collections; using System.Collections.Generic; using System.Net.Sockets; using System.Text; using System.Threading; using UnityEngine; public class TCPTestClient : MonoBehaviour { #region private members private TcpClient socketConnection; private Thread clientReceiveThread; #endregion // Use this for initialization void Start () { ConnectToTcpServer(); } // Update is called once per frame void Update () { if (Input.GetKeyDown(KeyCode.Space)) { SendMessage(); } } /// <summary> /// Setup socket connection. /// </summary> private void ConnectToTcpServer () { try { clientReceiveThread = new Thread (new ThreadStart(ListenForData)); clientReceiveThread.IsBackground = true; clientReceiveThread.Start(); } catch (Exception e) { Debug.Log("On client connect exception " + e); } } /// <summary> /// Runs in background clientReceiveThread; Listens for incomming data. /// </summary> private void ListenForData() { try { // 注意:把"myip"替换成服务器的IP,本地测试用127.0.0.1 socketConnection = new TcpClient("myip", 65535); Byte[] bytes = new Byte[1024]; while (true) { // Get a stream object for reading using (NetworkStream stream = socketConnection.GetStream()) { int length; // Read incomming stream into byte arrary. while ((length = stream.Read(bytes, 0, bytes.Length)) != 0) { var incommingData = new byte[length]; Array.Copy(bytes, 0, incommingData, 0, length); // Convert byte array to string message. string serverMessage = Encoding.ASCII.GetString(incommingData); Debug.Log("server message received as: " + serverMessage); } } } } catch (SocketException socketException) { Debug.Log("Socket exception: " + socketException); } } /// <summary> /// Send message to server using socket connection. /// </summary> private void SendMessage() { if (socketConnection == null) { return; } try { // Get a stream object for writing. NetworkStream stream = socketConnection.GetStream(); if (stream.CanWrite) { string clientMessage = "This is a message from one of your clients."; // Convert string message to byte array. byte[] clientMessageAsByteArray = Encoding.ASCII.GetBytes(clientMessage); // Write byte array to socketConnection stream. stream.Write(clientMessageAsByteArray, 0, clientMessageAsByteArray.Length); Debug.Log("Client sent his message - should be received by server"); } } catch (SocketException socketException) { Debug.Log("Socket exception: " + socketException); } } }
一些注意事项
- 测试时先启动服务器端的Unity实例,再启动客户端,否则客户端会连接失败
- 本地测试可以把IP换成
127.0.0.1,跨设备测试要确保两台设备在同一局域网,并且防火墙允许65535端口的TCP连接 - 后台线程里不要直接修改Unity的场景对象(比如修改Transform),如果需要的话,可以用主线程调度器(比如自己实现一个队列,在Update里处理)
- 服务器的
OnApplicationQuit我补充了tcpListener?.Stop(),避免直接终止线程导致的资源泄漏问题
内容的提问来源于stack exchange,提问作者user878813




