Unity 实现手机端和电脑项目在局域网内通信

电脑端启动后自动广播自身存在,手机端启动后监听广播并发现服务器。

发现后自动建立 UDP 连接,双方可互发消息。

内置心跳检测,网络中断时会自动检测并提示断开

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

public class ComputerServer : MonoBehaviour
{
    private const int broadcastPort = 1369;
    private const int communicationPort = 1370; // 通信端口
    private UdpClient broadcastClient;
    private UdpClient commClient;
    private Thread broadcastThread;
    private Thread receiveThread;
    private IPEndPoint clientEndpoint;
    private bool isRunning = true;
    private float lastHeartbeatTime;
    private const float heartbeatInterval = 2f; // 心跳间隔
    private const float timeoutThreshold = 5f; // 超时阈值

    // 主线程任务队列
    private Queue<System.Action> mainThreadActions = new Queue<System.Action>();

    void Start()
    {
        // 初始化广播客户端
        broadcastClient = new UdpClient();
        broadcastThread = new Thread(BroadcastMessage);
        broadcastThread.IsBackground = true;
        broadcastThread.Start();

        // 初始化通信客户端
        commClient = new UdpClient(communicationPort);
        receiveThread = new Thread(ReceiveMessages);
        receiveThread.IsBackground = true;
        receiveThread.Start();

        // 主线程初始化时间
        lastHeartbeatTime = Time.time;
    }

    // 将操作放入主线程队列
    private void RunOnMainThread(System.Action action)
    {
        lock (mainThreadActions)
        {
            mainThreadActions.Enqueue(action);
        }
    }

    void BroadcastMessage()
    {
        while (isRunning)
        {
            try
            {
                string message = "ServerHere";
                byte[] data = Encoding.UTF8.GetBytes(message);
                IPEndPoint broadcastEndpoint = new IPEndPoint(IPAddress.Broadcast, broadcastPort);
                broadcastClient.Send(data, data.Length, broadcastEndpoint);
                Debug.Log("Broadcast sent: ServerHere");
                Thread.Sleep(1000);
            }
            catch (System.Exception e)
            {
                Debug.LogError("Broadcast error: " + e.Message);
            }
        }
    }

    void ReceiveMessages()
    {
        while (isRunning)
        {
            try
            {
                IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
                byte[] data = commClient.Receive(ref sender);
                string message = Encoding.UTF8.GetString(data);

                // 处理心跳包 - 使用主线程更新时间
                if (message == "Heartbeat")
                {
                    // 将时间更新操作放入主线程队列
                    RunOnMainThread(() => {
                        lastHeartbeatTime = Time.time;
                    });
                    Debug.Log("Received heartbeat from client");
                    SendMessageToClient("HeartbeatAck"); // 回复心跳确认
                }
                else
                {
                    Debug.Log("Received from client: " + message);
                    // 处理客户端发送的业务消息
                }

                // 记录客户端端点(首次连接时)
                if (clientEndpoint == null)
                {
                    clientEndpoint = sender;
                }
            }
            catch (SocketException e)
            {
                if (e.SocketErrorCode == SocketError.Interrupted)
                {
                    Debug.Log("Receive thread interrupted");
                }
                else
                {
                    Debug.LogError("Receive error: " + e.Message + " (Code: " + e.SocketErrorCode + ")");
                }
            }
            catch (System.Exception e)
            {
                Debug.LogError("Receive error: " + e.Message);
            }
        }
    }

