上篇分析了LengthFieldBasedFrameDecoder,只需要簡(jiǎn)單靈活的指定幾個(gè)參數(shù)就能滿足多種情況下的拆包處理,將服務(wù)端接收到的二進(jìn)制流基于長(zhǎng)度前綴法解析為一個(gè)個(gè)的ByteBuf消息體。
但是請(qǐng)?jiān)O(shè)想一下異常的情況:如果網(wǎng)絡(luò)傳輸過(guò)程中出現(xiàn)丟幀或者長(zhǎng)度域中某個(gè)位在模數(shù)轉(zhuǎn)換時(shí)出現(xiàn)了意外沒有得到正確轉(zhuǎn)換,是否意味著所有的消息體都無(wú)法再得到正確的解析了呢?本篇我們就來(lái)聊聊TCP協(xié)議的可靠性保證。
溫故知新,首先我們回憶一下TCP報(bào)文結(jié)構(gòu):


16 位源端口號(hào)和 16 位目的端口號(hào):端口號(hào)通常也稱為進(jìn)程地址。網(wǎng)絡(luò)中的兩個(gè)終端通信,說(shuō)到底其實(shí)是兩個(gè)終端上的各自的進(jìn)程在通信,通過(guò)端口號(hào)才能區(qū)分響應(yīng)的數(shù)據(jù)包來(lái)自遠(yuǎn)端的哪一個(gè),以及需要發(fā)送到本機(jī)的哪個(gè)進(jìn)程來(lái)處理。
32 位序號(hào):序號(hào)用來(lái)標(biāo)識(shí)從 TCP 發(fā)送端向 TCP 接收端發(fā)送的數(shù)據(jù)字節(jié)流。
32 位確認(rèn)序列號(hào):表示一個(gè)準(zhǔn)備接收包的序列號(hào)。
4 位首部長(zhǎng)度:首部長(zhǎng)度指出了首部中 32 bit 字的數(shù)目,正常的 TCP 首部長(zhǎng)度是 20 字節(jié)。
6 個(gè)標(biāo)志字段:URG 緊急指針;ACK 確認(rèn)序號(hào);PSH 推標(biāo)志;RST 重建連接;SYN 同步序號(hào);
FIN 結(jié)束標(biāo)志。
16 位窗口:TCP 的流量控制由連接的每一端通過(guò)聲明的滑動(dòng)窗口大小來(lái)提供,窗口大小為字節(jié)數(shù)。
16 位檢驗(yàn)和:檢驗(yàn)和字段覆蓋了 TCP 首部和 TCP 數(shù)據(jù)。TCP 檢驗(yàn)和的計(jì)算方法和 UDP 檢驗(yàn)和的
計(jì)算方法一樣,計(jì)算時(shí)需要考慮偽報(bào)頭。
16 位緊急指針:URG 標(biāo)志置 1 時(shí)緊急指針才有效。
可以看出TCP通過(guò)多種方式來(lái)保證消息的可靠性:
1、檢驗(yàn)和:保證接收到字節(jié)流未出現(xiàn)模數(shù)轉(zhuǎn)換異常
2、序號(hào)和確認(rèn)序列號(hào):發(fā)送方對(duì)每個(gè)字節(jié)進(jìn)行編號(hào),接收方對(duì)接收的數(shù)據(jù)包進(jìn)行序號(hào)確認(rèn),超時(shí)后發(fā)送方會(huì)重發(fā)。
3、滑動(dòng)窗口大?。和ㄟ^(guò)設(shè)置滑動(dòng)窗口大小表示自身socket緩沖區(qū)的大小,防止緩沖區(qū)滿后發(fā)送方仍然不停發(fā)數(shù)據(jù)導(dǎo)致的丟包。
tcpdump是一款強(qiáng)大的命令,通過(guò)tcpdump可以很方便的排查出網(wǎng)絡(luò)連接中的一些故障問(wèn)題。下面是我在本機(jī)和es服務(wù)通信的一段抓包信息:
[root@localhost ~]# tcpdump -i any -c 100 host 192.168.1.77 and tcp and port 5601
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on any, link-type LINUX_SLL (Linux cooked), capture size 262144 bytes
20:13:30.311360 IP 192.168.1.77.50316 > 192.168.1.99.esmagent: Flags [P.], seq 2903948008:2903948424, ack 1028358340, win 255, length 416
20:13:30.321104 IP 192.168.1.99.esmagent > 192.168.1.77.50316: Flags [P.], seq 1:274, ack 416, win 190, length 273
20:13:30.321383 IP 192.168.1.99.esmagent > 192.168.1.77.50316: Flags [.], seq 274:3194, ack 416, win 190, length 2920
20:13:30.321395 IP 192.168.1.99.esmagent > 192.168.1.77.50316: Flags [.], seq 3194:6114, ack 416, win 190, length 2920
20:13:30.321399 IP 192.168.1.99.esmagent > 192.168.1.77.50316: Flags [.], seq 6114:9034, ack 416, win 190, length 2920
20:13:30.321404 IP 192.168.1.99.esmagent > 192.168.1.77.50316: Flags [.], seq 9034:11954, ack 416, win 190, length 2920
20:13:30.322488 IP 192.168.1.77.50316 > 192.168.1.99.esmagent: Flags [.], ack 3194, win 256, length 0
20:13:30.322502 IP 192.168.1.99.esmagent > 192.168.1.77.50316: Flags [.], seq 11954:13414, ack 416, win 190, length 1460
20:13:30.322508 IP 192.168.1.99.esmagent > 192.168.1.77.50316: Flags [P.], seq 13414:13528, ack 416, win 190, length 114
20:13:30.322561 IP 192.168.1.77.50316 > 192.168.1.99.esmagent: Flags [.], ack 6114, win 256, length 0
20:13:30.323577 IP 192.168.1.77.50316 > 192.168.1.99.esmagent: Flags [.], ack 9034, win 256, length 0
20:13:30.323654 IP 192.168.1.77.50316 > 192.168.1.99.esmagent: Flags [.], ack 11954, win 256, length 0
20:13:30.323658 IP 192.168.1.77.50316 > 192.168.1.99.esmagent: Flags [.], ack 13528, win 256, length 0
20:13:30.338768 IP 192.168.1.77.50316 > 192.168.1.99.esmagent: Flags [P.], seq 416:921, ack 13528, win 256, length 505
20:13:30.339477 IP 192.168.1.99.esmagent > 192.168.1.77.50316: Flags [P.], seq 13528:13838, ack 921, win 198, length 310
20:13:30.346640 IP 192.168.1.77.50316 > 192.168.1.99.esmagent: Flags [P.], seq 921:1397, ack 13838, win 255, length 476
本次tcpdump抓包過(guò)濾規(guī)則:
ip為192.168.1.77,tcp協(xié)議,端口為5601的前100個(gè)數(shù)據(jù)包。
可以清晰的看到,es服務(wù)在接收到請(qǐng)求數(shù)據(jù)包后,連續(xù)發(fā)送了5個(gè)數(shù)據(jù)包,分別為:
seq 1:274, ack 416, win 190, length 273
seq 274:3194, ack 416, win 190, length 2920
seq 3194:6114, ack 416, win 190, length 2920
seq 6114:9034, ack 416, win 190, length 2920
seq 9034:11954, ack 416, win 190, length 2920
而接收方在接收到數(shù)據(jù)包后依次發(fā)送了確認(rèn)包:
ack 3194, win 256, length 0
ack 6114, win 256, length 0
ack 9034, win 256, length 0
ack 11954, win 256, length 0
可以看到接收方并不需要對(duì)每一個(gè)數(shù)據(jù)包進(jìn)行確認(rèn),ack 3194代表接收方對(duì)3194之前的數(shù)據(jù)包都進(jìn)行了確認(rèn)。
嗯,至此算是對(duì)本篇開頭的疑問(wèn)進(jìn)行了解答。
圖片來(lái)自參考資料:
《網(wǎng)絡(luò)協(xié)議分析及應(yīng)用實(shí)驗(yàn)教程》
轉(zhuǎn)載請(qǐng)備注原文鏈接。