在進行TCP Socket開發(fā)時,都需要處理數(shù)據(jù)包粘包和分包的情況。本文詳細講解解決該問題的步驟。使用的語言是Python。實際上解決該問題很簡單,在應用層下,定義一個協(xié)議:消息頭部+消息長度+消息正文即可。
那什么是粘包和分包呢?
粘包:發(fā)送方發(fā)送兩個字符串”hello”+”world”,接收方卻一次性接收到了”helloworld”。
分包:發(fā)送方發(fā)送字符串”helloworld”,接收方卻接收到了兩個字符串”hello”和”world”。
雖然socket環(huán)境有以上問題,但是TCP傳輸數(shù)據(jù)能保證幾點:
順序不變。例如發(fā)送方發(fā)送hello,接收方也一定順序接收到hello,這個是TCP協(xié)議承諾的,因此這點成為我們解決分包、黏包問題的關鍵。 分割的包中間不會插入其他數(shù)據(jù)。因此如果要使用socket通信,就一定要自己定義一份協(xié)議。目前最常用的協(xié)議標準是:消息頭部(包頭)+消息長度+消息正文
TCP是以段(Segment)為單位發(fā)送數(shù)據(jù)的,建立TCP鏈接后,有一個最大消息長度(MSS)。如果應用層數(shù)據(jù)包超過MSS,就會把應用層數(shù)據(jù)包拆分,分成兩個段來發(fā)送。這個時候接收端的應用層就要拼接這兩個TCP包,才能正確處理數(shù)據(jù)。
相關的,路由器有一個MTU( 最大傳輸單元),一般是1500字節(jié),除去IP頭部20字節(jié),留給TCP的就只有MTU-20字節(jié)。所以一般TCP的MSS為MTU-20=1460字節(jié)。
當應用層數(shù)據(jù)超過1460字節(jié)時,TCP會分多個數(shù)據(jù)包來發(fā)送。
擴展閱讀
TCP的RFC定義MSS的默認值是536,這是因為 RFC 791里說了任何一個IP設備都得最少接收576尺寸的大小(實際上來說576是撥號的網(wǎng)絡的MTU,而576減去IP頭的20個字節(jié)就是536)。
TCP為什么會粘包
有時候,TCP為了提高網(wǎng)絡的利用率,會使用一個叫做Nagle的算法。該算法是指,發(fā)送端即使有要發(fā)送的數(shù)據(jù),如果很少的話,會延遲發(fā)送。如果應用層給TCP傳送數(shù)據(jù)很快的話,就會把兩個應用層數(shù)據(jù)包“粘”在一起,TCP最后只發(fā)一個TCP數(shù)據(jù)包給接收端。
消息頭部不一定只能是一個字節(jié)比如0xAA什么的,也可以包含協(xié)議版本號,指令等,當然也可以把消息長度合并到消息頭部里,唯一的要求是包頭長度要固定的,包體則可變長。下面是我自定義的一個包頭:
| 版本號(ver) | 消息長度(bodySize) | 指令(cmd) |
|---|
版本號,消息長度,指令數(shù)據(jù)類型都是無符號32位整型變量,于是這個消息長度固定為4×3=12字節(jié)。在Python由于沒有類型定義,所以一般是使用struct模塊生成包頭。示例:
新聞熱點
疑難解答
圖片精選