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.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)既是客戶機也是服務器 永久在線 間歇接入網絡 節點間…