粘包问题
粘包問題
我們知道,TCP 協議是面向連接的、可靠的、基于字節流的傳輸層通信協議。其實,TCP作為面向流的協議,不存在“粘包問題”。
什么是粘包
其實這里面有兩種含義:
其一是指,由于TCP是面向流的協議,不會按照應用開發者的期望保持send輸入數據的邊界,導致接收側有可能一下子收到多個應用層報文,需要應用開發者自己分開。
其二是指,用戶數據被TCP發出去的時候,存在多個小尺寸數據被封裝在一個TCP報文中發出去的可能性。這種“粘”不是接收側的效果,而是由于Nagle算法(或者TCP_CORK)的存在,在發送的時候,就把應用開發者多次send的數據,“粘”在一個tcp報文里面發出去了,于是,先被send的數據可能需要等待一段時間,才能跟后面被send的數據一起組成報文發出去。
這兩個其實都不是“問題”。
一、針對上面第一點
1、分析
1)TCP 協議是基于字節流的傳輸層協議,其中不存在消息和數據包的概念。
2)應用層協議沒有使用基于長度或者基于終結符的消息邊界,導致多個消息的粘連,出現了“粘包”想象。這其實是使用者對于 TCP 的理解有誤導致的一個問題。完全可以把這個問題轉換為如何設計應用層協議的問題,即如何將TCP流解碼為報文數據(stream2datagram)。
3)數據傳遞
為了說清楚這個問題,我們先來看下client/server之間數據傳遞的過程。
1、客戶端->發送數據
2、服務端->接收數據
通常我們直覺性的認為,客戶端直接向網絡中傳輸數據,對端從網絡中讀取數據,但是這是不正確的。
socket有緩沖區buffer的概念,每個TCP socket在內核中都有一個發送緩沖區和一個接收緩沖區。客戶端send操作僅僅是把數據拷貝到buffer中,也就是說send完成了,數據并不代表已經發送到服務端了,之后才由TCP協議從buffer中發送到服務端。此時服務端的接收緩沖區被TCP緩存網絡上來的數據,而后server才從buffer中讀取數據。
因此,如果接收數據端的應用層沒有及時讀取 TCP Recv Buffer 中的數據,就會發生粘包。
2、解決辦法
最常見的兩種解決方案就是基于長度或者基于終結符(Delimiter)。
二、針對上面第二點
1、分析
1)Nagle 算法是一種通過減少數據包的方式提高 TCP 傳輸性能的算法。
因為網絡 帶寬有限,它不會將小的數據塊直接發送到目的主機,而是會在本地緩沖區中等待更多待發送的數據,這種批量發送數據的策略雖然會影響實時性和網絡延遲,但是能夠降低網絡擁堵的可能性并減少額外開銷。
Nagle算法如下:
如果包長度達到MSS(或含有Fin包),立刻發送,否則等待下一個包到來;如果下一包到來后兩個包的總長度超過MSS的話,就會進行拆分發送;
等待超時(一般為200ms),第一個包沒到MSS長度,但是又遲遲等不到第二個包的到來,則立即發送。
幾十年前還會發生網絡擁塞的問題,但是今天的網絡帶寬資源不再像過去那么緊張,在默認情況下,Linux 內核都會使用如下的方式默認關閉 Nagle 算法:
TCP_NODELAY = 1
3)除了 Nagle 算法之外,TCP 協議棧中還有另一個用于延遲發送數據的選項 TCP_CORK,如果我們開啟該選項,那么當發送的數據小于 MSS 時,TCP 協議就會延遲 200ms 發送該數據或者等待緩沖區中的數據超過 MSS5。無論是 TCP_NODELAY 還是 TCP_CORK,它們都會通過延遲發送數據來提高帶寬的利用率,它們會對應用層協議寫入的數據進行拆分和重組,而這些機制和配置能夠出現的最重要原因是 — TCP 協議是基于字節流的協議,其本身沒有數據包的概念,不會按照數據包發送數據。
4)其實99%的情況下禁不禁止都一樣,延遲根本不是Nagle算法導致的;就算真有問題,最優解決方案也不是屏蔽Nagle算法。
2、解決辦法
設置TCP_NODELAY來屏蔽Nagle算法。不過這是要看情況的,比如:在頻繁進行超短的信息交互(比如幾個字節)時禁用Nagle算法就是明智之舉。
不過,就算關閉 Nagle 算法,如果接收數據端的應用層沒有及時讀取 TCP Recv Buffer 中的數據,還是會發生粘包。
最后,“粘包”這個詞語容易引起歧義,不建議沿用。stream2datagram就是stream2datagram,TCP_NODELAY就是TCP_NODELAY。
參考鏈接:
https://www.zhihu.com/question/20210025/answer/1744906223
https://segmentfault.com/a/1190000039691657
https://draveness.me/whys-the-design-tcp-message-frame/
總結
- 上一篇: 人口普查系统_晋江市召开第七次人口普查区
- 下一篇: python中xlsxwriter_py