    // 发送消息到客户端
    public void SendMessageToClient(string message)
    {
        if (clientEndpoint == null)
        {
            Debug.LogWarning("No client connected");
            return;
        }

        try
        {
            byte[] data = Encoding.UTF8.GetBytes(message);
            commClient.Send(data, data.Length, clientEndpoint);
        }
        catch (System.Exception e)
        {
            Debug.LogError("Send error: " + e.Message);
        }
    }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Tab))
            SendMessageToClient("我是服务端");
        // 处理主线程任务队列
        while (mainThreadActions.Count > 0)
        {
            System.Action action;
            lock (mainThreadActions)
            {
                action = mainThreadActions.Dequeue();
            }
            action.Invoke();
        }

        // 检测客户端超时
        if (clientEndpoint != null && Time.time - lastHeartbeatTime > timeoutThreshold)
        {
            Debug.LogWarning("Client timeout, disconnect detected");
            clientEndpoint = null; // 重置连接状态
        }

        // 定时发送心跳(如果已连接)
        if (clientEndpoint != null && Time.time - lastHeartbeatTime > heartbeatInterval)
        {
            SendMessageToClient("Heartbeat");
            lastHeartbeatTime = Time.time;
        }
    }

    void OnDestroy()
    {
        isRunning = false;

        // 安全终止线程
        if (broadcastThread != null && broadcastThread.IsAlive)
        {
            broadcastThread.Interrupt();
        }
        if (receiveThread != null && receiveThread.IsAlive)
        {
            receiveThread.Interrupt();
        }

        // 关闭客户端
        broadcastClient?.Close();
        commClient?.Close();
    }
}
using UnityEngine;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using UnityEngine.UI;
using System.Collections.Generic;

//按下TAb发送消息

public class MobileClient : MonoBehaviour
{
    private const int broadcastPort = 1369;
    private const int communicationPort = 1370;
    private UdpClient broadcastClient;
    private UdpClient commClient;
    private Thread broadcastThread;
    private Thread receiveThread;
    private string serverIP = null;
    private IPEndPoint serverEndpoint;
    private bool isRunning = true;
    private float lastHeartbeatTime;
    private const float heartbeatInterval = 2f;
    private const float timeoutThreshold = 5f;

    // 主线程任务队列
    private Queue<System.Action> mainThreadActions = new Queue<System.Action>();

    public Text statusText;
    public InputField messageInput;

    void Start()
    {
        try
        {
            broadcastClient = new UdpClient(broadcastPort);
            broadcastClient.EnableBroadcast = true;

            broadcastThread = new Thread(ReceiveBroadcast);
            broadcastThread.IsBackground = true;
            broadcastThread.Start();

            // 在主线程初始化时间
            lastHeartbeatTime = Time.time;
            RunOnMainThread(() => {
                statusText.text = "Searching for server...";
            });
        }
        catch (System.Exception e)
        {
            Debug.LogError("Initialization error: " + e.Message);
            RunOnMainThread(() => {
                statusText.text = "Initialization failed";
            });
        }
    }

    private void RunOnMainThread(System.Action action)
    {
        lock (mainThreadActions)
        {
            mainThreadActions.Enqueue(action);
        }
    }

    void ReceiveBroadcast()
    {
        while (isRunning && serverIP == null)
        {
            try
            {
                IPEndPoint anyIP = new IPEndPoint(IPAddress.Any, 0);
                byte[] data = broadcastClient.Receive(ref anyIP);
                string message = Encoding.UTF8.GetString(data);

                if (message == "ServerHere")
                {
                    lock (this)
                    {
                        if (IPAddress.TryParse(anyIP.Address.ToString(), out IPAddress parsedIp))
                        {
                            serverIP = parsedIp.ToString();
                            serverEndpoint = new IPEndPoint(parsedIp, communicationPort);

                            RunOnMainThread(() => {
                                statusText.text = "Found server: " + serverIP;
                            });
                            Debug.Log("Server IP found: " + serverIP);
                            InitializeCommunication();
                        }
                        else
                        {
                            Debug.LogError("Invalid server IP address");
                        }
                    }
                }
            }
            catch (System.Exception e)
            {
                Debug.LogError("Broadcast receive error: " + e.Message);
            }
        }
    }

