当前位置: 首页 > news >正文

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();}
}

http://www.dtcms.com/a/315110.html

相关文章:

  • 【推荐100个unity插件】Unity 的 Hot Reload 热重载实现,加快unity程序编译速度——FastScriptReload插件
  • MySQL InnoDB 表数据结构存储方式详解
  • pathspec ‘with_def_layout‘ did not match any file(s) known to git`
  • Vue 详情header组件
  • Go语言Context
  • ISO(感光度)的工作原理
  • 接口权限(@SaCheckPermission)
  • ebaz4205矿板以太网连接不稳定问题解决方案
  • SQL基础语法(四个分类、库和表的增删改)
  • 【笔记】ROS1|6 中间人攻击移动过程【旧文转载】
  • 私有化部署即时通讯,企业专属通讯系统BeeWorks
  • 计算机网络:网络号和网络位是不是同一个意思
  • 4.5 点云表达方式——图
  • 纯前端使用ExcelJS插件导出Excel
  • 并发编程常用工具类(上):CountDownLatch 与 Semaphore 的协作应用
  • C++信息学奥赛一本通-第一部分-基础一-第一章
  • 高并发抢单系统核心实现详解:Redisson分布式锁实战
  • Swin-Transformer从浅入深详解
  • ubuntu 20.04 C和C++的标准头文件都放在哪个目录?
  • 安卓逆向(基础①-Google Pixel-Root)
  • <PhotoShop><JavaScript><脚本>基于JavaScript,利用脚本实现PS软件批量替换图片,并转换为智能对象?
  • 【拓扑序 时间倒流法】P7077 [CSP-S2020] 函数调用|省选-
  • 嵌入式开发入门——电子元器件~电容
  • RLCraft开服踩坑记录
  • 防火墙web页面练习
  • 使用AWS for PHP SDK实现Minio文件上传
  • Centos7离线安装Mysql8.0版本
  • 政务云数智化转型:灵雀云打造核心技术支撑能力
  • HarmonyOS 多屏适配最佳实践:基于 ArkUI 的响应式 UI 方案
  • 在CentOS 7上安装配置MySQL 8.0完整指南