Unity udp通信詳解

在Unity中實現UDP通信,需要使用C#的System.Net和System.Net.Sockets命名空間。UDP(用戶數據報協議)是一種無連接的網絡協議,它允許數據包在網絡上發送和接收,但不保證數據包的到達順序、完整性或可靠性。這使得UDP非常適合那些對實時性要求高的應用,如在線游戲和實時通信。

以下是在Unity中實現UDP發送和接收的基本步驟:

  1. 創建UDP客戶端

首先,你需要創建一個UDP客戶端來發送和接收數據。

using System.Net;
using System.Net.Sockets;
using System.Text;
using UnityEngine;public class UDPClient : MonoBehaviour
{private UdpClient udpClient;private IPEndPoint remoteEndPoint;void Start(){int port = 9876; // 選擇一個端口udpClient = new UdpClient();remoteEndPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), port); // 目標IP和端口}
}
  1. 發送數據

你可以使用UdpClient.Send方法來發送數據。

public void Send(string message)
{byte[] data = Encoding.UTF8.GetBytes(message);udpClient.Send(data, data.Length, remoteEndPoint);
}
  1. 接收數據

接收數據稍微復雜一些,因為你通常需要在一個單獨的線程或協程中進行監聽,以避免阻塞主線程。

void Start()
{// 初始化UDP客戶端udpClient = new UdpClient(9876); // 監聽的端口StartReceiving();
}private void StartReceiving()
{udpClient.BeginReceive(ReceiveCallback, null);
}private void ReceiveCallback(IAsyncResult ar)
{IPEndPoint remoteIpEndPoint = new IPEndPoint(IPAddress.Any, 0);byte[] receivedBytes = udpClient.EndReceive(ar, ref remoteIpEndPoint);string receivedText = Encoding.UTF8.GetString(receivedBytes);Debug.Log("Received: " + receivedText);// 繼續監聽StartReceiving();
}
  1. 關閉UDP客戶端

當你完成UDP通信后,應該關閉UDP客戶端以釋放資源。

void OnDisable()
{udpClient.Close();
}

注意事項

在實際部署時,你需要處理網絡異常和錯誤。

考慮到UDP的不可靠性,你可能需要實現一些形式的錯誤檢測和糾正機制,或者在應用層面上處理丟包和重復包的問題。

如果你的應用需要廣播或多播功能,UDP是一個很好的選擇。

這就是在Unity中使用UDP進行基本通信的方法。根據你的具體需求,你可能需要對這些代碼進行調整和擴展。

UDP廣播,不指定ip

設置UDP客戶端以支持廣播

首先,你需要允許UdpClient發送廣播數據包。這可以通過設置EnableBroadcast屬性為true來實現。

void Start()
{udpClient = new UdpClient();udpClient.EnableBroadcast = true;remoteEndPoint = new IPEndPoint(IPAddress.Broadcast, 9876); // 使用廣播地址
}

發送廣播數據

發送函數不需要改變,你只需確保remoteEndPoint已經設置為廣播地址。

public void Send(string message)
{byte[] data = Encoding.UTF8.GetBytes(message);udpClient.Send(data, data.Length, remoteEndPoint);
}

接收廣播數據

在接收端,你不需要做任何特別的設置來接收廣播數據。只需監聽正確的端口即可。確保接收端的防火墻設置允許接收UDP數據。

void Start()
{udpClient = new UdpClient(9876); // 監聽廣播消息的端口StartReceiving();
}private void StartReceiving()
{udpClient.BeginReceive(ReceiveCallback, null);
}private void ReceiveCallback(IAsyncResult ar)
{IPEndPoint remoteIpEndPoint = new IPEndPoint(IPAddress.Any, 0);byte[] receivedBytes = udpClient.EndReceive(ar, ref remoteIpEndPoint);string receivedText = Encoding.UTF8.GetString(receivedBytes);Debug.Log("Received: " + receivedText);// 繼續監聽StartReceiving();
}

注意事項

使用廣播時,所有在同一網絡上監聽相應端口的設備都將接收到發送的數據包。這可能會導致網絡擁塞,特別是在大型網絡中。

廣播通常不會穿越路由器,因此它通常限于本地網絡。

確保網絡配置和設備的安全設置允許廣播通信。

通過這種方式,你可以在Unity中實現UDP廣播,使得一個客戶端可以向同一局域網內的所有設備發送數據,而無需指定特定的IP地址。

使用協程的方法

上面我使用的是異步方法BeginReceive和EndReceive來處理UDP數據的接收。這種方法不會阻塞主線程,因為它在底層使用了.NET的異步模式,但它并不是基于Unity的協程。

如果你想使用Unity的協程來接收UDP數據,你可以通過在協程中循環調用Receive方法來實現。這里是如何使用協程來接收UDP數據的一個示例:

使用協程接收UDP數據

首先,你需要設置一個UDP客戶端,并啟動一個協程。

using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using UnityEngine;public class UDPServer : MonoBehaviour
{private UdpClient udpClient;private bool isRunning = true;void Start(){udpClient = new UdpClient(9876); // 監聽的端口StartCoroutine(ReceiveData());}IEnumerator ReceiveData(){while (isRunning){if (udpClient.Available > 0){IPEndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);byte[] data = udpClient.Receive(ref remoteEndPoint); // 這將阻塞,直到數據到達string receivedText = Encoding.UTF8.GetString(data);Debug.Log("Received: " + receivedText);}yield return null; // 等待下一幀}}void OnDisable(){isRunning = false;udpClient.Close();}
}

注意事項

使用Receive方法在協程中接收數據時,需要注意這個方法是阻塞的。如果沒有數據可用,它將阻塞當前線程,直到數據到達。為了避免阻塞Unity的主線程,我在調用Receive之前檢查了udpClient.Available,以確保有數據可讀。

使用協程來接收數據時,你需要小心處理程序的退出和資源釋放,確保在組件被禁用或銷毀時正確關閉UDP客戶端并停止協程。

這種使用協程的方法適用于數據流量不是非常高的情況,因為每次調用Receive都會在沒有數據時阻塞。如果你期望高頻率的數據傳輸,使用異步方法可能更合適。

接收的數據量很高,并且不能堵塞主線程

如果你需要處理高數據量且不能阻塞主線程,最佳的方法是使用異步接收,而不是協程。異步接收可以有效地處理大量數據而不會影響Unity的主線程性能。這是因為異步操作在.NET的線程池中處理,不會干擾到Unity的渲染和游戲邏輯線程。

下面是一個使用BeginReceive和EndReceive方法實現的異步UDP數據接收示例,這種方法不會阻塞主線程:

using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using UnityEngine;public class UDPServer : MonoBehaviour
{private UdpClient udpClient;void Start(){udpClient = new UdpClient(9876); // 監聽的端口StartReceiving();}private void StartReceiving(){udpClient.BeginReceive(new AsyncCallback(ReceiveCallback), null);}private void ReceiveCallback(IAsyncResult ar){IPEndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);byte[] receivedBytes = udpClient.EndReceive(ar, ref remoteEndPoint);string receivedText = Encoding.UTF8.GetString(receivedBytes);// 處理接收到的數據Debug.Log("Received: " + receivedText);// 繼續接收更多數據StartReceiving();}void OnDisable(){udpClient.Close();}
}

解釋

異步接收啟動:在Start方法中,我們初始化UdpClient并調用StartReceiving方法來開始異步接收數據。

異步回調:BeginReceive方法啟動異步操作,并立即返回。當UDP客戶端接收到數據時,它會調用ReceiveCallback方法。

處理數據:在ReceiveCallback中,我們使用EndReceive方法來結束接收并獲取數據。然后,我們可以處理這些數據,例如將其轉換為字符串并打印到控制臺。

繼續接收:每次接收完數據后,我們需要再次調用StartReceiving來繼續監聽更多的數據。

資源清理:在OnDisable方法中,我們確保關閉UdpClient以釋放網絡資源。

優點

非阻塞:使用異步方法不會阻塞Unity的主線線程,從而保持游戲性能和流暢度。

高效:適合高數據量的接收,因為它利用了.NET的線程池,不會占用主線程資源。

注意事項

線程安全:由于ReceiveCallback可能在不同的線程上執行,如果你需要更新Unity的UI或調用某些Unity API,你可能需要使用UnityMainThreadDispatcher或類似的工具來確保在主線程上執行這些操作。

錯誤處理:在生產環境中,你應該添加錯誤處理邏輯來處理網絡錯誤和異常情況。

new IPEndPoint(IPAddress.Any, 0);詳解

在使用 IPEndPoint 類時,第一個參數是IP地址,第二個參數是端口號。當你在接收數據時創建一個 IPEndPoint 對象并使用 IPAddress.Any 和端口號 0,這里的設置有特殊的意義:

IPAddress.Any 表示服務器將接受發往任何本地地址的數據包。這是在設置服務器偵聽時常用的,意味著服務器不限制數據包必須發送到特定的IP地址。

端口號 0 在這個上下文中用于接收操作時并不是指定服務器監聽的端口號。實際上,當使用 UdpClient 的 BeginReceive 方法時,你已經在 UdpClient 的構造函數中或通過其 Client.Bind 方法指定了監聽端口(在本例中是9876)。端口號 0 在創建 IPEndPoint 用于接收數據時,實際上是一個占位符,表示在這一步操作中不需要指定端口號,因為 UdpClient 已經綁定到了一個具體的端口。

因此,當你看到這樣的代碼:

IPEndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);

這里的 remoteEndPoint 用于 EndReceive 方法,它的作用是獲取發送方的IP地址和端口號。IPAddress.Any 和 0 在這里不是用來限制接收數據的地址或端口,而是用來正確地初始化 IPEndPoint 對象,以便 EndReceive 能填充這個對象,告訴你從哪個IP地址和端口號接收到了數據。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/40165.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/40165.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/40165.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

沃德校園助手丨校園跑腿-校園外賣-校園論壇三合一系統

校園跑腿項目其實由來已久,由于大學校園生活的特殊性,除了日常的課程學習之外,大學生的所有生活基本長期處于全模式形態下的校園封閉環境中,再加之當前大學生一部分學業繁忙,辦事不便。另一部分自理能力較差&#xff0…

【kafka】可視化工具cmak(原kafka-manager)安裝問題解決

眾所周知(反正不管你知不知道),kafka-maneger更名了,現在叫cmak!原因是什么呢?據不可靠小道信息說,原kafka-manager這個名字涉及到kafka商標使用問題,應該是被律師函警告了&#xff…

如何批量創建、提取和重命名文件夾!!!

你是否還在一個一個手動創建文件名! 你是否還在一個一個手動提取文件名! 你是否還在一個一個手動修改文件名! 請隨小生一起批量自動創建、提取、重命名! 1、批量創建文件夾 【案例】創建1日-31日共31個文件夾 【第一步】在A列…

Android SurfaceFlinger——動畫播放流程(十六)

前兩篇文章介紹了系統啟動動畫服務的啟動和準備階段,并且我們選擇了自定義動畫的分支,該分支的動畫播放流程主要包含一下幾個階段: loadAnimation:解析 zip 包的動畫數據。playAnimation:播放解析好的紋理數據。releaseAnimation:播放完畢釋放資源。一、動畫播放流程 1、…

Gradle學習-5 發布二進制插件

注:以下示例基于Gradle8.0 1、發布插件 復制一分 buildSrc,執行命令行,生成一個新目錄 leon-gradle-plugin cp -rf buildSrc leon-gradle-plugin在 leon-gradle-plugin 目錄下的 build.gradle 中引入maven plugins{// 引用 Groovy 插件&…

(五十二)第 8 章 動態存儲管理(邊界標識法)

1. 背景說明 2. 示例代碼 1) errorRecord.h // 記錄錯誤宏定義頭文件#ifndef ERROR_RECORD_H #define ERROR_RECORD_H#include <stdio.h> #include <string.h> #include <stdint.h>// 從文件路徑中提取文件名 #define FILE_NAME(X) strrchr(X, \\) ? strr…

Linux環境下的字節對齊現象

在Linux環境下&#xff0c;字節對齊是指數據在內存中的存儲方式。字節對齊是為了提高內存訪問的效率和性能。 在Linux中&#xff0c;默認情況下&#xff0c;結構體和數組的成員會進行字節對齊。具體的對齊方式可以通過編譯器選項來控制。 在使用C語言編寫程序時&#xff0c;可…

【Linux】線程——線程的概念、線程的特點、線程的優點和缺點、線程和進程、線程函數的使用

文章目錄 Linux線程1. 線程的概念1.1 什么是線程 2. 線程的特點2.1 線程的優點2.2 線程的缺點2.4 線程和進程 3. 線程函數的使用pthread_create() 創建線程pthread_self() 獲取線程IDpthread_exit() 線程終止pthread_cancel() 線程取消pthread_join() 線程等待pthread_detach()…

【第14章】MyBatis-Plus批量操作

文章目錄 前言一、功能概覽二、類結構說明1.MybatisBatch<?>2.MybatisBatch.Method<?>3. BatchMethod<?>4.使用步驟5.返回值說明 三、使用示例1. execute方法2. 示例一&#xff1a;實體類型數據3. 示例二&#xff1a;非實體類型數據4. 示例三&#xff1a;…

茗鶴 | 如何借助APS高級計劃排程系統提高汽車整車制造的效率

在我們做了詳盡的市場調研及頭部汽車制造企業排程需求溝通后&#xff0c;我們發現盡管企業有很多的業務系統做支撐&#xff0c;在計劃排程領域&#xff0c;所有的汽車制造總裝廠仍舊使用人工“Excel”做排產規劃&#xff0c;其中少部分也會借助MRP、第三方輔助排產工具。鑒于我…

ChatGPT:Java中的對象引用實現方式

ChatGPT&#xff1a;Java中的對象引用實現方式 如果使用句柄的話&#xff0c;那么 Java 堆中將會劃分出一塊內存來作為句柄池&#xff0c;reference 中存儲的就是對象的句柄地址&#xff0c;而句柄中包含了對象實例數據與對象類型數據各自的具體地址信息。 你提到的句柄機制是…

JVM原理(十一):JVM虛擬機六種必需對類進行初始化的情況

Java虛擬機把描述類的數據從Class文件加載到內存&#xff0c;并對數據進行校驗、轉換解析和初始化&#xff0c;最終形成可以被虛擬機直接使用的Java類型&#xff0c;這個過程被稱作虛擬機的類加載機制。Java天生可以動態擴展的語言特性就是依賴運行期間動態加載和動態鏈接這個特…

104.二叉樹的最大深度

給定一個二叉樹 root &#xff0c;返回其最大深度。 二叉樹的 最大深度 是指從根節點到最遠葉子節點的最長路徑上的節點數。 示例 1&#xff1a; 輸入&#xff1a;root [3,9,20,null,null,15,7] 輸出&#xff1a;3 示例 2&#xff1a; 輸入&#xff1a;root [1,null,2] 輸出…

相機參數與圖像處理技術解析

01. 相機內參和外參的含義&#xff1f;如果將圖像放大兩倍&#xff0c;內外參如何變化&#xff1f; 相機有兩個最基礎的數據&#xff1a;內參(Instrinsics)和外參(Extrinsics)&#xff0c;內參主要描述的是相機的CCD/CMOS感光片尺寸/分辨率以及光學鏡頭的系數&#xff0c;外參主…

每日兩題 / 20. 有效的括號 155. 最小棧(LeetCode熱題100)

20. 有效的括號 - 力扣&#xff08;LeetCode&#xff09; 遇到左括號入棧 遇到右括號判斷棧頂是否為匹配的左括號 最后判斷棧是否為空 func isValid(s string) bool {var stk []runefor _, value : range s {if value ( || value { || value [ {stk append(stk, value)}…

阿里巴巴圖標庫iconfont的使用方式

文章目錄 什么是 iconfong創建一個自己的項目如何使用Unicode 使用方法Font class 使用方式Symbol 使用方式還有一種使用方式 在線鏈接&#xff08;不推薦&#xff0c;但可用于測試&#xff09; 什么是 iconfong Iconfont 是一種圖標字體服務。它將各種圖標設計轉換為字體格式…

數據庫的約束與索引

數據庫的約束與索引 文章目錄 數據庫的約束與索引一、約束1、定義2、主鍵索引3、唯一約束4、非空約束5、外鍵約束 二、索引1、定義2、主鍵索引3、唯一索引4、普通索引5、全文索引 三、深入索引面試題&#xff08;一&#xff09;面試題&#xff08;二&#xff09;面試題&#xf…

【設計模式】行為型-狀態模式

在變幻的時光中&#xff0c;狀態如詩篇般細膩流轉。 文章目錄 一、可調節的燈光二、狀態模式三、狀態模式的核心組件四、運用狀態模式五、狀態模式的應用場景六、小結推薦閱讀 一、可調節的燈光 場景假設&#xff1a;我們有一個電燈&#xff0c;它可以被打開和關閉。用戶可以…

snap和apt的區別簡單了解

Linux中沒有tree命令的時候提示安裝的時候出現了兩個命令&#xff0c;簡單看了看兩者有何區別&#xff08;一般用apt就可以了&#xff09;&#xff1a; sudo snap install tree 和 sudo apt install tree 這兩個命令都是用來安裝 tree 命令行工具的&#xff0c;但它們使用的是不…

在線教育平臺,easyexcel使用案例

控制器 因為如何想要在讀數據的時候操作數據庫&#xff0c;就必須使用構造方法傳dao 或者service&#xff0c;因為這個不歸spring管理&#xff0c;不能自動注入&#xff0c;所以參數里需要傳遞service 或者 dao AutowiredIEduSubjectService subjectService;添加課程分類的方法…