    void InitializeCommunication()
    {
        try
        {
            commClient = new UdpClient(new IPEndPoint(IPAddress.Any, communicationPort + 1));
            receiveThread = new Thread(ReceiveServerMessages);
            receiveThread.IsBackground = true;
            receiveThread.Start();

            RunOnMainThread(() => {
                statusText.text = "Connected to server";
            });

            SendMessageToServer("Client connected");
        }
        catch (System.Exception e)
        {
            Debug.LogError("Communication initialization error: " + e.Message);
            RunOnMainThread(() => {
                statusText.text = "Failed to connect";
            });
        }
    }

    void ReceiveServerMessages()
    {
        while (isRunning && serverEndpoint != null)
        {
            try
            {
                IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
                byte[] data = commClient.Receive(ref sender);

                if (sender.Address.ToString() == serverEndpoint.Address.ToString() &&
                    sender.Port == serverEndpoint.Port)
                {
                    string message = Encoding.UTF8.GetString(data);

                    if (message == "Heartbeat")
                    {
                        // 将时间更新操作放到主线程
                        RunOnMainThread(() => {
                            lastHeartbeatTime = Time.time;
                        });
                        SendMessageToServer("HeartbeatAck");
                        Debug.Log("Received server heartbeat");
                    }
                    else if (message == "HeartbeatAck")
                    {
                        // 将时间更新操作放到主线程
                        RunOnMainThread(() => {
                            lastHeartbeatTime = Time.time;
                        });
                        Debug.Log("Received heartbeat ack");
                    }
                    else
                    {
                        Debug.Log("Received from server: " + message);
                        string msg = message;
                        RunOnMainThread(() => {
                            statusText.text = "Server: " + msg;
                        });
                    }
                }
                else
                {
                    Debug.LogWarning("Received message from unknown source: " + sender.Address);
                }
            }
            catch (SocketException e)
            {
                if (e.SocketErrorCode == SocketError.Interrupted)
                {
                    Debug.Log("Receive thread interrupted");
                }
                else
                {
                    Debug.LogError("Communication socket error: " + e.Message + " (Code: " + e.SocketErrorCode + ")");
                }
            }
            catch (System.Exception e)
            {
                Debug.LogError("Communication receive error: " + e.Message);
            }
        }
    }

    public void SendMessageToServer(string message)
    {
        if (serverEndpoint == null || commClient == null)
        {
            Debug.LogWarning("No server connected");
            return;
        }

        try
        {
            byte[] data = Encoding.UTF8.GetBytes(message);
            commClient.Send(data, data.Length, serverEndpoint);
        }
        catch (System.Exception e)
        {
            Debug.LogError("Send to server error: " + e.Message);
        }
    }

    public void OnSendButtonClick()
    {
        if (!string.IsNullOrEmpty(messageInput.text))
        {
            SendMessageToServer(messageInput.text);
            messageInput.text = "";
        }
    }

    void Update()
    {
        // 处理主线程队列
        while (mainThreadActions.Count > 0)
        {
            System.Action action;
            lock (mainThreadActions)
            {
                action = mainThreadActions.Dequeue();
            }
            action.Invoke();
        }

        if (serverIP != null)
        {
            // 检测服务器超时
            if (Time.time - lastHeartbeatTime > timeoutThreshold)
            {
                RunOnMainThread(() => {
                    statusText.text = "Server disconnected";
                });
                Debug.LogWarning("Server timeout");
            }
            // 发送心跳
            else if (Time.time - lastHeartbeatTime > heartbeatInterval)
            {
                SendMessageToServer("Heartbeat");
                lastHeartbeatTime = Time.time;
            }
        }

        if (Input.GetKeyDown(KeyCode.Tab))
            SendMessageToServer("我是客户端");
    }

    void OnDestroy()
    {
        isRunning = false;

        if (broadcastThread != null && broadcastThread.IsAlive)
        {
            broadcastThread.Interrupt();
        }
        if (receiveThread != null && receiveThread.IsAlive)
        {
            receiveThread.Interrupt();
        }

        broadcastClient?.Close();
        commClient?.Close();
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值