【Unity网络通讯】一招教你使用TCP、UDP协议

一篇Unity即当客户端又当服务端的文章
通讯协议TCP、UDP
服务端有许多重复的方法,感兴趣的小伙伴可以写成基类降低重复率

一、前言

实现通过TCP或UDP协议发送指定命令,接收端做出控制对象激活相关操作;
注意仔细看结尾总结;
Demo下载

二、TCP协议

客户端(发送端)

public class TCPSender : MonoBehaviour
{
    
    
    [Header("目标IP地址")] public string ipAddress = "127.0.0.1"; 
    [Header("目标端口号")] public int port = 12345; 
    [Header("发送内容")] public string hexCode = "FE 04 00 00 00 01 25 C5"; 
    [Header("发送按钮")] public Button Btn_Send; 
    
    private TcpClient client;
    private NetworkStream stream;

    private void Start()
    {
    
    
        Btn_Send.onClick.AddListener(OnButtonClick);
        client = new TcpClient(ipAddress, port);
    }

    public void OnButtonClick()
    {
    
    
        stream = client.GetStream();
        byte[] data = Encoding.UTF8.GetBytes(hexCode);
        stream.Write(data, 0, data.Length);
    }

    private void OnApplicationQuit()
    {
    
    
        client.Close();
    }
}

服务端(接收端)

using System;
using System.Text;
using System.Net;
using System.Net.Sockets;
using UnityEngine;

public class TCPSever : MonoBehaviour
{
    
    
    private TcpListener tcpListener;

    [Header("接收内容")] public string Response;
    [Header("是否激活对象")] public bool IsActive;
    [Header("待激活对象")] public GameObject[] OBJ;
    [Header("服务器端口")] public int Port = 12345;
    [Header("是否需要激活时间")] public bool IsActiveTime;
    [Header("激活时间")] public float ActiveTime;

    private float ActiveTimeTemp;
    private bool isStart;
    private UnityMainThreadDispatcher mainThreadDispatcher;
    private void Start()
    {
    
    
        try
        {
    
    
            // 创建TCP监听器
            tcpListener = new TcpListener(IPAddress.Any, Port);
            tcpListener.Start();
            Debug.Log("TCP服务器已启动,等待客户端连接...");

            //开启主线程
            mainThreadDispatcher = UnityMainThreadDispatcher.Instance;
           
            // 在新线程中等待客户端连接
            System.Threading.Thread acceptThread = new System.Threading.Thread(AcceptClients);
            acceptThread.Start();
        }
        catch (Exception e)
        {
    
    
            Debug.LogError("发生错误: " + e.Message);
        }
    }

    private void AcceptClients()
    {
    
    
        while (true)
        {
    
    
            // 等待客户端连接
            TcpClient client = tcpListener.AcceptTcpClient();
            Debug.Log("客户端已连接: " + ((IPEndPoint)client.Client.RemoteEndPoint).Address);

            // 在新线程中处理客户端请求
            System.Threading.Thread clientThread = new System.Threading.Thread(() => HandleClient(client));
            clientThread.Start();

        }
    }

    private void HandleClient(TcpClient client)
    {
    
    
        try
        {
    
    
            NetworkStream stream = client.GetStream();
            byte[] buffer = new byte[1024];
            int bytesRead;

            while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) != 0)
            {
    
    
                // 处理客户端发送的数据
                string data = Encoding.UTF8.GetString(buffer, 0, bytesRead);
                Debug.Log("接收到消息: " + data);
                
                //处理消息
                Client(data);

                // 可以在这里回复客户端
                string response = "服务器已接收消息: " + data;
                byte[] responseBytes = Encoding.UTF8.GetBytes(response);
                stream.Write(responseBytes, 0, responseBytes.Length);
            }

            client.Close();
        }
        catch (Exception e)
        {
    
    
            Debug.LogError("处理客户端请求时发生错误: " + e.Message);
        }
    }

    private void OnApplicationQuit()
    {
    
    
        if (tcpListener != null)
        {
    
    
            tcpListener.Stop();
        }
    }

    public void Client(string received)
    {
    
    
        if (received != null)
        {
    
    
            //表现
            Response = received;

            ComparisonResponse(received);
        }

    }

    //处理请求
    public void ComparisonResponse(string response)
    {
    
    
        switch (response)
        {
    
    
            case "FE 04 00 00 00 01 25 C5":
                TimeOnclick();
                break;
        }
    }


    public void TimeOnclick()
    {
    
    
        if (IsActiveTime)
        {
    
    
            isStart = true;
            ActiveTimeTemp = ActiveTime;
        }
        else
        {
    
    
            ObjActive();
        }
    }


    public void ObjActive()
    {
    
    
        mainThreadDispatcher.Enqueue(() =>
        {
    
    
            foreach (GameObject o in OBJ)
            {
    
    
                o.SetActive(IsActive);
            }
        });
    }

    private void Update()
    {
    
    
        if (isStart)
        {
    
    
            ActiveTimeTemp -= Time.deltaTime;
            if (ActiveTimeTemp <= 0)
            {
    
    
                ObjActive();
                isStart = false;
            }
        }
    }
}

三、UDP协议

客户端(发送端)

