UDP可靠傳輸那些事


有空來論壇走走,發現討論udp可靠傳輸又熱了起來,有人認為udp高效率,有人認為udp丟包重傳機制容易控制,還有朋友搞極限測試,當然也有人推銷自己的東西,這里寫一點我個人的看法。

  udp可靠傳輸其實非常非常的簡單,我最開始接觸udp可靠傳輸大約是在2005年,因為那時候開發FtpAnywhere,由於路由的映射和網關nat處理方面,認為udp具有天生優勢,因此開始編寫自己的udp可靠傳輸協議,好象那個時候已經有了udt,我也下了源代碼看了下,不過很快就看不下去了,因為它用了定時器,加上跨平台處理,導致它的代碼,反正我看着很亂,理不出一個完整的邏輯圖。但是原理和tcp的基本一樣,並沒有什么特殊的。后來我寫了我自己的第一個udp可靠傳輸類,QTUdp,這個是最簡單的,也就是和tcp一樣,實現2點之間的傳輸,並不是我現在寫的多點之間無中心p2p傳輸,效率很高,但是當時的編碼我還沒有養成現在的習慣,用了大量的DWORD之類的數據定義,包括包的定義,從現在角度看,不及格,不過至少它可以實用,並整合到了FtpAnywhere軟件中,在調試和運行過程中,慢慢的統計和發現udp包的傳輸中,最影響性能的部分以及其他一些細節,后來的ut 1.x多點傳輸協議就是在QTUdp基礎上開發的,在到現在 ut 2.x , phoenix 2.x,實現了幾個跨越。

  請相信,可靠udp傳輸從來都不是高效率可靠傳輸的代名詞,影響傳輸效率的最重要因素在於,sendto函數,每次只能投遞一個mtu長度的包,頻繁的系統調用極大的影響了極限性能,也許你會說,udp默認可以達到64KB,你可以投遞大包,是的,可以投遞,但是由於網路上mtu設備的限制,大包會被拆成小包,如果你定義一個包大於mtu,那么當其中任何一個小包發生丟包的時候,會導致整個包需要重傳,這個開銷非常巨大,特別是在Internet上,而采用mtu大小限制內的包進行傳輸,丟失一個,只需要重傳一個,開銷小的多。udp可靠傳輸的自定義校驗是另外一個限制,為了避免偽造的udp包,我們需要在我們自己的可靠udp包中加入自定義的校驗,這個校驗方法也直接影響到性能,最快的是直接套用crc32校驗,由於目前cpu指令集對這個計算進行了優化,因此它的計算速度幾乎是最快的,但是代價是,人家要破解或者偽造你的udp包也很容易,因為算法是透明的,以前暴出tcp偽造漏洞也是這樣,由於它的包組成是透明公開,唯一有保密性的是序列號,結果有些系統初始序列號存在規律,結果就導致了安全問題。最后,發送和接收,由於是在應用層進行[無論你是采用api epoll select overlap io,最終的執行都是在應用層],這個發送-確認過程中,由於應用層不是象tcp/ip協議棧那樣在內核態運行,因此可能有延遲,不過,目前的cpu核心數量和頻率,這個影響在工業應用[internet 等]已經幾乎可以忽略,唯一產生影響是在進行本機極限性能測試中。TCP的效率要高過udp可靠傳輸,因為它的send函數,幾乎每次都可以拷貝幾十KB,當然你可以將緩沖調整的很大,例如幾百KB或者幾MB,但是默認情況下的幾十KB足夠了,想當於幾十次調用udp sendto , 而tcp只需要調用一次,其次,tcp的包處理是在內核態進行,確認也是內核態,這就足以與應用層的udp可靠傳輸拉開距離,更別說硬件層的優化了. 那么,你可能會認為,為什么書上說udp性能好呢?其實這是針對不可靠udp傳輸,並且是內網 大包,例如

char buffs[32*1024];

memset(buffs,0,32*1024);

