Win32可以通過設置setsockopt為TCP_NODELAY的方式,來取消Nagle算法,這樣就會出現隻要有小數據段就會發送,接收方隻要數據處理的過來,就可以避免TCP的拆包和粘包問題。但是這種方法會造成TCP傳輸效率降低,也不是一種理想的方法
也就是說協議棧為了避免一個字節就帶上包頭造成資源浪費,一般會啟動tcp nagle算法,他會把這些1個字節的數據粘起來,然後一起發送。當然你可以做手動關閉,即禁用tcp Nagle算法,涉及TCP_NODELAY選項。下面來說下nagle算法規則:
為了盡可能的利用網絡帶寬,TCP總是希望盡可能多的發送足夠大的數據。一個鍊接會設置MSS參數,因此TCP/IP希望每次都能夠以MSS尺寸的數據塊來發送數據。即Nagle算法就是為了盡可能發送大塊數據,避免網絡中充斥着許多小數據塊,提高網絡利用率。
Nagle算法的基本定義是:任意時刻,最多隻能有一個未被确認的小段。所謂小段,指的是小于MSS尺寸的數據塊,所謂未被确認,是指一個數據塊發送出去後,沒有收到對方發送的ACK确認該數據已經收到。同時擁有以下幾個規則:
1. 如果包長度達到MSS,則允許發送
2. 如果該包設置了FIN,則允許發送
3. 如果設置了TCP_NODELAY選項,則允許發送。
4. 未設置TCP_CORK選項時,若所有發出去的小數據包(包長度小于MSS)均被确認,則允許發送
5. 上述條件都未滿足,但發生了超時(一般為200ms), 則立即發送
Nagle算法隻允許一個未被ACK的包存在網絡,它不管包的大小。因此它事實上就是一個擴展的停止協議,隻不過他是基于“包停”等的,而不是基于“字節停”等的。即Nagle算法完全由TCP協議的ACK機制決定,這會帶來一些問題。比如如果對端的ACK回複很快的話,Nagle事實上不會拼接太多的包,雖然避免了網絡擁塞,但是網絡總體的利用率很低。所以Nagle算法一般和延遲确認機制一起使用 TCP延遲确認機制介紹,但是也會存在一些情況導緻網絡超時。
同時會存在需要關閉Nagle的情況分别為:
1. 對端不向本端發送數據,并且對延時比較敏感的操作;這種操作沒法捎帶ack。
2. 寫-寫-讀操作;對于此種情況,優先使用其他方式,而不是關閉Nagle算法:
1. 使用writev,而不是兩次調用write,單個writev調用會使tcp輸出一次而不是兩次,隻産生一個tcp分節,這是首選方法;
2. 把兩次寫操作的數據複制到單個緩沖區,然後對緩沖區調用一次write;
3. 關閉Nagle算法,調用write兩次;有損于網絡,通常不考慮;
粘包問題:
先來簡單介紹下什麼是粘包?
因為TCP是面向字節流的協議,沒有消息保護邊界,同時在TCP的首部沒有表示數據長度的字段。一方發送的多個數據包,可能會被合并成一個大的數據包進行傳輸,這就是粘包。粘包情況有兩種:
* 一種是粘在一起的包都是完整的數據包
* 另一種情況是粘在一起的包有不完整的包。
與粘包對應的還有一個概念:分包,他是指在出現粘包的時候我們的接收方要進行分包處理。(在長連接中都會出現) 數據包的邊界發生錯位,導緻讀出錯誤的數據分包,進而曲解原始數據含義。
原因:
發送方原因
TCP默認使用上面提到的Nagle協議,即會将小數據包合成大數據包一起發送出去。
接收方原因
接收方引起的粘包是由于接收方用戶進程不及時接收數據,從而導緻粘包現象。這是因為接收方先把收到的數據放在系統接收緩沖區,用戶進程從該緩沖區取數據,若下一包數據到達時前一包數據尚未被用戶進程取走,則下一包數據放到系統接收緩沖區時就接到前一包數據之後,而用戶進程根據預先設定的緩沖區大小從系統接收緩沖區取數據,這樣就一次取到了多包數據
從上面的可以看出,其實還是取決于因為接收方處理數據的速度太慢,發送方的數據發送的太快,導緻緩沖區存在多個數據包,當你進行正常讀取的時候,就會一次性取到幾個包粘在一起
解決辦法:
* 發送端給每個數據包添加包首部,首部中應該至少包含數據包的長度,這樣接收端在接收到數據後,通過讀取包首部的長度字段,便知道每一個數據包的實際長度了。 分包解決策略 - socket接收指定長度的buf
* 發送端将每個數據包封裝為固定長度(不夠的可以通過補0填充),這樣接收端每次從接收緩沖區中讀取固定長度的數據就自然而然的把每個數據包拆分開來。
* 可以在數據包之間設置邊界,如添加特殊符号,這樣,接收端通過這個邊界就可以将不同的數據包拆分開。
* 通過設置TCP_NODELAY來關閉Nagle算法
* 通過設置PUSH标志到TCP的報頭,發送方在發送數據的時候可以設置這個flag,這個标志會通知接收方将接收到的數據全部提交給接收進程,這裡所說的數據包括與PUSH包一起傳輸的數據以及之前就為該進程傳輸過來的數據。Server收到之後就會立刻把這些數據提交給應用層進程,而不再等待額外是否有數據到達。設置PUSH标志也不能完全解決TCP粘包,隻是降低了接收方粘包的可能性。實際上現在的TCP協議棧基本上都可以自行處理這個問題,而不是交給應用層處理,所以設置這個PUSH也不是一個理想方法
PS:
UDP因為不是面向“流”的,而且UDP是具有消息邊界的,也就是說UDP的發送的每一個數據包都是獨立的,因此UDP并不存在粘包的問題。
,更多精彩资讯请关注tft每日頭條,我们将持续为您更新最新资讯!