public class UdpSender : MonoBehaviour
{
    
    
    [Header("目标IP地址")] public string ipAddress = "127.0.0.1"; 
    [Header("目标端口号")] public int port = 12345; 
    [Header("发送内容")] public string hexCode = "FE 04 00 00 00 01 25 C5"; 
    [Header("发送按钮")] public Button Btn_Send;      
    private UdpClient udpClient;

    private void Start()
    {
    
    
        Btn_Send.onClick.AddListener(OnButtonClick);
    }

    public void OnButtonClick()
    {
    
    
        udpClient = new UdpClient(ipAddress, port);
        byte[] data = Encoding.UTF8.GetBytes(hexCode);
        udpClient.Send(data, data.Length);
        
        
    }

    private void OnApplicationQuit()
    {
    
    
        udpClient.Close();
    }
}

服务器(接收端)

using System;
using System.Collections;
using System.Net;
using System.Net.Sockets;
using System.Text;
using UnityEngine;

public class UDPSever : MonoBehaviour
{
    
    
    private UdpClient udpServer;
    
    [Header("接收内容")] public string Response;
    [Header("是否激活对象")] public bool IsActive;
    [Header("待激活对象")] public GameObject[] OBJ;
    [Header("服务器端口")] public int Port = 12345;
    [Header("是否需要激活时间")] public bool IsActiveTime;
    [Header("激活时间")] public float ActiveTime;

    private float ActiveTimeTemp;
    private bool isStart;
    private UnityMainThreadDispatcher mainThreadDispatcher;
    private void Start()
    {
    
    
        try
        {
    
    
            // 启动UDP服务器
            udpServer = new UdpClient(Port);
            Debug.Log("UDP服务器已启动,等待客户端连接...");

            mainThreadDispatcher = UnityMainThreadDispatcher.Instance;
            
            // 在新线程中接收来自客户端的数据
            IPEndPoint clientEndPoint = new IPEndPoint(IPAddress.Any, Port);
            StartServerReceiver(udpServer, clientEndPoint);
        }
        catch (Exception e)
        {
    
    
            Debug.LogError("发生错误: " + e.Message);
        }
    }

    private void StartServerReceiver(UdpClient server, IPEndPoint clientEndPoint)
    {
    
    
        System.Threading.Thread serverThread = new System.Threading.Thread(() =>
        {
    
    
            while (true)
            {
    
    
                byte[] receivedData = server.Receive(ref clientEndPoint);
                string receivedMessage = Encoding.UTF8.GetString(receivedData);
                
                Client(receivedMessage);

                // 在这里添加向客户端发送响应的代码
                string responseMessage = "服务器已收到消息: " + receivedMessage;
                byte[] responseData = Encoding.UTF8.GetBytes(responseMessage);
                server.Send(responseData, responseData.Length, clientEndPoint);
            }
        });

        serverThread.Start();
    }

    public void Client(string received)
    {
    
    
        if (received != null)
        {
    
    
                    
            //表现
            Response = received;

            ComparisonResponse(received);
        }

    }

    public void ComparisonResponse(string response)
    {
    
    
        switch (response)
        {
    
    
            case "FE 04 00 00 00 01 25 C5":
                TimeOnclick();
                break;
        }
    }
    
    
    public void TimeOnclick()
    {
    
    
        if (IsActiveTime)
        {
    
    
            isStart = true;
            ActiveTimeTemp = ActiveTime;
        }
        else
        {
    
    
            ObjActive();
        }
    }

    
    public void ObjActive()
    {
    
    
        mainThreadDispatcher.Enqueue(() => {
    
    
            foreach (GameObject o in OBJ)
            {
    
    
                o.SetActive(IsActive);
            }
        });
    }

    private void OnApplicationQuit()
    {
    
    
        udpServer.Close();
    }

    private void Update()
    {
    
    
        if (isStart)
        {
    
    
            ActiveTimeTemp -= Time.deltaTime;
            if (ActiveTimeTemp <= 0)
            {
    
    
                ObjActive();
                isStart = false;
            }
        }
    }
}

四、总结

比如携程、Invoke函数、控制物体显隐必须在主线程进行操作,然后我们服务端监听客户端时间时候启用了多线程;如果不采用主线程则无法完成控制物体显隐的操作;

解决方案:通过创建主线程实现,以下脚本无需挂载直接实例化调用即可

using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;

public class UnityMainThreadDispatcher : MonoBehaviour
{
    
    
    private static readonly Queue<Action> executionQueue = new Queue<Action>();

    private static UnityMainThreadDispatcher _instance = null;

    public static bool Exists
    {
    
    
        get {
    
     return _instance != null; }
    }

    public static UnityMainThreadDispatcher Instance
    {
    
    
        get
        {
    
    
            if (!Exists)
            {
    
    
                _instance = new GameObject("MainThreadDispatcher").AddComponent<UnityMainThreadDispatcher>();
            }
            return _instance;
        }
    }

    void Update()
    {
    
    
        while (executionQueue.Count > 0)
        {
    
    
            executionQueue.Dequeue().Invoke();
        }
    }

    public void Enqueue(IEnumerator action)
    {
    
    
        executionQueue.Enqueue(() => {
    
     StartCoroutine(action); });
    }

    public void Enqueue(Action action)
    {
    
    
        executionQueue.Enqueue(action);
    }
}

猜你喜欢

转载自blog.csdn.net/Xz616/article/details/134217385