本文由LearnLHC分享,原始出處:blog.csdn.net/LearnLHC/article/details/115268028,本文進行了排版和內容最佳化。
1、引言
熟悉網路程式設計的(尤其搞實時音影片聊天技術的)同學們都有個約定俗成的主觀論調,一提起UDP和TCP,馬上想到的是UDP沒有TCP可靠,但UDP肯定比TCP高效。說到UDP比TCP高效,理由是什麼呢?事實真是這樣嗎?跟著本文咱們一探究竟!
2、UDP報文格式
每個 UDP 報文分為 UDP 報頭和 UDP 資料區兩部分。報頭由 4 個 16 位長(2 位元組)欄位組成,分別說明該報文的源埠、目的埠、報文長度和校驗值。UDP 報文格式如圖所示:
UDP 報文中每個欄位的含義如下:
1)源埠: 16bits,傳送端的埠;
2)目的埠:16bits,即接收端的埠;
3)長度: 16bits,UDP 資料包總的大小:包頭+資料,單位:位元組;
4)校驗值:16bits,錯誤檢查碼,基於演算法,計算此 UDP 資料包是否損壞。
PS:關於UDP協議可以進一步學習《TCP/IP詳解 - 第11章·UDP:使用者資料包協議》。
3、UDP有傳送快取區嗎?
TCP 有 傳送/接收 快取區,那 UDP 有麼?
3.1 先說結論
每個 UDP socket 都有一個接收緩衝區,但沒有傳送緩衝區。從概念上來說就是隻要有資料就發,不管對方是否可以正確接收,所以不緩衝,不需要傳送緩衝區。UDP雖然有接收緩衝區,但當套介面接收緩衝區滿時,新來的資料包無法進入接收緩衝區,此資料包就被丟棄。因為UDP是沒有流量控制的,快的傳送者可以很容易地就淹沒慢的接收者,導致接收方的 UDP 丟棄資料包。而且:如果在傳輸過程中,一次傳輸被分成多個分片,傳輸中有一個小分片丟失,那接收端最終會捨棄整個檔案,導致傳輸失敗,這就是 UDP 不可靠的原因。
3.2 逐步分析
linux手冊中有設定 UDP 傳送緩衝區相關屬性,也明確提到了send buffer的概念:
那這是否意味著 UDP 是有傳送緩衝區的嗎?我們再看一下《UNIX Network Programming》書中所述,這本書的作者權威性我就不多說了吧,在國內高校此書都是當做教材使用的。(PS:經典書籍《UNIX網路程式設計》最全下載(卷1+卷2、中文版+英文版)[附件下載])書中有下面兩幅圖:
如上圖所示:一張是 TCP 傳送過程協議棧簡化圖,另一張是 UDP 的。UDP 中的 send buffer 是用虛線框圈起來的,具體的敘述我直接引用書中原文:
書中的描述很清楚了,UDP 是沒有傳送緩衝區的,因為 UDP 是不可靠的,他不必像 TCP 一樣需要一個實質的傳送buffer,而且真正 UDP 寫成功返回其實是傳遞到了鏈路層的 output queue 中。
4、UDP包最佳傳輸大小與分片
4.1 UDP 包最佳傳輸大小
資料鏈路層最大傳輸單元是 1500 位元組 (MTU) ,要想 IP 層不分包,那麼 UDP 資料包的最大大小應該是:1500位元組 – IP頭(20位元組) – UDP頭(8位元組) = 1472位元組。
但,理論上 UDP 報文最大長度是 65507 位元組,那實際上傳送這麼大的資料包效果最好嗎?
我們來看分析一下 “分片問題”。(關於 UDP 包大小、MTU相關知識,可以參考《最大可傳輸單元 MTU 對 UDP/TCP 包的大小限制》)
PS:另一篇相關文章也可以一讀《UDP中一個包的大小最大能多大?》。
4.2 分片問題
我們知道 UDP 是不可靠的傳輸協議,為了減少 UDP 包丟失的風險,我們最好能控制 UDP 包在 IP層協議的傳輸過程中不要被切割。
這是為什麼呢?
如果 MTU 是1500,Client 傳送一個 8000位元組大小的 UDP 包,那麼 Server 端阻塞模式下接包,在不丟包的情況下,recvfrom(9000) 是收到 1500,還是 8000。如果某個 IP 分片丟失了,recvfrom(9000),又返回什麼呢?
根據 UDP 通訊的有界性,在 buf 足夠大的情況下,接收到的一定是一個完整的資料包,UDP 資料在下層的分片和組片問題由 IP 層來處理,提交到 UDP 傳輸層一定是一個完整的 UDP 包,那麼 recvfrom(9000) 將返回 8000。如果某個 IP 分片丟失,udp 裡有個 CRC 檢驗,如果包不完整就會丟棄,也不會通知是否接收成功,所以 UDP 是不可靠的傳輸協議,那麼 recvfrom(9000) 將阻塞。
分片分的越多,雖然在傳輸層都是一次 send,一次 recv ,但在傳輸過程中,會傳輸多次,那麼丟包的概論就越大,如何解決丟包問題呢?
5、UDP丟包的原因
前提:在不考慮 IP 層的分片丟失,CRC 檢驗包不完整的情況下。
5.1 UDP緩衝區滿
如果 socke t緩衝區滿了,應用程式沒來得及處理在緩衝區中的 UDP 包,那麼後續來的 UDP 包會被核心丟棄,造成丟包。
在 socket 緩衝區滿造成丟包的情況下,可以透過增大緩衝區的方法來緩解UDP丟包問題。但是,如果服務已經過載了,簡單的增大緩衝區並不能解決問題,反而會造成滾雪球效應,造成請求全部超時,服務不可用。
5.2 UDP緩衝區過小或檔案過大
如果Client 傳送的 UDP 報文很大,而 socket 緩衝區過小無法容下該 UDP 報文,那麼該報文就會丟失。
以前遇到過這種問題,我把接收緩衝設定成 64K 就解決了:
int nRecvBuf=32*1024;//設定為32K
setsockopt(s,SOL_SOCKET,SO_RCVBUF,(const char*)&nRecvBuf,sizeof(int));
5.3 ARP 快取過期
ARP 的快取時間約 10 分鐘,APR 快取列表沒有對方的 MAC 地址或快取過期的時候,會傳送 ARP 請求獲取 MAC 地址,
在沒有獲取到 MAC 地址之前,使用者傳送出去的 UDP 資料包會被核心快取到 arp_queue 這個佇列中,預設最多快取 3 個包,多餘的 UDP 包會被丟棄。
5.4 接收端處理時間過長
呼叫 recv 方法接收端收到資料後,處理資料花了一些時間,處理完後再次呼叫 recv 方法,在這二次呼叫間隔裡,發過來的包可能丟失。
對於這種情況可以修改接收端,將包接收後存入一個緩衝區,然後迅速返回繼續 recv。
5.5 傳送的包巨大
雖然 send 方法會幫你做大包切割成小包傳送的事情,但包太大也不行。
例如超過 50K 的一個 udp 包,不切割直接透過send 方法傳送也會導致這個包丟失。這種情況需要切割成小包再逐個 send。
5.6 傳送的包頻率太快
雖然每個包的大小都小於 mtu size 但是頻率太快,例如 40 多個 mut size 的包連續傳送中間不 sleep,也有可能導致丟包。
這種情況也有時可以透過設定 socket 接收緩衝解決,但有時解決不了。
所以在傳送頻率過快的時候還是考慮 sleep一下吧。
5.7 區域網內不丟包,公網上丟包
這個問題我也是透過切割小包並 slee p傳送解決的。如果流量太大,這個辦法也不靈了。
總之 udp 丟包總是會有的,如果出現了用我的方法解決不了,還有這個幾個方法:要麼減小流量,要麼換 tcp 協議傳輸,要麼做丟包重傳的工作。
6、UDP丟包的解決方案
6.1 從傳送端解決——延遲傳送
適用條件:
1)傳送端是可以控制的;
2)微秒數量級的延遲可以接受。
解決方法:
1)傳送時使用 usleep(1) 延遲 1 微秒傳送,即傳送頻率不要過快;
2)延遲1微妙傳送,可以很好的解決這個問題。
6.2 從接收端解決——資料接收與資料處理相分離
適用條件:無法控制傳送端傳送資料的頻率。
解決方法:
1)用 recvfrom 函式收到資料之後儘快返回,進行下一次 recvfrom,可以透過 多執行緒+佇列 來解決;
2)收到資料之後將資料放入佇列中,另起一個執行緒去處理收到的資料。
6.3 從接收端解決——修改接收快取大小
適用條件:使用方法 2 依然出現大規模丟包的情況,需要進一步最佳化
解決方法:使用 setsockopt 修改接收端的緩衝區大小。
int rcv_size = 1024*1024; //1M
int optlen=sizeof(rcv_size);
//設定好緩衝區大小
int err=setsockopt(sock,SOL_SOCKET,SO_RCVBUF,(char *)&rcv_size,optlen);
設定完畢可以透過下列函式,來檢視當前 sock 的緩衝區大小:
setsockopt(sock,SOL_SOCKET,SO_RCVBUF,(char )&rcv_size,(socklen_t )&optlen);
但是,會發現查到的大小並不是1M而是256kb,後來發現原來是 linux 系統預設緩衝區大小為 128kb,設定最大是這個的 2倍,所以需要透過修改系統預設緩衝區大小來解決。
使用root賬戶在命令列下輸入:
vi /etc/sysctl.conf
新增一行記錄(1049576=1024*1024=1M):
net.core.rmem_max=1048576
儲存之後輸入:
/sbin/sysctl -p
使修改的配置生效:此時可以透過 sysctl -a|grep rmem_max 來看配置是否生效。
生效之後可以再次執行程式來 getsockopt 看緩衝區是否變大了,是否還會出現丟包現象了。
作者使用的是 方法2+方法3 雙管齊下,已經不會出現丟包現象了,如果還有不同程度的丟包 可以透過方法三種繼續增加緩衝區大小的方式來解決。
7、UDP實現資料必達
7.1 UDP致命性缺點
UDP 是無連線的,面向訊息的資料傳輸協議。
與TCP相比,有兩個致命的缺點:
1):資料包容易丟失;
2):資料包無序。
7.2 解決方案1:回覆 + 重發 + 編號 機制
7.2.1)分析:
要實現檔案的可靠傳輸,就必須在上層對資料丟包和亂序作特殊處理,必須要有要有 丟包重發機制 和 超時機制。
常見的可靠傳輸演算法有模擬 TC P協議,重發請求(ARQ)協議,它又可分為連續 ARQ 協議、選擇重發 ARQ 協議、滑動視窗協議等等。
如果只是小規模程式,也可以自己實現丟包處理,原理基本上就是給檔案分塊,每個資料包的頭部新增一個唯一標識序號的 ID 值,當接收的包頭部 ID 不是期望中的 ID 號,則判定丟包,將丟包 ID 發回服務端,伺服器端接到丟包響應則重發丟失的資料包。
模擬 TCP 協議也相對簡單,3 次握手的思想對丟包處理很有幫助。
7.2.2)回覆 + 重發 + 編號 機制:
1)接收方收到資料後,回覆一個確認包:
如果你不回覆,那麼傳送端是不會知道接收方是否成功收到資料的。比如:A 要發資料 “{data}” 到 B,那 B 收到後,可以回覆一個特定的確認包 “{OK}”,表示成功收到。
但是如果只做上面的回覆處理,還是有問題:比如 B 收到資料後回覆給 A 的資料 "{OK}" 的包,A 沒收到,怎麼辦呢?
2)當 A 沒有收到B的 "{OK}" 包後,要做定時重發資料:
定時重發,直到成功接收到確認包為止,再發下面的資料,當然,重發了一定數量後還是沒能收到確認包,可以執行一下 ARP 的流程,防止對方網路卡更換或別的原因。
但是這樣的話,B 會收到很多重複的資料,假如每次都是 B 回覆確認包 A 收不到的話。
3)傳送資料的包中加個識別符號 - 編號:
比如 A 要傳送的資料 "識別符號data" 到 B,B 收到後,先回復 “{OK}" 確認包,再根據原有的識別符號進行比較,如果識別符號相同,則資料丟失,如果不相同,則原有的識別符號 = 接收識別符號,且處理資料。
當 A 傳送資料包後,沒有收到確認包,則每隔 x 秒,把資料重發一次,直到收到確認包後,更新一下識別符號,再進行後一包的資料傳送。
經過上面1)、2)、3)點的做法,則可以保證資料百分百到達對方,當然,識別符號用 ID 號來代替更好。
7.3 解決方案2:冗餘傳輸
在外網通訊鏈路不穩定的情況下,有什麼辦法可以降低UDP的丟包率呢?一個簡單的辦法來採用冗餘傳輸的方式。如下圖:一般採用較多的是延時雙發,雙髮指的是將原本單發的前後連續的兩個包合併成一個大包傳送,這樣傳送的資料量是原來的兩倍。
這種方式提高丟包率的原理比較簡單,例如本例的冗餘發包方式,在偶數包全丟的情況下,依然能夠還原出完整的資料,也就是在這種情況下,50%的丟包率,依然能夠達到100%的資料接收。
7.4 解決方3:RUDP
詳情請檢視:《如何讓不可靠的UDP變的可靠?》。
文章摘錄如下:
UDP 實現可靠性既然那麼麻煩,那直接用 TCP 好了!
確實很多人也都是這樣做的,TCP 是個基於公平性的可靠通訊協議,但是在一些苛刻的網路條件下 TCP 要麼不能提供正常的通訊質量保證,要麼成本過高。為什麼要在 UDP 之上做可靠保證,究其原因就是在保證通訊的時延和質量的條件下儘量降低成本。
RUDP 主要解決以下相關問題。
1)端對端連通性問題:一般終端直接和終端通訊都會涉及到 NAT 穿越,TCP 在 NAT 穿越實現非常困難,相對來說 UDP 穿越 NAT 卻簡單很多,如果是端到端的可靠通訊一般用 RUDP 方式來解決,場景有:端到端的檔案傳輸、實時音影片傳輸、互動指令傳輸等等。(UDP NAT穿越簡單很多)
2)弱網環境傳輸問題:在一些 Wi-Fi 或者 3G/4G 移動網下,需要做低延遲可靠通訊,如果用 TCP 通訊延遲可能會非常大,這會影響使用者體驗。例如:實時的操作類網遊通訊、語音對話、多方白板書寫等,這些場景可以採用特殊的 RUDP 方式來解決這類問題;(弱網傳輸UDP延長會低很多)
3)頻寬競爭問題:有時候客戶端資料上傳需要突破本身 TCP 公平性的限制來達到高速低延時和穩定,也就是說要用特殊的流控演算法來壓榨客戶端上傳頻寬,例如:直播音影片推流,這類場景用 RUDP 來實現不僅能壓榨頻寬,也能更好地增加通訊的穩定性,避免類似 TCP 的頻繁斷開重連;
4)傳輸路徑最佳化問題:在一些對延時要求很高的場景下,會用應用層 relay 的方式來做傳輸路由最佳化,也就是動態智慧選路,這時雙方採用 RUDP 方式來傳輸,中間的延遲進行 relay 選路最佳化延時。還有一類基於傳輸吞吐量的場景,例如:服務與服務之間資料分發、資料備份等,這類場景一般會採用多點並聯 relay 來提高傳輸的速度,也是要建立在 RUDP 上的(這兩點在後面著重來描述);
5)資源最佳化問題:某些場景為了避免 TCP 的三次握手和四次揮手的過程,會採用 RUDP 來最佳化資源的佔用率和響應時間,提高系統的併發能力,例如 QUIC。
8、UDP真的比TCP高效嗎
回到正題:相信很多同學都認為 UDP 無連線,無需重傳和處理確認,UDP 肯定比較高效?
然而 UDP 在大多情況下並不一定比 TCP 高效。TCP 發展至今天,為了適應各種複雜的網路環境,其演算法已經非常豐富,協議本身經過了很多最佳化,如果能夠合理配置 TCP 的各種引數選項,那麼在多數的網路環境下 TCP 是要比 UDP 更高效的。
影響 UDP 高效因素有以下3點。
1)UDP 無法智慧利用空閒頻寬導致資源利用率低:
一個簡單的事實是 UDP 並不會受到 MTU 的影響,MTU 只會影響下層的 IP 分片,對此 UDP 一無所知。
在極端情況下,UDP 每次都是發小包,包是 MTU 的幾百分之一,這樣就造成 UDP 包的有效資料佔比較小 (UDP 頭的封裝成本);
或者,UDP 每次都是發巨大的 UDP 包,包大小是 MTU 的幾百倍,這樣會造成下層 IP 層的大量分片,大量分片的情況下,其中某個分片丟失了,就會導致整個 UDP 包的無效。
由於網路情況是動態變化的,UDP 無法根據變化進行調整,發包過大或過小,從而導致頻寬利用率低下,有效吞吐量較低。
而 TCP 有一套智慧演算法,當發現資料必須積攢的時候,就說明此時不積攢也不行,TCP 的複雜演算法會在延遲和吞吐量之間達到一個很好的平衡。
2)UDP無法動態調整發包:
由於 UDP 沒有確認機制,沒有流量控制和擁塞控制,這樣在網路出現擁塞 或 通訊兩端處理能力不匹配的時候,UDP 並不會進行調整傳送速率,從而導致大量丟包。
在丟包的時候,不合理的簡單重傳策略會導致重傳風暴,進一步加劇網路的擁塞,從而導致丟包率雪上加霜。
更加嚴重的是,UDP 的 無秩序性和自私性,一個瘋狂的 UDP 程式可能會導致這個網路的擁塞,擠壓其他程式的流量頻寬,導致所有業務質量都下降。
3)改進 UDP 的成本較高:
可能有同學想到針對 UDP 的一些缺點,在使用者態做些調整改進,新增上簡單的重傳和動態發包大小最佳化。
然而,這樣的改進並比簡單的,UDP 程式設計可是比 TCP 要難不少的,考慮到改造成本,為什麼不直接用TCP呢?
當然可以拿開源的一些實現來抄一下(例如:libjingle),或者擁抱一下 Google 的 QUIC 協議,然而,這些都需要不少成本的。
上面說了這麼多,難道真的不該用UDP了嗎?其實也不是的,在某些場景下,我們還是必須 UDP 才行的。那麼 UDP 的較為合適的使用場景是哪些呢?
9、UDP協議的最佳使用場合
9.1 高實時性和低持續性場景
在分組交換通訊當中,協議棧的成本主要表現在以下兩方面:
1) 封裝帶來的空間複雜度;
2)快取帶來的時間複雜度。
以上兩者是對立影響的,如果想減少封裝消耗,那麼就必須快取使用者資料到一定量在一次性封裝傳送出去,這樣每個協議包的有效載荷將達到最大化,這無疑是節省了頻寬空間,頻寬利用率較高,但是延時增大了。
如果想降低延時,那麼就需要將使用者資料立馬封裝發出去,這樣顯然會造成消耗更多的協議頭等消耗,浪費頻寬空間。
因此,我們進行協議選擇的時候,需要重點考慮一下空間複雜度和時間複雜度間的平衡。
通訊的持續性對兩者的影響比較大,根據通訊的持續性有兩種通訊型別:
1) 短連線通訊;
2) 長連線通訊。
對於短連線通訊:一方面如果業務只需要發一兩個包並且對丟包有一定的容忍度,同時業務自己有簡單的輪詢或重複機制,那麼採用 UDP 會較為好些。在這樣的場景下,如果用 TCP,僅僅握手就需要 3 個包,這樣顯然有點不划算,一個典型的例子是 DNS 查詢。
另一方面:如果業務實時性要求非常高,並且不能忍受重傳,那麼首先就是 UDP 了或者只能用 UDP 了,例如 NTP 協議,重傳 NTP 訊息純屬添亂(為什麼呢?重傳一個過期的時間包過來,還不如發一個新的 UDP 包同步新的時間過來)。
如果 NTP 協議採用 TCP,撇開握手消耗較多資料包互動的問題,由於 TCP 受 Nagel 演算法等影響,使用者資料會在一定情況下會被核心快取延後傳送出去,這樣時間同步就會出現比較大的偏差,協議將不可用。
9.2 多點通訊場景
對於一些多點通訊的場景,如果採用有連線的 TCP,那麼就需要和多個通訊節點建立其雙向連線,然後有時在 NAT 環境下,兩個通訊節點建立其直接的 TCP 連線不是一個容易的事情,在涉及 NAT 穿越的時候,UDP 協議的無連線性使得穿透成功率更高。
(原因詳見:由於 UDP 的無連線性,那麼其完全可以向一個組播地址傳送資料或者輪轉地向多個目的地持續傳送相同的資料,從而更為容易實現多點通訊。)
一個典型的場景是:多人實時音影片通訊,這種場景下實時性要求比較高,可以容忍一定的丟包率。
比如:對於音訊,對端連續傳送 p1、p2、p3 三個包,另一端收到了 p1 和 p3,在沒收到 p2 的保持 p1 的最後一個音(也是為什麼有時候網路丟包就會聽到嗞嗞嗞嗞嗞嗞…或者卟卟卟卟卟卟卟卟…重音的原因),等到到 p3 就接著播 p3 了,不需要也不能補幀,一補就越來越大的延時。
對於這樣的場景就比較合適用 UDP 了,如果採用 TCP,那麼在出現丟包的時候,就可能會出現比較大的延時。
9.3 其它UDP應用舉例
通常情況下,UDP 的使用範圍是較小的,但在以下的場景下,使用 UDP 才是明智的:
1)實時性要求很高,並且幾乎不能容忍重傳:例子:NTP 協議,實時音影片通訊,直播、實時遊戲、多人動作類遊戲中人物動作、位置;
2) TCP 實在不方便實現多點傳輸的情況;
3)需要進行 NAT 穿越;
4) 對網路狀態很熟悉,確保 udp 網路中沒有氓流行為,瘋狂搶頻寬;
5)熟悉 UDP 程式設計。
UDP本身是不可靠,現在需要保證可靠,在不改變 UDP 協議的情況下能夠想到的是在應用層做可靠性設計,但是應用層做可能通用性會差一些,那麼在傳輸層和應用層之間加一層實現UDP的可靠性呢?
基於這個想法提出了RUDP(Reliable UDP),實際上,已經有專案在這麼做了,比如 Google 的 QUIC(《一泡尿的時間,快速讀懂QUIC協議》) 和 WebRTC(《零基礎快速入門WebRTC:基本概念、關鍵技術、與WebSocket的區別等》)。
據瞭解,目前國內廠商做實時傳輸一般都會考慮 RUDP。
9.4 QQ聊天中使用的udp協議淺析
曾今的一個討論貼:《為什麼QQ用的是UDP協議而不是TCP協議?》,可以一併讀一讀。
1)用 tcp 長連線,對伺服器的負擔很大:
首先每一個 QQ 客戶端實際上都適合伺服器互動,再由伺服器轉發給正在通訊的使用者,如果每一個 QQ 從一上線到下線的這段時間全部採用 tcp 長連線,這對伺服器的負擔很大,而如果採用 tcp 短連線,頻繁的連線斷開也會造成網路負擔,而採用 udp 則可以避開上述麻煩,減少伺服器的負擔。
不管 udp 還是 tcp,最終登陸成功之後,QQ 都會有一個 tcp 連線來保持線上狀態。這個 tcp 連線的遠端埠一般是80,採用 udp 方式登陸的時候,埠是8000。
udp 協議是無連線方式的協議,它的效率高,速度快,佔資源少,但是其傳輸機制為不可靠傳送,必須依靠輔助的演算法來完成傳輸控制。
QQ 採用的通訊協議以 udp 為主,輔以 tcp 協議。由於 QQ 的伺服器設計容量是海量級的應用,一臺伺服器要同時容納十幾萬的併發連線,因此伺服器端只有採用 udp 協議與客戶端進行通訊才能保證這種超大規模的服務。
2)tcp 較難實現 NAT 穿越:
QQ 客戶端之間的訊息傳送也採用了 udp 模式,因為國內的網路環境非常複雜,而且很多使用者採用的方式是透過代理伺服器共享一條線路上網的方式。
在這些複雜的情況下,客戶端之間能彼此建立起來 tcp 連線的機率較小,嚴重影響傳送資訊的效率。
而 udp 包能夠穿透大部分的代理伺服器,因此 QQ 選擇了 udp 作為客戶之間的主要通訊協議。採用 udp 協議,透過伺服器中轉方式。因此,現在的 IP 偵探在你僅僅跟對方傳送聊天訊息的時候是無法獲取到IP的。
3)讓 UDP 變得可靠:
大家都知道,udp 協議是不可靠協議,它只管傳送,不管對方是否收到的,但它的傳輸很高效。
但是作為聊天軟體,怎麼可以採用這樣的不可靠方式來傳輸訊息呢?
於是,騰訊採用了上層協議來保證可靠傳輸:如果客戶端使用 udp 協議發出訊息後,伺服器收到該包,需要使用 udp 協議發回一個應答包,如此來保證訊息可以無遺漏傳輸。
之所以會發生在客戶端明明看到“訊息傳送失敗”但對方又收到了這個訊息的情況,就是因為客戶端發出的訊息伺服器已經收到並轉發成功,但客戶端由於網路原因沒有收到伺服器的應答包引起的。
QQ 並不是端對端的聊天軟體,是得經過伺服器轉發訊息的,透過 QQ 聊天,資料是 A 發到伺服器,伺服器再轉發到 B。
10、系列文章
本文是系列文章中的第 18篇,本系列文章的大綱如下:
《不為人知的網路程式設計(一):淺析TCP協議中的疑難雜症(上篇)》
《不為人知的網路程式設計(二):淺析TCP協議中的疑難雜症(下篇)》
《不為人知的網路程式設計(三):關閉TCP連線時為什麼會TIME_WAIT、CLOSE_WAIT》
《不為人知的網路程式設計(四):深入研究分析TCP的異常關閉》
《不為人知的網路程式設計(五):UDP的連線性和負載均衡》
《不為人知的網路程式設計(六):深入地理解UDP協議並用好它》
《不為人知的網路程式設計(七):如何讓不可靠的UDP變的可靠?》
《不為人知的網路程式設計(八):從資料傳輸層深度解密HTTP》
《不為人知的網路程式設計(九):理論聯絡實際,全方位深入理解DNS》
《不為人知的網路程式設計(十):深入作業系統,從核心理解網路包的接收過程(Linux篇)》
《不為人知的網路程式設計(十一):從底層入手,深度分析TCP連線耗時的秘密》
《不為人知的網路程式設計(十二):徹底搞懂TCP協議層的KeepAlive保活機制》
《不為人知的網路程式設計(十三):深入作業系統,徹底搞懂127.0.0.1本機網路通訊》
《不為人知的網路程式設計(十四):拔掉網線再插上,TCP連線還在嗎?一文即懂!》
《不為人知的網路程式設計(十五):深入作業系統,一文搞懂Socket到底是什麼》
《不為人知的網路程式設計(十六):深入分析與解決TCP的RST經典異常問題》
《不為人知的網路程式設計(十七):冰山之下,一次網路請求背後的技術秘密》
《不為人知的網路程式設計(十八):UDP比TCP高效?還真不一定!》(* 本文)
11、參考資料
[1] TCP/IP詳解 - 第11章·UDP:使用者資料包協議
[2] TCP/IP詳解 - 第17章·TCP:傳輸控制協議
[3] 快速理解TCP協議一篇就夠
[4] 快速理解TCP和UDP的差異
[5] 快速理解為什麼說UDP有時比TCP更有優勢
[6] 一泡尿的時間,快速搞懂TCP和UDP的區別
[7] 技術掃盲:新一代基於UDP的低延時網路傳輸層協議——QUIC詳解
[8] 什麼是公網IP和內網IP?NAT轉換又是什麼鬼?
[9] 假如你來設計TCP協議,會怎麼做?
[10] 深入地理解UDP協議並用好它
[11] 如何讓不可靠的UDP變的可靠?
[12] 從底層入手,深度分析TCP連線耗時的秘密
[13] 深入作業系統,從核心理解網路包的接收過程(Linux篇)
[14] 淺析TCP協議中的疑難雜症(上篇)
[15] UDP中一個包的大小最大能多大?
[16] 一泡尿的時間,快速讀懂QUIC協議
[17] 零基礎快速入門WebRTC:基本概念、關鍵技術、與WebSocket的區別等
(本文已同步釋出於:http://www.52im.net/thread-4724-1-1.html)