粘包/拆包
TCP 的一個(gè)數(shù)據(jù)包可能包含一個(gè)、多個(gè)或者不足一個(gè)應(yīng)用層數(shù)據(jù)包,程序需要按照一整個(gè)應(yīng)用包進(jìn)行處理,這就會(huì)帶來(lái)一個(gè)問(wèn)題,TCP 的數(shù)據(jù)包不能直接拿來(lái)處理。
原因深究
socket 緩沖區(qū):socket 通信時(shí),收發(fā)兩端在內(nèi)核層都有一個(gè)緩沖區(qū),send時(shí),數(shù)據(jù)不會(huì)馬上發(fā)送到對(duì)端,而是寫(xiě)入到send緩沖區(qū),由內(nèi)核在合適的時(shí)機(jī)將數(shù)據(jù)發(fā)送到對(duì)端;接受端原理相似。
MTU(Max Transfer Unit)最大傳輸單元:MTU是網(wǎng)絡(luò)層概念,表示一個(gè)網(wǎng)絡(luò)數(shù)據(jù)幀的最大值,當(dāng)送的數(shù)據(jù)大于MTU時(shí),網(wǎng)絡(luò)層會(huì)對(duì)數(shù)據(jù)進(jìn)行分割發(fā)送,這個(gè)過(guò)程叫做分片。
MSS(Max Segment Size)最大分段大小:MSS 是TCP傳輸層的概念,與MTU類似表示TCP數(shù)據(jù)包的最大值。為了達(dá)到最佳的傳輸效能TCP協(xié)議在建立連接的時(shí)候通常要協(xié)商雙方的MSS值,這個(gè)值TCP協(xié)議在實(shí)現(xiàn)的時(shí)候往往用MTU值代替(需要減去IP數(shù)據(jù)包包頭的大小20Bytes和TCP數(shù)據(jù)段的包頭20Bytes)所以往往MSS為1460。TCP根據(jù)MSS 將小數(shù)據(jù)包合并為一個(gè)大的數(shù)據(jù)包,或者將一個(gè)大的數(shù)據(jù)包分割成MSS大小的數(shù)據(jù)包進(jìn)行發(fā)送,這個(gè)過(guò)程叫做分段。TCP 通過(guò) MSS 避免了 在網(wǎng)絡(luò)層的分片。
基于以上,粘包/拆包會(huì)發(fā)生在以下幾種情況:
單次數(shù)據(jù)寫(xiě)操作超出了socket 緩沖區(qū)的大小,導(dǎo)致內(nèi)核不得不將緩沖區(qū)中的數(shù)據(jù)發(fā)送出去;
對(duì)于TCP協(xié)議,針對(duì)MSS的數(shù)據(jù)分段;
以太網(wǎng)幀大于MTU時(shí)的TCP分片;
應(yīng)用層數(shù)據(jù)包太小導(dǎo)致的數(shù)據(jù)合并;
解決方案
消息定長(zhǎng),比如規(guī)定一個(gè)數(shù)據(jù)包200bytes,不夠的空格補(bǔ)齊,很明顯這種方式擴(kuò)展性很差;
采用固定的消息分隔符,通過(guò)分隔符將數(shù)據(jù)包拆解開(kāi);
數(shù)據(jù)協(xié)議,將數(shù)據(jù)包分為消息頭和消息體,消息頭中說(shuō)明本消息的總長(zhǎng)度;
其他應(yīng)用層協(xié)議;