UdpClient的Connect究竟做了什么(轉)


最近在寫一個音頻通信的系統,因為需要還要處理其他事件,所以就自己設計底層的通信協議,用了不少底層的Socket編程(.Net Framework),搞清楚了不少細節問題。

先做一些鋪墊工作。音頻系統服務器需要給所有的客戶端發送音頻。服務器端要記錄下連接的客戶端的IPEndPoint(也就是IP+端口號),然后會對所有連接的客戶端群發。因為客戶端很可能是在NAT后的,所以不可能直接用向某個地址的Udp客戶端發送連接。所以客戶端需要把第一條消息發送給服務器端,NAT服務器就會打開一個口,允許服務器端向這個客戶端發送包(這就是最簡單的所謂的“Udp打洞”技術)。當然這個端口不會一直保留,一般不用的話很快就會被關閉。不過傳輸實時音頻一般都會連續使用的,所以不用太擔心這個問題。

所以客戶端先要向服務器發送一條消息,服務器端看到這是要求接受的消息后,就會把獲得的IPEndPoint加到群發列表中。服務器端的監聽UdpClient要Bind到一個端口,它只需要考慮接受消息。另一個UdpClient來發送消息,而這個不指定發送目標也沒有綁定到端口的UdpClient,需要在發送時指定消息目標,或者用Connect方法,來指定某個默認目標。服務器端因為要向不同的目標發送,所以不用Connect。

但是客戶端的UdpClient卻需要首先作為發送端,然后再作為接受端。因為在發起第一個發送的時候,操作系統會自動選取一個端口號,因此我們就希望客戶端能在此監聽。但是客戶端發送時只跟服務器的某個IPEndPoint通信,所以一開始我就用Connect連接到服務器的IPEndPoint。幾乎所有的文檔都說,Connect基本不做什么事情,它只是設置Send的默認接收端,免去每次發送都指定接收端的麻煩。但是Connect其實還做了一件事,導致客戶端接收不到服務器發送過來的消息。是什么呢?

因為Connect把該UdpClient所能接受的消息來源限制為所連接的接受端。但是服務器端的發送卻是另一個UdpClient執行的,它的端口號是由系統隨機分配的,而不是監聽消息的UdpClient。所以客戶端的UdpClient就不能接受到這個消息。所以這種需要連接一個UdpClient,卻需要接受另一個UdpClient消息的情況,就不能使用Connect了。解決的辦法是直接使用SendTo(Socket方法)或者UdpClient指定目的的Send方法的重載。

但是能夠用同一個正在Receive的UdpClient同時發送數據嗎?按說應該可以,但是沒有試驗過,有經驗的大牛直接告訴我得了。而且對於使用了線程的服務器來說(使用.Net的異步編程模型潛在使用了線程池),用一個UdpClient來做所有的工作,總是擔心會出現並發訪問問題,或者出現並發導致的效率損失。所以為了保險起見,還是各做各的事比較好。

如果在局域網,或者以后大家都用IPv6,就沒有這些復雜的問題,直接客戶端開一個端口監聽就行了。服務器只要知道IP,就可以向默認的端口發送消息。多么美好的景象!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM