什么時候選擇TCP,什么時候選擇UDP?


在接到網絡軟件開發項目的時候,首先要考慮到的一個大問題就是,究竟應該使用tcp還是udp,或者是采用混合的做連接來實現, 這是在搭建軟件整體框架的時候考慮的最多的地方,也是最困難的選擇,每一個新手基本都會在這里栽跟頭,吃苦頭,然后才能慢慢的成熟.

  什么時候選擇tcp,什么時候選擇udp,什么時候采用多連接,什么時候采用混合連接?霧里看花,頭暈了吧,這么簡單的選擇還有這么多學問?無論是經典的Unix網絡編程,還是tcp/ip協議卷1-3,都只是籠統的描述了一下大概如何選擇,且不說對與錯,但是今天從我的角度看,這幾個經典的著作里,其實都是從協議本身特性來指導大家如何選擇,卻沒有考慮到tcp協議棧本身實現存在的各種局限, 網絡上有很多文章,什么大師,架構師之類的,往往是從cpu , 緩沖 , 多隊列網卡等硬件層,加上 Linux下的epoll,windows下的完成端口等系統調用,來侃侃而談如何優化web服務之類,什么從1萬連接到100萬, 說句老實話, 真想做優化, 別的不說, 光是選擇的層級我就覺得有問題,因為從1萬到100萬,造成性能瓶頸的主要方面除了上下文切換導致的開銷,其實最大的問題出在tcp/ip協議棧的實現上,不去研究優化tcp/ip協議棧的影響,卻抓最底層的硬件和操作系統的系統調用實現,無論如何我是不敢苟同,你把原來的p4 2.0g cpu換成新一點的如xeon e1230 平台, 內存從ddr 400 2g升級到 16g ddr3-1600 , 應對網絡的並發連接能力能提升一個等級,大把的錢投進去了,總能聽到個響吧.下面是我個人的一點基本總結.

  [1] tcp和udp的區別,簡單來說, tcp是有序可靠傳輸, 而udp是不可靠亂序傳輸,但是實際上你把udp看做IP可能更准確一點,因為可以在udp基礎上開發出可靠udp等各種傳輸.

  [2] tcp的優缺點是什么?優點,這是一個國際通行了很多年的標准實現,調用簡單,省心,很多應用都基於tcp進行實現的,最關鍵的是,它的兼容性是跨平台的,也就是,只要你選擇的是tcp,那么不管是windows還是linux,unix,只要支持tcp/ip,那么就可以保證實現可靠連接和傳輸.作為軟件的開發者只需要考慮應用層,比如應用協議等的實現就可以了. 至於可靠傳輸的性能,由於是國際標准,在內核層實現和運行,很多都做了硬件級的優化,因此對比起用udp等實現的可靠傳輸,本機[注意是本機]的極限傳輸性能要高很多.

     那么tcp有什么明顯的缺點呢? 對開發者來說最頭大的恐怕就是2條, 第一是每個tcp連接都是1對1連接,這意味着每個連接都需要一個套接字socket,並且需要隨時測試是否數據可讀可寫.當連接數量達到一定的程度,性能會直線下降. 沒有經歷過大規模並發應用開發的朋友可能很難想像,就一個基本沒有數據的空連接怎么會消耗系統的性能呢?其實這個問題,不可以單純從硬件或者api接口等進行解釋,而需要從tcp/ip協議棧的實現中去發現的,從可以查看到的代碼,Linux freebsd unix來看,無一列外,在操作系統里使用的是指針鏈表進行管理, 重要的事情說3遍 , 用的是指針鏈表 ,指針鏈表,指針鏈表 , 看下Linux 下的實現 [不是最新版,不過這個應該不會改變,因為會導致整個代碼重構] ,

ip層的接收 

for( qp=ipqueue; qp!=NULL; qplast=qp,qp=qp->next)

{

if(iph->id==qp->iph->id && .......

}

新版本4的測試版Linux核心tcp/ip實現, 和之前的比,是使用了rcu_函數來優化,其實換湯不換葯,在執行插入和寫操作的時候,需要執行rcu_writelock, 這和readlock不同,能且只有一個鎖定,而無法多重鎖定,性能在這里遇到了瓶頸, 這個瓶頸出現在tcp/ip協議棧的實現中,由於tcp包的構成問題,至少到目前,或者可以遇見的將來,tcp這個嚴重影響並發性能的問題會一直存在在實現中,這是絕大部分人都始終沒有搞明白,為什么tcp在面對海量並發的時候,在超過一定數字后性能出現直線下降的其中一個關鍵原因.   [我曾經嘗試過為Linux下Tcp/ip棧的指針鏈表引入排序等優化,但是經過各種測試,我發現事實上這個實現在大部分情況下性能比排序好,因為排序等反而導致了性能下降,因此除非有革命性的優化出現,目前這個瓶頸會一直存在.]

給出我們自己編寫的延遲測試結果 硬件是Intel i3 2350 [2.3g] ddr3 1333 , tcp 連接數量大並頻繁小包傳輸下,協議棧導致延遲非常明顯,

看出問題在那里了嗎?是個指針遍歷操作 , 獨占模式  ,一萬空連接在目前的cpu前,可能沒什么 ,但是如果是並發模式n萬小包呢, 那開銷n/2......,你再多的cpu核心也沒用,一個鏈表只有一個獨占模式的遍歷操作,其他cpu只能干着急。所以在面對小數據量海量長時間連接的應用時,選擇tcp必須要慎重再慎重. 第二個大問題是tcp是雙向流傳輸,而keepalive這個功能並非普遍支持, 雙向流傳輸意味着,數據是采用流模式過來的,如果切割取決於協議的制定者,例如普遍的采用\r\n作為分割字符, 而keepalive的非普遍實現,則導致另外一個問題,那就是每個連接,必須每過一定時間在應用協議層檢測連接是否還存活,最典型的就是ftp,如果沒有指令操作,那么客戶端每過一定時間必須發送一條指令,通常是noop指令給服務器端,告訴服務器,我還保持着連接,不要中斷. 可能有人無法理解,tcp不是可靠連接嗎? tcp是可靠連接,要命的時,當tcp建立連接后,如果雙方沒有應用層的數據傳輸,那么當物理中斷發生的時候,等待的一方是接收不到發生故障的一方的任何消息的,直到沒有發生故障的一方,主動發送數據給另一方,出現發送超時的時候,才能給出中斷判斷,否則就是個死等待,這就是ftp等協議中引入noop等類似機制的原因. 流傳輸的另一個問題是,無法實現數據的並發傳輸,而只等排隊發送,這在很多應用,特別是游戲類應用是嚴重的缺陷,無論你有多着急,一個連接就是一個流,你要排隊先發送到緩沖,然后由系統負責發送緩沖數據,可能有人說可以使用緊急指針帶外數據,但這玩意不是讓你用來傳輸數據的,其實是讓你用來發送緊急通知用的,在tcp中使用帶外數據,除了帶來更復雜的實現,沒有什么實質性作用,以ftp為例子,實際上無論你使用使用緊急指針帶外數據,都可以中斷數據傳輸的,這個緊急指針在那里除了保持兼容性,沒有實際作用.

[3] udp的優缺點是什么? udp是比tcp更接近IP的協議, 通常udp是不可靠傳輸,但是我們可以在應用層對udp加上校驗和序列號,做成可靠傳輸,這一點不可不知.  udp的優點是什么? 書上總是說udp的性能比tcp好,有沒有人想過為什么? 其實原因很簡單, udp是發射后不管, 不需要對方發送ack包進行確認, tcp由於需要對每一個包進行ack確認,一來一回,就會影響到傳輸效率,但事實上,這個影響是很小的,如果不考慮丟包和線路不穩定等,這個差距一般只有百分只幾,除非你做極限測試.  但實際上,真正用到udp高效傳輸的場合是非常少的,一個關鍵的原因在於它的不可靠性,特別是在Internent上,遇到網關路由高負載的時候,優先扔掉udp包,而且有幾率發生連續的丟包. 我個人認為,udp最大的優點在於它的可塑性非常強, 我們可以通過各種機制來改造udp,例如實現可靠傳輸,實現1對多傳輸, 實現包和流模式同時傳輸,優先發送,多路雙向傳輸等等, 很多擴展在tcp上是無法實現的,但是通過udp擴展就可以很輕松的做到. 同樣,通過擴展udp來實現可靠傳輸,我們可以避開tcp/ip協議棧實現中指針鏈表查詢導致的性能急劇下降,很多人都沒有意識到這點,其實在應對海量連接方面,udp可靠傳輸能支持的用戶數量遠超tcp,因為udp不需要那種大規模的鏈表查詢,是個隊列操作. 那么udp的缺點是什么,最要命的就是不可靠傳輸,其次是包的偽造,雖然我們可以通過加入各種機制和擴展,把udp改造成可靠傳輸,但是由於這個實現是在應用層,因此在面對少量用戶大流量傳輸的時候,極限輸出不如tcp,例如本機.

那么如何選擇tcp還是udp?

先看下人家怎么選

1 HTTP,  http協議現在已經深入影響到我們的方方面面,重要性就不說了, 它采用的是tcp 協議,為什么使用tcp, 因為它傳輸的內容是不可以出現丟失,亂序等各種錯誤的,其次它需要跨平台實現,而tcp滿足了這個要求,發展到今天,http享受了tcp帶來的簡潔高效和跨平台,但是也承受了tcp的各種缺點,例如缺少tcp keep alive機制[這個其實是后來添加的支持,並非普遍實現], tcp協議棧的實現問題引發的難以支持海量用戶並發連接[只能通過dns等級別的集群或者cdn來實現],協議太復雜導致很難模塊化處理[其實這個問題已經在nginx解決了,nginx通過模塊化和對協議的分段處理機制,並引入消息機制,避免了多進程[線程]的頻繁切換,相比apache等老牌web服務器軟件,在應對大量用戶上擁有極大的優勢。 即使站在今天的角度看,http也確實應該選擇tcp.

2 FTP, 這個協議比http更加古老,它采用的也是tcp協議, 因為它的每一個指令,或者文件傳輸的數據流,都需要保證可靠性,同時要求在各種平台上廣泛支持,那么就只能選擇tcp, 和http不同,它采用了noop指令機制來處理tcp缺少keep alive機制帶來的問題,也就是客戶端必須每過一段時間,如果沒有發送其他指令,就必須發送一個noop指令給服務器,避免被服務器認為是死連接。 Ftp的缺陷在哪里呢?,其次它的文件傳輸是采用新的數據連接來執行,等於1個用戶需要2個連接,其次當一個文件正在傳輸的時候,你無法進行其他操作,例如列表,也許你可以把它當作是一一對應的典范,因為這樣我們可以直接用命令行進行控制,但是很多用戶其實是需要在下載的時候同時進行列表等操作的,為了解決這個問題,很多客戶端只要開啟多個指令連接[和數據連接],這樣一來,無形中額外帶給了Ftp服務器很多壓力,而采用udp可靠傳輸就不存在這個問題,但是udp可靠傳輸是沒有跨平台支持的,這樣是魚和熊掌不可兼得,對於這樣一個簡單的開放協議的實現,tcp是個好選擇。

3 POP3/SMTP, 常見的郵件協議,沒什么好說的,反應--應答模式,跨平台要求,因此tcp是個選擇,這個協議的悲劇在於,當初沒有考慮到郵件附件會越來越大的問題,因此它的實現中將附件文件采用了base64編碼格式,用文本模式進行發送,導致產生了大量的額外流量。

4 TFTP ,這是一個非常古老的用於內部傳輸小文件的協議,沒有FTP那么多功能,采用的是udp協議,通過在包中加入包頭信息,用盡可能簡單的代碼來實現小文件傳輸,注意是小文件,是一個值得參考的udp改造應用范例.

5 通常的voip,實時視頻流等,通常會采用udp協議,這是以內這些應用可以允許丟包,很多人可能認為是udp的高效率所以才在這方面有廣泛應用,這我不敢苟同,我個人認為,之所以采用udp,是因為這些傳輸允許丟包,這是一個最大的前提

那么現在來歸納一下

[1] 如果數據要求完整,不允許任何錯誤發生

     [A] 應用層協議開放模式 [例如http ftp]

           建議選擇tcp,幾乎是唯一選擇.

    [B] 應用曾協議封閉模式 [例如游戲]

         (1) 大量連接

               [a] 長連接  

                    [一] 少量數據傳輸

                           優先考慮可靠udp傳輸 , tcp建議在20000連接以下使用.

                   [二] 大流量數據傳輸

                          只有在10000連接以下可以考慮tcp , 其他情況優先使用udp可靠傳輸    

               [b] 短連接

                  [一] 少量數據傳輸

                         建議使用udp標准模式, 加入序列號, 如果連接上限不超2萬,可以考慮tcp

                 [二] 大流量數據傳輸

                       10000連接以下考慮tcp ,其他情況使用udp可靠傳輸

               在遇到海量連接的情況下,建議優先考慮udp可靠傳輸,使用tcp,由於tcp/ip棧的鏈表實現的影響,連接越多,性能下降越快,而udp可以實現隊列,是一條平滑的直線,幾乎沒有性能影響.

         (2) 有限連接 [通常小於2000 , 一般每服務器為幾百到1000左右]

                  [a] 長連接  

                        除非有數據的實時性要求,優先考慮tcp,否則使用udp可靠傳輸.        

                 

               [b] 短連接

                      優先考慮tcp.

              在有限連接的情況下,使用tcp可以減少代碼的復雜性,增加廣泛的移植性,並且不需要考慮性能問題.  

[2] 允許丟包,甚至可以亂序

           [a] 對實時性要求比較高,例如voip , 那么udp是最優選擇.

           [b] 部分數據允許丟包,部分數據要求完整,部分有實時性要求,通常這樣的需求是出現在游戲里, 這時候, 基於udp協議改造的udp多路可靠傳輸[同時支持不可靠模式],基本是唯一能滿足的,當然也可以使用tcp來傳輸要求完整的數據,但是通常不建議,因為同時使用tcp和udp傳輸數據,會導致代碼臃腫復雜度增加,udp可靠傳輸完全可以替代tcp.

           [c]部分數據優先傳輸,部分可丟棄數據在規定時效內傳輸, 這通常是實時視頻流, 在有限連接模式下,可以考慮tcp+udp , 但是通常, 可靠udp傳輸是最好的選擇.      

最后的話, tcp/ip協議很偉大, 在這些基礎上誕生了很多划時代的應用,但是時代在發展,需求也在改變,幾十年前誕生的基礎協議,也遇到了各種問題,典型的是32位地址編碼問題,雖然通過nat等技術盡可能的支持更多的機器接入,但是很多應用被限制了,由於tcp/ip協議的巨大影響和事實的上的壟斷,導致后續的更新必須考慮到完全兼容, ipv6出現了, 它繼承了ipv4的幾乎所有優點和缺點,只為了一個字,兼容.    我們可以擁有更快的cpu , 內存, 更強大的tcp/ip系統調用api,但是比較遺憾,tcp/ip協議棧的實現,我們始終無法繞開指針鏈表, 而正是這,導致了tcp模式在面對海量連接的時候,超過一定數量,網絡io性能直線下降, 許許多多的工程師始終認為是cpu 內存不夠導致,卻沒有想到是tcp協議棧的實現上存在性能瓶頸. 在目前的情況下,也只有udp能避開這個協議棧的性能瓶頸,為什么? 因為udp采用的是1對多的虛擬連接, 例如,當虛擬[或者實際]通過udp構建的連接數量是1萬個的時候,實際上在協議棧增加的單元只有1個, 而同樣1萬個連接,tcp增加的單元是1萬個,每個片的到達平均要查詢5千次,而可靠udp采用隊列模式,查詢次數是1, 因此, 在今天, 如果你希望你的每台服務器能支持更多的連接,除非你的應用協議需要開放或者兼容其他應用,否則盡可能考慮采用udp, 而不是tcp.
---------------------

原文:https://blog.csdn.net/yjxsdzx/article/details/71937886


免責聲明!

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



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