一新翎、TCP 粘包和拆包基本介紹
TCP是面向連接的,面向流的该默,提供高可靠性服務(wù)瞳氓。收發(fā)兩端(客戶端和服務(wù)器端)都要有一一成對(duì)的socket,因此权均,發(fā)送端為了將多個(gè)發(fā)給接收端的包顿膨,更有效的發(fā)給對(duì)方,使用了優(yōu)化方法(Nagle算法)叽赊,將多次間隔較小且數(shù)據(jù)量小的數(shù)據(jù)恋沃,合并成一個(gè)大的數(shù)據(jù)塊,然后進(jìn)行封包必指。這樣做雖然提高了效率囊咏,但是接收端就難于分辨出完整的數(shù)據(jù)包了,因?yàn)槊嫦蛄鞯耐ㄐ攀菬o(wú)消息保護(hù)邊界的塔橡。
由于TCP無(wú)消息保護(hù)邊界, 需要在接收端處理消息邊界問(wèn)題梅割,也就是我們所說(shuō)的粘包、拆包問(wèn)題葛家。
TCP粘包户辞、拆包圖解
假設(shè)客戶端分別發(fā)送了兩個(gè)數(shù)據(jù)包D1和D2給服務(wù)端,由于服務(wù)端一次讀取到字節(jié)數(shù)是不確定的癞谒,故可能存在以下四種情況:
- 1底燎、服務(wù)端分兩次讀取到了兩個(gè)獨(dú)立的數(shù)據(jù)包刃榨,分別是D1和D2,沒(méi)有粘包和拆包双仍。
- 2枢希、服務(wù)端一次接受到了兩個(gè)數(shù)據(jù)包,D1和D2粘合在一起朱沃,稱之為T(mén)CP粘包苞轿。
- 3、服務(wù)端分兩次讀取到了數(shù)據(jù)包逗物,第一次讀取到了完整的D1包和D2包的部分內(nèi)容搬卒,第二次讀取到了D2包的剩余內(nèi)容,這稱之為T(mén)CP拆包敬察。
- 4秀睛、服務(wù)端分兩次讀取到了數(shù)據(jù)包,第一次讀取到了D1包的部分內(nèi)容D1_1莲祸,第二次讀取到了D1包的剩余部分內(nèi)容D1_2和完整的D2包蹂安。
特別要注意的是,如果TCP的接受滑窗非常小锐帜,而數(shù)據(jù)包D1和D2比較大田盈,很有可能會(huì)發(fā)生第五種情況,即服務(wù)端分多次才能將D1和D2包完全接受缴阎,期間發(fā)生多次拆包允瞧。
二、 粘包蛮拔、拆包發(fā)生原因
產(chǎn)生原因主要有這3種:滑動(dòng)窗口述暂、MSS/MTU限制、Nagle算法
1建炫、滑動(dòng)窗口
TCP流量控制主要使用滑動(dòng)窗口協(xié)議畦韭,滑動(dòng)窗口是接受數(shù)據(jù)端使用的窗口大小,用來(lái)告訴發(fā)送端接收端的緩存大小肛跌,以此可以控制發(fā)送端發(fā)送數(shù)據(jù)的大小艺配,從而達(dá)到流量控制的目的。這個(gè)窗口大小就是我們一次傳輸幾個(gè)數(shù)據(jù)衍慎。對(duì)所有數(shù)據(jù)幀按順序賦予編號(hào)转唉,發(fā)送方在發(fā)送過(guò)程中始終保持著一個(gè)發(fā)送窗口,只有落在發(fā)送窗口內(nèi)的幀才允許被發(fā)送稳捆;同時(shí)接收方也維持著一個(gè)接收窗口赠法,只有落在接收窗口內(nèi)的幀才允許接收。這樣通過(guò)調(diào)整發(fā)送方窗口和接收方窗口的大小可以實(shí)現(xiàn)流量控制乔夯。
現(xiàn)在來(lái)看一下滑動(dòng)窗口是如何造成粘包砖织、拆包的原朝?
粘包:假設(shè)發(fā)送方的每256 bytes表示一個(gè)完整的報(bào)文,接收方由于數(shù)據(jù)處理不及時(shí)镶苞,這256個(gè)字節(jié)的數(shù)據(jù)都會(huì)被緩存到SO_RCVBUF(接收緩存區(qū))中。如果接收方的SO_RCVBUF中緩存了多個(gè)報(bào)文鞠评,那么對(duì)于接收方而言茂蚓,這就是粘包。
拆包:考慮另外一種情況剃幌,假設(shè)接收方的窗口只剩了128聋涨,意味著發(fā)送方最多還可以發(fā)送128字節(jié),而由于發(fā)送方的數(shù)據(jù)大小是256字節(jié)负乡,因此只能發(fā)送前128字節(jié)牍白,等到接收方ack后,才能發(fā)送剩余字節(jié)抖棘。這就造成了拆包茂腥。
2、MSS和MTU分片
MSS:是Maximum Segement Size縮寫(xiě)切省,表示TCP報(bào)文中data部分的最大長(zhǎng)度最岗,是TCP協(xié)議在OSI五層網(wǎng)絡(luò)模型中傳輸層對(duì)一次可以發(fā)送的最大數(shù)據(jù)的限制。
MTU:最大傳輸單元是Maxitum Transmission Unit的簡(jiǎn)寫(xiě)朝捆,是OSI五層網(wǎng)絡(luò)模型中鏈路層(datalink layer)對(duì)一次可以發(fā)送的最大數(shù)據(jù)的限制般渡。
當(dāng)需要傳輸?shù)臄?shù)據(jù)大于MSS或者M(jìn)TU時(shí),數(shù)據(jù)會(huì)被拆分成多個(gè)包進(jìn)行傳輸芙盘。由于MSS是根據(jù)MTU計(jì)算出來(lái)的驯用,因此當(dāng)發(fā)送的數(shù)據(jù)滿足MSS時(shí),必然滿足MTU儒老。
為了更好的理解蝴乔,我們先介紹一下在5層網(wǎng)絡(luò)模型中應(yīng)用通過(guò)TCP發(fā)送數(shù)據(jù)的流程:
對(duì)于應(yīng)用層來(lái)說(shuō),只關(guān)心發(fā)送的數(shù)據(jù)DATA贷盲,將數(shù)據(jù)寫(xiě)入socket在內(nèi)核中的發(fā)送緩沖區(qū)SO_SNDBUF即返回淘这,操作系統(tǒng)會(huì)將SO_SNDBUF中的數(shù)據(jù)取出來(lái)進(jìn)行發(fā)送。
傳輸層會(huì)在DATA前面加上TCP Header巩剖,構(gòu)成一個(gè)完整的TCP報(bào)文铝穷。
當(dāng)數(shù)據(jù)到達(dá)網(wǎng)絡(luò)層(network layer)時(shí),網(wǎng)絡(luò)層會(huì)在TCP報(bào)文的基礎(chǔ)上再添加一個(gè)IP Header佳魔,也就是將自己的網(wǎng)絡(luò)地址加入到報(bào)文中曙聂。
到數(shù)據(jù)鏈路層時(shí),還會(huì)加上Datalink Header和CRC鞠鲜。
當(dāng)?shù)竭_(dá)物理層時(shí)宁脊,會(huì)將SMAC(Source Machine断国,數(shù)據(jù)發(fā)送方的MAC地址),DMAC(Destination Machine榆苞,數(shù)據(jù)接受方的MAC地址 )和Type域加入稳衬。
可以發(fā)現(xiàn)數(shù)據(jù)在發(fā)送前,每一層都會(huì)在上一層的基礎(chǔ)上增加一些內(nèi)容坐漏,下圖演示了MSS薄疚、MTU在這個(gè)過(guò)程中的作用。
MTU是以太網(wǎng)傳輸數(shù)據(jù)方面的限制赊琳,每個(gè)以太網(wǎng)幀都有最小的大小64bytes最大不能超過(guò)1518bytes街夭。刨去以太網(wǎng)幀的幀頭 (DMAC目的MAC地址48bit=6Bytes+SMAC源MAC地址48bit=6Bytes+Type域2bytes)14Bytes和幀尾 CRC校驗(yàn)部分4Bytes(這個(gè)部分有時(shí)候大家也把它叫做FCS),那么剩下承載上層協(xié)議的地方也就是Data域最大就只能有1500Bytes這個(gè)值 我們就把它稱之為MTU躏筏。
由于MTU限制了一次最多可以發(fā)送1500個(gè)字節(jié)板丽,而TCP協(xié)議在發(fā)送DATA時(shí),還會(huì)加上額外的TCP Header和Ip Header趁尼,因此刨去這兩個(gè)部分埃碱,就是TCP協(xié)議一次可以發(fā)送的實(shí)際應(yīng)用數(shù)據(jù)的最大大小,也就是MSS弱卡。
MSS長(zhǎng)度=MTU長(zhǎng)度-IP Header-TCP Header
TCP Header的長(zhǎng)度是20字節(jié)乃正,IPv4中IP Header長(zhǎng)度是20字節(jié),IPV6中IP Header長(zhǎng)度是40字節(jié)婶博,因此:在IPV4中瓮具,以太網(wǎng)MSS可以達(dá)到1460byte;在IPV6中凡人,以太網(wǎng)MSS可以達(dá)到1440byte名党。
需要注意的是MSS表示的一次可以發(fā)送的DATA的最大長(zhǎng)度,而不是DATA的真實(shí)長(zhǎng)度挠轴。發(fā)送方發(fā)送數(shù)據(jù)時(shí)传睹,當(dāng)SO_SNDBUF中的數(shù)據(jù)量大于MSS時(shí),操作系統(tǒng)會(huì)將數(shù)據(jù)進(jìn)行拆分岸晦,使得每一部分都小于MSS欧啤,這就是拆包,然后每一部分都加上TCP Header启上,構(gòu)成多個(gè)完整的TCP報(bào)文進(jìn)行發(fā)送邢隧,當(dāng)然經(jīng)過(guò)網(wǎng)絡(luò)層和數(shù)據(jù)鏈路層的時(shí)候,還會(huì)分別加上相應(yīng)的內(nèi)容冈在。
需要注意: 默認(rèn)情況下倒慧,與外部通信的網(wǎng)卡的MTU大小是1500個(gè)字節(jié)。而本地回環(huán)地址的MTU大小為65535,這是因?yàn)楸镜販y(cè)試時(shí)數(shù)據(jù)不需要走網(wǎng)卡纫谅,所以不受到1500的限制炫贤。
3、Nagle算法
TCP/IP協(xié)議中付秕,無(wú)論發(fā)送多少數(shù)據(jù)兰珍,總是要在數(shù)據(jù)(DATA)前面加上協(xié)議頭(TCP Header+IP Header),同時(shí)询吴,對(duì)方接收到數(shù)據(jù)俩垃,也需要發(fā)送ACK表示確認(rèn)。
即使從鍵盤(pán)輸入的一個(gè)字符汰寓,占用一個(gè)字節(jié),可能在傳輸上造成41字節(jié)的包苹粟,其中包括1字節(jié)的有用信息和40字節(jié)的首部數(shù)據(jù)有滑。這種情況轉(zhuǎn)變成了4000%的消耗,這樣的情況對(duì)于重負(fù)載的網(wǎng)絡(luò)來(lái)是無(wú)法接受的嵌削。
為了盡可能的利用網(wǎng)絡(luò)帶寬毛好,TCP總是希望盡可能的發(fā)送足夠大的數(shù)據(jù)。(一個(gè)連接會(huì)設(shè)置MSS參數(shù)苛秕,因此肌访,TCP/IP希望每次都能夠以MSS尺寸的數(shù)據(jù)塊來(lái)發(fā)送數(shù)據(jù))。
Nagle算法就是為了盡可能發(fā)送大塊數(shù)據(jù)艇劫,避免網(wǎng)絡(luò)中充斥著許多小數(shù)據(jù)塊吼驶。
Nagle算法的基本定義是任意時(shí)刻,最多只能有一個(gè)未被確認(rèn)的小段店煞。 所謂“小段”蟹演,指的是小于MSS尺寸的數(shù)據(jù)塊,所謂“未被確認(rèn)”顷蟀,是指一個(gè)數(shù)據(jù)塊發(fā)送出去后酒请,沒(méi)有收到對(duì)方發(fā)送的ACK確認(rèn)該數(shù)據(jù)已收到。
Nagle算法的規(guī)則:
- 1鸣个、如果SO_SNDBUF(發(fā)送緩沖區(qū))中的數(shù)據(jù)長(zhǎng)度達(dá)到MSS羞反,則允許發(fā)送;
- 2囤萤、如果該SO_SNDBUF中含有FIN昼窗,表示請(qǐng)求關(guān)閉連接,則先將SO_SNDBUF中的剩余數(shù)據(jù)發(fā)送阁将,再關(guān)閉膏秫;
- 3、設(shè)置了TCP_NODELAY=true選項(xiàng),則允許發(fā)送缤削。TCP_NODELAY是取消TCP的確認(rèn)延遲機(jī)制窘哈,相當(dāng)于禁用了Nagle 算法。
- 4亭敢、未設(shè)置TCP_CORK選項(xiàng)時(shí)滚婉,若所有發(fā)出去的小數(shù)據(jù)包(包長(zhǎng)度小于MSS)均被確認(rèn),則允許發(fā)送;
- 5帅刀、上述條件都未滿足让腹,但發(fā)生了超時(shí)(一般為200ms),則立即發(fā)送扣溺。
三骇窍、通信協(xié)議
在了解了粘包、拆包產(chǎn)生的原因之后锥余,現(xiàn)在來(lái)分析接收方如何對(duì)此進(jìn)行區(qū)分腹纳。道理很簡(jiǎn)單,如果存在不完整的數(shù)據(jù)(拆包)驱犹,則需要繼續(xù)等待數(shù)據(jù)嘲恍,直至可以構(gòu)成一條完整的請(qǐng)求或者響應(yīng)。
通過(guò)定義通信協(xié)議(protocol)雄驹,可以解決粘包佃牛、拆包問(wèn)題。協(xié)議的作用就定義傳輸數(shù)據(jù)的格式医舆。這樣在接受到的數(shù)據(jù)的時(shí)候:
- 如果粘包了俘侠,就可以根據(jù)這個(gè)格式來(lái)區(qū)分不同的包。
- 如果拆包了蔬将,就等待數(shù)據(jù)可以構(gòu)成一個(gè)完整的消息來(lái)處理兼贡。
3.1、定長(zhǎng)協(xié)議
定長(zhǎng)協(xié)議:顧名思義娃胆,就是指定一個(gè)報(bào)文的必須具有固定的長(zhǎng)度遍希。例如,我們規(guī)定每3個(gè)字節(jié)里烦,表示一個(gè)有效報(bào)文凿蒜,如果我們分4次總共發(fā)送以下9個(gè)字節(jié):
+---+----+------+----+
| A | BC | DEFG | HI |
+---+----+------+----+
那么根據(jù)協(xié)議,我們可以判斷出來(lái)胁黑,這里包含了3個(gè)有效的請(qǐng)求報(bào)文废封,如下:
+-----+-----+-----+
| ABC | DEF | GHI |
+-----+-----+-----+
在定長(zhǎng)協(xié)議中:
- 發(fā)送方,必須保證發(fā)送報(bào)文長(zhǎng)度是固定的丧蘸。如果報(bào)文字節(jié)長(zhǎng)度不能滿足條件漂洋,如規(guī)定長(zhǎng)度是1024字節(jié),但是實(shí)際需要發(fā)送的內(nèi)容只有900個(gè)字節(jié),那么不足的部分可以補(bǔ)充0刽漂。因此定長(zhǎng)協(xié)議可能會(huì)浪費(fèi)帶寬演训。
- 接收方,每讀取到固定長(zhǎng)度的內(nèi)容時(shí)贝咙,則認(rèn)為讀取到了一個(gè)完整的報(bào)文样悟。
提示:Netty中提供了FixedLengthFrameDecoder,支持把固定的長(zhǎng)度的字節(jié)數(shù)當(dāng)做一個(gè)完整的消息進(jìn)行解碼庭猩。
3.2窟她、特殊字符分隔符協(xié)議
在包尾部增加回車或者空格符等特殊字符進(jìn)行分割 。例如蔼水,按行解析震糖,遇到字符\n、\r\n的時(shí)候趴腋,就認(rèn)為是一個(gè)完整的數(shù)據(jù)包试伙。對(duì)于以下二進(jìn)制字節(jié)流:
+--------------+
| ABC\nDEF\r\n |
+--------------+
那么根據(jù)協(xié)議,我們可以判斷出來(lái)于样,這里包含了2個(gè)有效的請(qǐng)求報(bào)文
+-----+-----+
| ABC | DEF |
+-----+-----+
在特殊字符分隔符協(xié)議中:
- 發(fā)送方,需要在發(fā)送一個(gè)報(bào)文時(shí)潘靖,需要在報(bào)文尾部添加特殊分割符號(hào)穿剖。
- 接收方,在接收到報(bào)文時(shí)卦溢,需要對(duì)特殊分隔符進(jìn)行檢測(cè)糊余,直到檢測(cè)到一個(gè)完整的報(bào)文時(shí),才能進(jìn)行處理单寂。
在使用特殊字符分隔符協(xié)議的時(shí)候贬芥,需要注意的是,我們選擇的特殊字符宣决,一定不能在消息體中出現(xiàn)蘸劈,否則可能會(huì)出現(xiàn)錯(cuò)誤的拆包。例如尊沸,發(fā)送方希望把”12\r\n34”威沫,當(dāng)成一個(gè)完整的報(bào)文,如果是按行拆分洼专,那么就會(huì)錯(cuò)誤的拆分為2個(gè)報(bào)文棒掠。一種解決策略是,發(fā)送方對(duì)需要發(fā)送的內(nèi)容預(yù)先進(jìn)行base64編碼屁商,由于base64編碼只包含64個(gè)字符:0-9烟很、a-z、A-Z、+雾袱、/恤筛,我們可以選擇這64個(gè)字符之外的特殊字符作為分隔符。
提示:netty中提供了DelimiterBasedFrameDecoder根據(jù)特殊字符進(jìn)行解碼谜酒。事實(shí)上叹俏,我們熟悉的的緩存服務(wù)器redis,也是通過(guò)換行符來(lái)區(qū)分一個(gè)完整的報(bào)文僻族。
3.3粘驰、變長(zhǎng)協(xié)議
將消息區(qū)分為消息頭和消息體述么,在消息頭中蝌数,我們使用一個(gè)整形數(shù)字,例如一個(gè)int度秘,來(lái)表示消息體的長(zhǎng)度顶伞。而消息體實(shí)際實(shí)際要發(fā)送的二進(jìn)制數(shù)據(jù)字節(jié)。以下是一個(gè)基本格式:
header body
+--------+----------+
| Length | Content |
+--------+----------+
在變長(zhǎng)協(xié)議中:
- 發(fā)送方剑梳,發(fā)送數(shù)據(jù)之前唆貌,需要先獲取需要發(fā)送內(nèi)容的二進(jìn)制字節(jié)大小,然后在需要發(fā)送的內(nèi)容前面添加一個(gè)整數(shù)垢乙,表示消息體二進(jìn)制字節(jié)的長(zhǎng)度锨咙。
- 接收方,在解析時(shí)追逮,先讀取內(nèi)容長(zhǎng)度Length酪刀,其值為實(shí)際消息體內(nèi)容(Content)占用的字節(jié)數(shù),之后必須讀取到這么多字節(jié)的內(nèi)容钮孵,才認(rèn)為是一個(gè)完整的數(shù)據(jù)報(bào)文骂倘。
提示:Netty中提供了LengthFieldPrepender給實(shí)際內(nèi)容Content進(jìn)行編碼添加Length字段,接受方使用LengthFieldBasedFrameDecoder解碼巴席。
3.4历涝、序列化
序列化本質(zhì)上已經(jīng)不是為了解決粘包和拆包問(wèn)題,而是為了在網(wǎng)絡(luò)開(kāi)發(fā)中可以更加的便捷漾唉。在變長(zhǎng)協(xié)議中睬关,我們看到可以在實(shí)際要發(fā)送的數(shù)據(jù)之前加上一個(gè)length字段,表示實(shí)際要發(fā)送的數(shù)據(jù)的長(zhǎng)度毡证。這實(shí)際上給我們了一個(gè)很好的思路电爹,我們完全可以將一個(gè)對(duì)象轉(zhuǎn)換成二進(jìn)制字節(jié),來(lái)進(jìn)行通信料睛,例如使用一個(gè)Request對(duì)象表示請(qǐng)求丐箩,使用一個(gè)Response對(duì)象表示響應(yīng)摇邦。
序列化框架有很多種,我們?cè)谶x擇時(shí)屎勘,主要考慮序列化/反序列化的速度施籍,序列化占用的體積,多語(yǔ)言支持等概漱。下面列出了業(yè)界流行的序列化框架:
提示:xml丑慎、json也屬于序列化框架的范疇,上面的表格中并沒(méi)有列出瓤摧。
一些網(wǎng)絡(luò)通信的RPC框架通常會(huì)支持多種序列化方式竿裂,例如dubbo支持hessian、json照弥、kyro腻异、fst等。在支持多種序列化框架的情況下这揣,在協(xié)議中通常需要有一個(gè)字段來(lái)表示序列化的類型悔常,例如,我們可以將上述變長(zhǎng)協(xié)議的格式改造為:
+--------+-------------+------------+
| Length | serializer | Content |
+--------+-------------+------------+
這里使用1個(gè)字節(jié)表示Serializer的值给赞,使用不同的值代表不同的框架机打。
發(fā)送方,選擇好序列化框架后編碼后片迅,需要指定Serializer字段的值残邀。
接收方,在解碼時(shí)障涯,根據(jù)Serializer的值選擇對(duì)應(yīng)的框架進(jìn)行反序列化。
3.5膳汪、壓縮
通常唯蝶,為了節(jié)省網(wǎng)絡(luò)開(kāi)銷,在網(wǎng)絡(luò)通信時(shí)遗嗽,可以考慮對(duì)數(shù)據(jù)進(jìn)行壓縮粘我。常見(jiàn)的壓縮算法有l(wèi)z4、snappy痹换、gzip等征字。在選擇壓縮算法時(shí),我們主要考慮壓縮比以及解壓縮的效率娇豫。
我們可以在網(wǎng)絡(luò)通信協(xié)議中匙姜,添加一個(gè)compress字段,表示采用的壓縮算法:
+--------+-----------+------------+------------+
| Length | serializer| compress | Content |
+--------+-----------+------------+------------+
通常冯痢,我們沒(méi)有必要使用一個(gè)字節(jié)氮昧,來(lái)表示采用的壓縮算法框杜,1個(gè)字節(jié)可以標(biāo)識(shí)256種可能情況,而常用壓縮算法也就那么幾種袖肥,因此通常只需要使用2~3個(gè)bit來(lái)表示采用的壓縮算法即可咪辱。
另外,由于數(shù)據(jù)量比較小的時(shí)候椎组,壓縮比并不會(huì)太高油狂,沒(méi)有必要對(duì)所有發(fā)送的數(shù)據(jù)都進(jìn)行壓縮,只有再超過(guò)一定大小的情況下寸癌,才考慮進(jìn)行壓縮专筷。如rocketmq,producer在發(fā)送消息時(shí)灵份,默認(rèn)消息大小超過(guò)4k仁堪,才會(huì)進(jìn)行壓縮。因此填渠,compress字段弦聂,應(yīng)該有一個(gè)值,表示沒(méi)有使用任何壓縮算法氛什,例如使用0莺葫。
3.6、查錯(cuò)校驗(yàn)碼
一些通信協(xié)議傳輸?shù)臄?shù)據(jù)中枪眉,還包含了查錯(cuò)校驗(yàn)碼捺檬。典型的算法如CRC32、Adler32等贸铜。java對(duì)這兩種校驗(yàn)方式都提供了支持堡纬,java.util.zip.Adler32、java.util.zip.CRC32蒿秦。
+--------+-----------+------------+------------+---------+
| Length | serializer| compress | Content | CRC32 |
+--------+-----------+------------+------------+---------+
這里并不對(duì)CRC32烤镐、Adler32進(jìn)行詳細(xì)說(shuō)明,主要是考慮棍鳖,為什么需要進(jìn)行校驗(yàn)?
有人說(shuō)是因?yàn)榭紤]到安全炮叶,這個(gè)理由似乎并不充分,因?yàn)槲覀円呀?jīng)有了TLS層的加密渡处,CRC32镜悉、Adler32的作用不應(yīng)該是為了考慮安全。
一位同事的觀點(diǎn)医瘫,我非常贊同:二進(jìn)制數(shù)據(jù)在傳輸?shù)倪^(guò)程中侣肄,可能因?yàn)殡姶鸥蓴_,導(dǎo)致一個(gè)高電平變成低電平醇份,或者低電平變成高電平茫孔。這種情況下叮喳,數(shù)據(jù)相當(dāng)于受到了污染,此時(shí)通過(guò)CRC32等校驗(yàn)值缰贝,則可以驗(yàn)證數(shù)據(jù)的正確性馍悟。
另外,通常校驗(yàn)機(jī)制在通信協(xié)議中剩晴,是可選的配置的锣咒,并不需要強(qiáng)制開(kāi)啟,其雖然可以保證數(shù)據(jù)的正確赞弥,但是計(jì)算校驗(yàn)值也會(huì)帶來(lái)一些額外的性能損失毅整。如Mysql主從同步,雖然高版本默認(rèn)開(kāi)啟CRC32校驗(yàn)绽左,但是也可以通過(guò)配置禁用悼嫉。
小結(jié)
本節(jié)通過(guò)一些基本的案例,講解了在TCP編程中拼窥,如何通過(guò)協(xié)議來(lái)解決粘包戏蔑、拆包問(wèn)題。在實(shí)際開(kāi)發(fā)中鲁纠,通常我們的協(xié)議會(huì)更加復(fù)雜总棵。例如,一些RPC框架改含,會(huì)在協(xié)議中添加唯一標(biāo)識(shí)一個(gè)請(qǐng)求的ID情龄,一些支持雙向通信的RPC框架,如sofa-bolt捍壤,還會(huì)添加一個(gè)方向信息等骤视。當(dāng)然,所謂復(fù)雜专酗,無(wú)非是在協(xié)議中添加了某個(gè)字段用于某個(gè)用途唯卖,只要弄清楚這些字段的含義,也就不復(fù)雜了颠锉。
參考:
https://www.cnblogs.com/Leo_wl/p/10297113.html
https://www.cnblogs.com/sidesky/p/6913109.html
https://blog.csdn.net/u022812849/article/details/107254239