sendto(s , buffs,32*1024,.... 發射后不管,無論是否丟包

象這樣發包,效率才會超過tcp,不過,主要就是包頭大小的差距.

   使用udp可靠傳輸的目的是為了它的靈活性,在很多協議傳輸中,例如,遠程視頻流傳輸,jrtlib,通常分為關鍵幀和普通幀,關鍵幀的丟失,會導致普通幀失去作用,這個時候就需要使用udp的可靠傳輸+不可靠傳輸,實現方法是首先以可靠模式投遞關鍵幀,在關鍵幀處理完成后,剩余的時間用非可靠模式投遞普通幀,每隔指定時間重復這一流程. 如果使用tcp,這個效果就不好了,因為網絡帶寬就那么多,而且帶寬變化很大,把所有的關鍵幀和普通幀都進行可靠傳輸,可能導致不流暢,網絡阻塞等.

  關於udp的穿透能力,也就是傳說中的打洞,這根本是個偽命題,有人還在那里搞什么測試說打通了4種 nat 模型, 你認為可能嗎? 還有人用猜端口的方法進行打洞,我了個去,工業上能這么用? udp的穿透其實完全取決於路由[網關]的配置,國產的家用或者soho針對p2p進行過調整,但是你用cisco 等專業的大型設備進行打洞看看,為了保護用戶的安全,一般管理員都設置了高安全非透明,通常,就算是內部同一台電腦,同一個udp端口,發送給不同的目標ip數據包,路由都會隨機重新分配一個端口,打洞根本不可能成功的. 所以,如果你為了nat處理而使用udp,建議你還是放棄吧,直接使用tcp+upnp就行.

  關於udp可靠傳輸的性能,有人說使用epoll , overlap io 等,效率高於使用select模型, 我可以根據我的這幾年開發的經歷和測試結果告訴你,在校驗模式,投遞模式以及數據處理邏輯相同的情況下,多核心cpu平台下,本機效率差距不超過1%,而如果使用在Internet上,效率差距無限接近0,現在是cpu過剩的年代,linux  windows的調度下,如果沒有內核態運算,那么就會從等待的線程中挑選一個,你不要以為從內核態切換到用戶態是不需要開銷的,這個開銷同樣很大,頻繁的調度同樣影響性能,雖然不算在你的代碼中而算在系統開銷中. 當然,如果你需要在linux系統中同時運行apache等web服務,那效率差距會比較明顯,因為進程和線程太多,得不到第一時間的調度.

 關於udp可靠傳輸下緩沖大小,說實話,我是第一次見識,最簡單的點對點 udp可靠傳輸,有人開了上百KB的緩沖,緩沖是個好東西,通常,緩沖越大,效率越高,因為一次投遞的包數量多,IO性能就高,但是,這在Internet上是不提倡的,這種實現我不知道是否經過嚴格的網絡丟包和負載測試,在有家用路由小帶寬上傳模式下,如果有多人或者多個網絡程序使用帶寬,這個延遲參數變動非常頻繁,不必要的重傳率會非常高,最簡單的測試方法,找個銅包鋁網線,使用1 1對應的非標准接頭法,一頭接電腦,一頭接路由,然后與Internet上遠端進行測試,估計這個丟包重傳的概率會高的嚇人.

  udp可靠傳輸比TCP的慢啟動好?我想不一定,還是我上面那個網線做測試,如果采用快速啟動,在丟包嚴重的網絡環境下,帶寬浪費太離譜了,我調整我自己的udp可靠傳輸啟動方法好多次,越來越覺得tcp的慢啟動是非常有道理的,雖然恢復的慢,但是,它因為錯誤重傳而產生帶寬的浪費是非常小的. 當然,如果考慮到性能,還是可以適當調整的快點.

 關於udp可靠傳輸的文件傳輸效率,這是個偽命題,這個極限是硬件本身造成的,首先,正常的udp可靠傳輸效率肯定高於普通硬盤的讀寫盤速度,即使用memory map 技術對文件執行加速,還是跟不上udp本身的速度,其次,文件傳輸協議會影響到傳輸效率,最快的模式是什么? 就是直接發送文件從開始到結尾,和流 [FTP數據連接 HTTP]一樣,這是最快的,但是通常,為了避免數據差錯,會對文件傳輸內容進行分塊傳輸,並加入校驗.這就影響到了傳輸效率. 你說一輛公共汽車是從起點到終點直達快?還是一站站的停過去快?很明顯,第一個假設脫離實際的.

關於udp可靠傳輸和cpu的關系,有嗎?肯定有,但是在普通應用下,基本沒影響,只有在追求極限,例如本機測試,2G+光釺網絡等,才會有明顯的影響,可問題是,如果你的服務器[電腦]接入的是2G+光釺,你這服務器得什么硬件配置? 在一般應用下,cpu和內存開銷以及錯誤的丟包重傳才是第一位的. 如果一個udp可靠傳輸,100mbps網絡下,必須要Intel piii 1Ghz以上,那在這個基礎上開發出來的應用可能比現在的電腦游戲還離譜了.  如果不是多對多[因為這個邏輯比普通點對點udp可靠傳輸復雜的太多],普通的點對點udp可靠傳輸, 給幾個以前的測試數據, QtUdp ,環境是Pentium M 1.7G[單核心], Ati xpress主板 768m ddr2 533[單通道] , 本機器模擬傳輸當時測試的數據大約在 78MB/s , MTU=1380 , 緩沖是 17 * MTU.  , CPU開銷大約是75%, 如果使用目前雙核心,估計可以翻倍. 你可能會覺得這個數字太低,但是請注意當時的硬件環境,這個速度已經超過udt好多好多了. 最基本的點對點udp可靠傳輸,標准crc32校驗,以目前的硬件環境, i74核心, DDR3 1600 , 加上我們的一種特殊包處理技術,本機器UDP小包[1400]可靠傳輸的極限大約在270MB/s,如果還要提升,估計只能和ms 的IIS一樣,編寫網絡驅動了,但是x86處理器和內存速度始終在提升,說不定明天主頻直接翻倍了...

 其實,我轉向udp可靠傳輸的一個非常重要原因,是IPV6取消了傳輸中ip層的數據校驗,這導致tcp層完全負擔起了數據校驗任務,TCP數據是否還象IPV4下那么可靠,需要加個問號了,雖然IP層本身的差錯率非常低,但是取消掉校驗,直接帶來的風險到底有多大,並沒有經過實踐的評估,靠ipv6實驗網絡得出的是理論結果,也許有一天會出現一種突破性的IPV6 TCP偽造技術. 而如果采用udp可靠傳輸,由於udp本身有校驗,加上我們自己設計的校驗和序列號,這個可靠程度完全超過了IPV4 下的TCP,更別提IPV6下的TCP了.

 udp可靠傳輸,其實非常簡單,只要你有tcp/ip的基礎,最簡單的點對點udp可靠傳輸是非常容易編寫的,無非就是封包,校驗,發送,確認,重新組包,接口可以模仿tcp的幾個函數,這其中的一個難點是,判斷是否需要重發,這需要根據以前包的確認時間來推導本包,如果你不希望那么復雜,也可以,經驗數字 [不適合網通到電信], 第一次的重傳時間 750 ms , 第二次是 1600 , 以后每次都是 2000 , 雖然不科學,但是用這個延遲數字,可以保證你足夠的傳輸效率[即使存在小概率丟包],也不會產生大量的重傳,當然最好的方法是從之前包的延遲來推導. 

 總之,udp可靠傳輸沒那么復雜和神秘,它非常簡單,而且,它的效率也沒有各位想象中的那么高,可能會讓各位失望,但是,它很可愛,你可以隨意的塑造你自己的包,實現各種擴展,其中的取舍完全在於聰明的你.

 


免責聲明!

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



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