You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

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

火山引擎 最新活动