對於 Socket 粘包的困惑?

我正在用nodejs寫一個im的後端,在傳輸數據的時候可能會有粘包、分包的問題。

在進行了大量google之後,找到了兩種通用且常用的方案:
1. 添加分隔符 =&>[數據流][分隔符][數據流] [分隔符]
2. 添加一個包長 =&> [包長][數據流][包長][數據流]

對於以上兩種方案我都有一些疑問,找了很久都沒有找到答案,而使用上述方案的人視乎都沒有我的疑問,所以我覺得可能是因為有一些我不知道的原因造成了我的疑問,期待大家幫助我解惑:

1. 針對使用分隔符來拆包,因為數據的格式無法嚴格控制(比如說用戶的輸入、文字表情等)所以難免會有分隔符和數據中的某個字元相同的問題,這個要如何解決?

2. 如何保證分隔符前面的數據是正確的比如:
=&> [我是完整數據][分隔符] 客戶端發送給服務端一次完整的數據
=&> [數據][分隔符] 服務端接收到的不完整數據("我是完整"這4個漢字數據因為某些原因在傳輸過程中丟失,沒有接收到)

3. 針對添加數據流長度來拆包,如果長度規定的是4個位元組,但是因為某種原因丟失了前面兩個位元組,那麼我整個數據流就相當於全部錯位了,這個如何處理?

針對上述的 2 和 3,其實歸根到底都是一個問題,就是在傳輸過程中丟失一部分位元組。我在搜尋答案的時候發現大家都很少有考慮過這種情況(幾乎沒有)

所以我感覺我的這種顧慮可能因為一些其他的因素不會存在,比如:
在傳輸數據過程中tcp會對數據進行校驗如果有缺失就要求客戶端的tcp重新發送一次、或者乾脆就捨棄這一整塊數據等等。

因為這只是我的猜想,而我又不知道如何去驗證,所以我無法說服自己,期待有一些權威的說明來幫我理解一下。


1. 簡單的辦法是 escape,更好的做法是不要用分隔符做分包。
2. 3. TCP 不會丟中間的位元組,但是你需要考慮收發數據長度相同但內容不一致的情況。

完整的討論見書第 7.3.1 節「TCP 分包」以及 A.1.13 「TCP 的可靠性有多高」。


誰告訴你TCP要按包讀寫數據你就回去打誰一頭包。
誰告訴你TCP會在中間丟兩個位元組你就回去把誰扯成兩節。


不會中間丟幾個位元組的。

拿不準就用http協議吧。


tcp傳輸過程中不會丟失位元組,不過有可能傳輸過程中某些位元組被修改,所以可以定義一個固定長度的包頭加變長的傳輸內容,包頭裡邊有內容長度以及內容檢驗值以及其他一些諸如版本號


TCP/IP 高效編程 - 改善網路程序的 44 個技巧. (Effective TCP/IP Programming - 44 Tips to improve Your Network Programs) 一書中.

第二章, 技巧 6: 記住, TCP 是一種流協議. 有對你提到的兩種常見的分包方式的對比說明.

增加報文首部(記錄包長)是一種更通用簡單的辦法.


tcp是可靠的原因之一就是,要麼死,要麼對。

所以要麼連接中斷,要麼就不會丟包,當然對收到的數據做校驗還是有必要的,低概率事件底層crc校驗沒檢查出來。

https://github.com/atframework/libatbus/blob/master/src/channel_io_stream.cpp

我這裡用了動態長度整數來做包長度記錄,僅供參考。


【NodeJs的TCP中的粘包、分包問題的解決方案!?】傳送門:https://github.com/lvgithub/stickPackage


完全不能理解為什麼需要用分隔符,我自己有用Java寫過WebSocket的伺服器,不管是瀏覽器端的JavaScript還是伺服器端的endpoint ,都是面對一個個完整的message,它們連frame都根本不知道。


TCP是流,跟自來水一樣,不會亂序的


推薦閱讀:

MQTT和Websocket的區別是什麼?

TAG:Socket | TCPIP | TCP | WebSocket |