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

发现后自动建立 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.pswp.cn/pingmian/92003.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/92003.shtml
英文地址,请注明出处:http://en.pswp.cn/pingmian/92003.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++_389_定义一个禁用了赋值操作、具有线程同步资源保护的结构体,作为一些回调函数的参数,方便获取响应操作的结果等信息

/* 回调参数。注意:此结构体禁用了赋值,会编译报错 */struct API_CALLBACK_T{public:API_CALLBACK_T(){eRet = e_fail;bWait = true;

uniapp基础 (一)

目录 UniApp 是什么&#xff1f;有什么优势 跨平台高效开发 Vue.js 技术生态 插件生态丰富 渐进式开发支持 UniApp 跨平台兼容的实现原理 编译时转 运行时适配层 条件编译 性能优化策略 1.预编译模 2.原生组件混合渲 3.分包加载 UniApp 的生命周期钩子有哪些&#x…

【图像算法 - 10】进阶入门:改进 YOLO11 安全帽检测的关键参数与场景适配

一、项目背景与需求 视频全文介绍 【图像算法 - 10】进阶入门&#xff1a;改进 YOLO11 安全帽检测的关键参数与场景适配今天我们使用深度学习来训练一个安全帽检测系统&#xff0c;基于YOLO11的安全帽检测系统。我们使用了两万张图片的数据集训练了这次的基于YOLO11的安全帽检…

【C 学习】04.1-类型转换浮点数

“知道做不到就是不知道”一、类型转换1.自动类型转换&#xff1a;当运算符&#xff08;常见、-、*、/、%&#xff09;两边出现不一致的类型时&#xff0c;编译器会自动转换成较大的&#xff08;范围更大&#xff09;类型。从小到大&#xff1a;char-short-int-long-long long;…

基于反事实对比学习的鲁棒图像表征|文献速递-医学影像算法文献分享

Title题目Robust image representations with counterfactual contrastive learning基于反事实对比学习的鲁棒图像表征01文献速递介绍医学影像中的对比学习已成为利用未标记数据的有效策略。这种自监督学习方法已被证明能显著提升模型跨领域偏移的泛化能力&#xff0c;并减少训…

机器学习(5):朴素贝叶斯分类算法

贝叶斯的核心思想就是&#xff0c;谁的概率高就归为哪一类。贝叶斯推论P(A):先验概率。即在B事件发生之前&#xff0c;我们对A事件概率的一个判断。P(A|B)&#xff1a;后验概率。即在B事件发生之后&#xff0c;我们对A事件概率的重新评估。P(B|A)/P(B)&#xff1a;可能性函数。…

Docker 容器内进行 frp 内网穿透

开始之前需要有一台可以进行公网访问的服务器 下载安装 frp 这个直接到 github 官网就可以下载了 点击Releases 就可以查看到可以下载的源&#xff0c;根据自己电脑的型号进行选择就好了。 linux服务器上下载 如果是在linux的服务器上的话可以直接通过wget进行下载 例如&a…

复制网页文字到Word、WPS文字?选中后直接拖放

要把网页、PDF或其他应用中的文字内容复制到Word、WPS文字、记事本等&#xff0c;不一定要先复制、再粘贴&#xff0c;也可以选中文字后直接拖动到目标位置即可。多次操作&#xff0c;可以把窗口并排再拖动。如果你经常需要在不同应用之间引用文字&#xff0c;不妨试一试。操作…

Starrocks中的 Query Profile以及explain analyze及trace命令中的区别

背景 本文基于Starrocks 3.5.5 现有公司因为业务的不同&#xff0c;可能会更加关系单个SQL 的RT&#xff0c;因为如果一个SQL的RT比较大的话&#xff0c;影响的就是这个业务&#xff0c;从而影响收入&#xff0c;所以对于这方面我们就比较关心&#xff0c; 而最近在基于Starro…

网络 —— 笔记本(主机)、主机虚拟机(Windows、Ubuntu)、手机(笔记本热点),三者进行相互ping通

背景介绍最近在笔记本电脑上的虚拟机(Ubuntu、Windows Server搭配)上部署了"WD"开源手游服务器(旧版本)&#xff0c;手机连接上了笔记本电脑开启的WIFI热点&#xff0c;同时手机上安装了"WD"手游客户端。于是首先得保证网络相互畅通才能玩游戏&#xff0c;…

裸露土堆识别准确率↑32%:陌讯多模态融合算法在生态监测的实战解析

原创声明本文为原创技术解析文章&#xff0c;涉及技术参数及架构描述均参考《陌讯技术白皮书》&#xff0c;禁止任何形式的转载与抄袭。一、行业痛点&#xff1a;裸露土堆识别的现实挑战在生态环境保护、建筑工地监管等场景中&#xff0c;裸露土堆的精准识别是遏制扬尘污染、防…

网站从HTTP升级到HTTPS网址方法

将网站从HTTP升级到HTTPS涉及几个关键步骤&#xff0c;以确保安全连接以及用户和搜索引擎的平稳过渡。获取并安装SSL/TLS证书&#xff1a;1、从CA机构授权提供商Gworg获取SSL/TLS证书。选项包括域名验证(DV)、组织验证(OV)和扩展验证(EV)证书&#xff0c;验证严格度各不相同&am…

WaitForSingleObject 函数参数影响及信号处理分析

一、第二个参数&#xff08;超时时间&#xff09;的影响 DWORD result WaitForSingleObject(hHandle, 1000);中的第二个参数1000表示等待超时时间为1000毫秒&#xff08;1秒&#xff09;&#xff0c;其核心影响如下&#xff1a; 1. 函数行为控制 立即返回&#xff1a;若对象已…

dbeaver导入数据及配置讲解

导入数据教程&#xff1a; 前提.csv文件&#xff1a;且只能导入一个sheet点击下一步选中导入的.csv文件对应好数据字段和表字段&#xff0c;感觉不需要导入的可以skip配置一下&#xff0c;下面有介绍&#xff1a;以下为你详细解析这些数据加载相关功能的含义与作用&#xff1a;…

JAVA学习笔记 自增与自减的使用-006

目录 1 基本概述 2 自增与自减的用法 2.1单独使用 2.2 参与运算 3 思考与练习 3.1 基础题 3.2 中等题 3.3 进阶题 4 总结 源计划&#xff1a;我从来不认为自己的成功过程有多心酸&#xff0c;只是心中不惧失败&#xff0c;能够承受别人不能接受的失望而已&#xff01;…

从LCM到SomeIP,再到DDS:技术演进与工作原理剖析

文章目录一、LCM&#xff1a;轻量级通信与编组库工作原理C 代码示例局限性二、SomeIP&#xff1a;面向服务的可扩展中间件工作原理C 代码示例优势与特点三、DDS&#xff1a;数据分发服务工作原理C 代码示例优势与应用场景四、技术演进总结在分布式系统通信领域&#xff0c;技术…

Redis里面什么是sdshdr,可以详细介绍一下吗?

文章目录为什么 Redis 不直接使用 C 语言的字符串&#xff1f;sdshdr 的结构sdshdr 的不同类型sdshdr 带来的优势总结我们来详细解析一下 Redis 的核心数据结构之一&#xff1a; sdshdr。sdshdr 是 “Simple Dynamic String header” 的缩写&#xff0c;意为“简单动态字符串头…

RocketMq如何保证消息的顺序性

文章目录1.顺序消息的全流程1.1 发送阶段&#xff1a;消息分区1.2.存储阶段&#xff1a;顺序写入1.3.消费阶段&#xff1a;串行消费2.第三把锁有什么用?3.顺序消费存在的问题和Kafka只支持同一个Partition内消息的顺序性一样&#xff0c;RocketMQ中也提供了基于队列(分区)的顺…

zabbix平台无法删除已停用主机的处理案例

在zabbix平台上删除已停用的主机&#xff0c;提示“SQL描述式执行已失败: "DELETE FROM items WHERE (itemid IN &#xff08;.....)”&#xff0c;无法删除&#xff0c;本文为处理情况。一、问题现象在zabbix平台上删除已停用的主机&#xff0c;提示“SQL描述式执行已失败…

【计算机网络】6应用层

1.网络应用模型 特性 客户/服务器模型(Client-Server, C/S) 对等模型(Peer-to-Peer, P2P) 中心化 是(依赖服务器) 否(去中心化) 角色特点 服务器 客户机 无中心服务器 提供计算服务 请求计算服务 每个节点(Peer)既是客户机也是服务器 永久在线 间歇接入网络 节点间…