一、TCP Out of Order 簡(jiǎn)介
TCP Out of Order,中文翻譯為 TCP亂序,指的是 TCP 數(shù)據(jù)包在傳輸過(guò)程中,出現(xiàn)了被接收端亂序接收的現(xiàn)象。例如,A 發(fā)送了 1、2、3 三個(gè)數(shù)據(jù)包,但是接收端收到的順序是 1、3、2,這就是 TCP Out of Order 問(wèn)題。
TCP Out of Order 問(wèn)題通常出現(xiàn)在高延遲、高丟包的網(wǎng)絡(luò)中,也常常成為 TCP 傳輸性能瓶頸的一個(gè)關(guān)鍵因素。
二、TCP Out of Order 原理
TCP 傳輸是一種可靠傳輸協(xié)議,其通過(guò)基于窗口的流量控制、擁塞控制、錯(cuò)誤校驗(yàn)等機(jī)制保證數(shù)據(jù)的可靠傳輸。在傳輸過(guò)程中,TCP 通過(guò)使用序列號(hào)來(lái)區(qū)分每一個(gè)發(fā)送的數(shù)據(jù)包,TCP 接收端通過(guò)序列號(hào)來(lái)確保數(shù)據(jù)包信息的組裝。
當(dāng) TCP 數(shù)據(jù)包在傳輸過(guò)程中,發(fā)生了丟包、重復(fù)收包等問(wèn)題,TCP 接收端就需要重新組裝數(shù)據(jù)包,這可能導(dǎo)致部分?jǐn)?shù)據(jù)需要重新排序,造成 TCP Out of Order 問(wèn)題。
三、TCP Out of Order 診斷與分析
TCP Out of Order 問(wèn)題診斷的主要方法是通過(guò)網(wǎng)絡(luò)抓包進(jìn)行分析。常用工具有 wireshark、tcpdump 等。
對(duì)于 TCP Out of Order 問(wèn)題的分析,首先需要確認(rèn)網(wǎng)絡(luò)傳輸質(zhì)量,檢查網(wǎng)絡(luò)鏈路是否存在丟包、重傳等問(wèn)題。同時(shí),還需要檢查服務(wù)端和客戶端的配置是否存在問(wèn)題,如 MTU 等參數(shù)的設(shè)置是否正確,防火墻是否屏蔽了某些 TCP 數(shù)據(jù)包等。
如果網(wǎng)絡(luò)問(wèn)題都排除了,那么就需要深入分析 TCP 流量數(shù)據(jù)包,確認(rèn)是否存在 TCP Out of Order 問(wèn)題。當(dāng)然,如果需要做一些針對(duì)性優(yōu)化,也可以基于抓包分析得出結(jié)論。
四、TCP Out of Order 優(yōu)化
對(duì)于 TCP Out of Order 問(wèn)題,最根本的解決方案是優(yōu)化網(wǎng)絡(luò)性能,降低網(wǎng)絡(luò)延遲和丟包率,但是這一方面通常不太好做。除此之外,還可以從優(yōu)化 TCP TImeout 等參數(shù)入手,改善 TCP 數(shù)據(jù)包傳輸。
常見的 TCP Out of Order 優(yōu)化手段包括:
1、優(yōu)化 TCP TimeOut 參數(shù)。
2、禁用 SACK
3、調(diào)整 TCP 窗口大小
4、使用更快速的 TCP 協(xié)議棧等。
五、代碼示例
// C++ 代碼示例
// 檢測(cè) TCP Out of Order 的函數(shù)實(shí)現(xiàn)
bool checkTcpOutOfOrder(const char* packet_data, uint32_t packet_data_length) {
// 已組裝數(shù)據(jù)包序列號(hào)
uint32_t last_packet_seq_num = 0;
// 需要組裝的下一個(gè)數(shù)據(jù)包序列號(hào)
uint32_t expected_seq_num = 0;
for (uint32_t i = 0; i < packet_data_length; i += TCP_HEADER_LEN + PAYLOAD_LEN) {
// 解析 TCP 數(shù)據(jù)包頭
tcp_header_t* tcp_hdr = (tcp_header_t*)(packet_data + i);
// 計(jì)算序列號(hào)
uint32_t seq_num = ntohl(tcp_hdr.seq_num);
// 當(dāng)當(dāng)前序列號(hào)小于已組裝數(shù)據(jù)包序列號(hào)時(shí),說(shuō)明該數(shù)據(jù)包已經(jīng)被組裝,濾過(guò)
if (seq_num < last_packet_seq_num) {
continue;
}
// 如果序列號(hào)與期望的序列號(hào)不同,說(shuō)明出現(xiàn)亂序
if (seq_num != expected_seq_num) {
return true;
}
// 更新已組裝數(shù)據(jù)包序列號(hào),以及需要組裝的下一個(gè)數(shù)據(jù)包的期望序列號(hào)
last_packet_seq_num = seq_num;
expected_seq_num += PAYLOAD_LEN;
}
// 數(shù)據(jù)包序列號(hào)不連續(xù),數(shù)據(jù)包出現(xiàn)亂序
return false;
}