TCP 就沒什么缺陷嗎?
大家好,我是小林。
我寫過最多題材的文章就是 TCP 了:
寫的多了后,忽然思考一個(gè)問題,TCP 通過序列號(hào)、確認(rèn)應(yīng)答、超時(shí)重傳、流量控制、擁塞控制等方式實(shí)現(xiàn)了可靠傳輸,看起來它很完美,事實(shí)真的是這樣嗎?TCP 就沒什么缺陷嗎?
所以,今天就跟大家聊聊,TCP 協(xié)議有哪些缺陷?主要有四個(gè)方面:
- 升級(jí) TCP 的工作很困難;
- TCP 建立連接的延遲;
- TCP 存在隊(duì)頭阻塞問題;
- 網(wǎng)絡(luò)遷移需要重新建立 TCP 連接;
接下來,針對(duì)這四個(gè)方面詳細(xì)說一下。
升級(jí) TCP 的工作很困難
TCP 協(xié)議是誕生在 1973 年,至今 TCP 協(xié)議依然還在實(shí)現(xiàn)很多的新特性。
但是 TCP 協(xié)議是在內(nèi)核中實(shí)現(xiàn)的,應(yīng)用程序只能使用不能修改,如果要想升級(jí) TCP 協(xié)議,那么只能升級(jí)內(nèi)核。
而升級(jí)內(nèi)核這個(gè)工作是很麻煩的事情,麻煩的事情不是說升級(jí)內(nèi)核這個(gè)操作很麻煩,而是由于內(nèi)核升級(jí)涉及到底層軟件和運(yùn)行庫(kù)的更新,我們的服務(wù)程序就需要回歸測(cè)試是否兼容新的內(nèi)核版本,所以服務(wù)器的內(nèi)核升級(jí)比較保守和緩慢。
很多 TCP 協(xié)議的新特性,都是需要客戶端和服務(wù)端同時(shí)支持才能生效的,比如 TCP Fast Open 這個(gè)特性,雖然在 2013 年就被提出了,但是由于因?yàn)楹芏?PC 端的系統(tǒng)升級(jí)滯后很嚴(yán)重,所以一些老舊的系統(tǒng)是無法支持這個(gè)特性的。
所以,即使 TCP 有比較好的特性更新,也很難快速推廣,用戶往往要幾年或者十年才能體驗(yàn)到。
TCP 建立連接的延遲
基于 TCP 實(shí)現(xiàn)的應(yīng)用協(xié)議,都是需要先建立三次握手才能進(jìn)行數(shù)據(jù)傳輸,比如 HTTP 1.0/1.1、HTTP/2、HTTPS。
現(xiàn)在大多數(shù)網(wǎng)站都是使用 HTTPS 的,這意味著在 TCP 三次握手之后,還需要經(jīng)過 TLS 四次握手后,才能進(jìn)行 HTTP 數(shù)據(jù)的傳輸,這在一定程序上增加了數(shù)據(jù)傳輸?shù)难舆t。
TCP 三次握手和 TLS 握手延遲,如圖:
TCP 三次握手的延遲被 TCP Fast Open (快速打開)這個(gè)特性解決了,這個(gè)特性可以在「第二次建立連接」時(shí)減少 TCP 連接建立的時(shí)延。
常規(guī) HTTP 請(qǐng)求 與 Fast Open HTTP 請(qǐng)求
過程如下:
- 在第一次建立連接的時(shí)候,服務(wù)端在第二次握手產(chǎn)生一個(gè) Cookie (已加密)并通過 SYN、ACK 包一起發(fā)給客戶端,于是客戶端就會(huì)緩存這個(gè) Cookie,所以第一次發(fā)起 HTTP Get 請(qǐng)求的時(shí)候,還是需要 2 個(gè) RTT 的時(shí)延;
- 在下次請(qǐng)求的時(shí)候,客戶端在 SYN 包帶上 Cookie 發(fā)給服務(wù)端,就提前可以跳過三次握手的過程,因?yàn)?Cookie 中維護(hù)了一些信息,服務(wù)端可以從 Cookie 獲取 TCP 相關(guān)的信息,這時(shí)發(fā)起的 HTTP GET 請(qǐng)求就只需要 1 個(gè) RTT 的時(shí)延;
TCP Fast Open 這個(gè)特性是不錯(cuò),但是它需要服務(wù)端和客戶端的操作系統(tǒng)同時(shí)支持才能體驗(yàn)到,而 TCP Fast Open 是在 2013 年提出的,所以市面上依然有很多老式的操作系統(tǒng)不支持,而升級(jí)操作系統(tǒng)是很麻煩的事情,因此 TCP Fast Open 很難被普及開來。
還有一點(diǎn),針對(duì) HTTPS 來說,TLS 是在應(yīng)用層實(shí)現(xiàn)的握手,而 TCP 是在內(nèi)核實(shí)現(xiàn)的握手,這兩個(gè)握手過程是無法結(jié)合在一起的,總是得先完成 TCP 握手,才能進(jìn)行 TLS 握手。
也正是 TCP 是在內(nèi)核實(shí)現(xiàn)的,所以 TLS 是無法對(duì) TCP 頭部加密的,這意味著 TCP 的序列號(hào)都是明文傳輸,所以就存安全的問題。
一個(gè)典型的例子就是攻擊者偽造一個(gè)的 RST 報(bào)文強(qiáng)制關(guān)閉一條 TCP 連接,而攻擊成功的關(guān)鍵則是 TCP 字段里的序列號(hào)位于接收方的滑動(dòng)窗口內(nèi),該報(bào)文就是合法的。
為此 TCP 也不得不進(jìn)行三次握手來同步各自的序列號(hào),而且初始化序列號(hào)時(shí)是采用隨機(jī)的方式(不完全隨機(jī),而是隨著時(shí)間流逝而線性增長(zhǎng),到了 2^32 盡頭再回滾)來提升攻擊者猜測(cè)序列號(hào)的難度,以增加安全性。
但是這種方式只能避免攻擊者預(yù)測(cè)出合法的 RST 報(bào)文,而無法避免攻擊者截獲客戶端的報(bào)文,然后中途偽造出合法 RST 報(bào)文的攻擊的方式。
大膽想一下,如果 TCP 的序列號(hào)也能被加密,或許真的不需要三次握手了,客戶端和服務(wù)端的初始序列號(hào)都從 0 開始,也就不用做同步序列號(hào)的工作了,但是要實(shí)現(xiàn)這個(gè)要改造整個(gè)協(xié)議棧,太過于麻煩,即使實(shí)現(xiàn)出來了,很多老的網(wǎng)絡(luò)設(shè)備未必能兼容。
TCP 存在隊(duì)頭阻塞問題
TCP 是字節(jié)流協(xié)議,TCP 層必須保證收到的字節(jié)數(shù)據(jù)是完整且有序的,如果序列號(hào)較低的 TCP 段在網(wǎng)絡(luò)傳輸中丟失了,即使序列號(hào)較高的 TCP 段已經(jīng)被接收了,應(yīng)用層也無法從內(nèi)核中讀取到這部分?jǐn)?shù)據(jù)。如下圖:
圖中發(fā)送方發(fā)送了很多個(gè) packet,每個(gè) packet 都有自己的序號(hào),你可以認(rèn)為是 TCP 的序列號(hào),其中 packet #3 在網(wǎng)絡(luò)中丟失了,即使 packet #4-6 被接收方收到后,由于內(nèi)核中的 TCP 數(shù)據(jù)不是連續(xù)的,于是接收方的應(yīng)用層就無法從內(nèi)核中讀取到,只有等到 packet #3 重傳后,接收方的應(yīng)用層才可以從內(nèi)核中讀取到數(shù)據(jù)。
這就是 TCP 隊(duì)頭阻塞問題,但這也不能怪 TCP ,因?yàn)橹挥羞@樣做才能保證數(shù)據(jù)的有序性。
HTTP/2 多個(gè)請(qǐng)求是跑在一個(gè) TCP 連接中的,那么當(dāng) TCP 丟包時(shí),整個(gè) TCP 都要等待重傳,那么就會(huì)阻塞該 TCP 連接中的所有請(qǐng)求,所以 HTTP/2 隊(duì)頭阻塞問題就是因?yàn)?TCP 協(xié)議導(dǎo)致的。
網(wǎng)絡(luò)遷移需要重新建立 TCP 連接
基于 TCP 傳輸協(xié)議的 HTTP 協(xié)議,由于是通過四元組(源 IP、源端口、目的 IP、目的端口)確定一條 TCP 連接。
那么當(dāng)移動(dòng)設(shè)備的網(wǎng)絡(luò)從 4G 切換到 WIFI 時(shí),意味著 IP 地址變化了,那么就必須要斷開連接,然后重新建立 TCP 連接。
而建立連接的過程包含 TCP 三次握手和 TLS 四次握手的時(shí)延,以及 TCP 慢啟動(dòng)的減速過程,給用戶的感覺就是網(wǎng)絡(luò)突然卡頓了一下,因此連接的遷移成本是很高的。
結(jié)尾
我記得之前在群里看到,有位讀者字節(jié)一面的時(shí)候被問到:「如何基于 UDP 協(xié)議實(shí)現(xiàn)可靠傳輸?」
很多同學(xué)第一反應(yīng)就會(huì)說把 TCP 可靠傳輸?shù)奶匦?序列號(hào)、確認(rèn)應(yīng)答、超時(shí)重傳、流量控制、擁塞控制)在應(yīng)用層實(shí)現(xiàn)一遍。
實(shí)現(xiàn)的思路確實(shí)這樣沒錯(cuò),但是有沒有想過,既然 TCP 天然支持可靠傳輸,為什么還需要基于 UDP 實(shí)現(xiàn)可靠傳輸呢?這不是重復(fù)造輪子嗎?
所以,我們要先弄清楚 TCP 協(xié)議有哪些痛點(diǎn)?而這些痛點(diǎn)是否可以在基于 UDP 協(xié)議實(shí)現(xiàn)的可靠傳輸協(xié)議中得到改進(jìn)?
現(xiàn)在市面上已經(jīng)有基于 UDP 實(shí)現(xiàn)的可靠傳輸協(xié)議的成熟方案了,那就是 QUIC 協(xié)議,QUIC 協(xié)議把我本文說的 TCP 的缺點(diǎn)都給解決了,而且已經(jīng)應(yīng)用在了 HTTP/3。
所以,下次再聊聊 QUIC 協(xié)議是怎么實(shí)現(xiàn)可靠傳輸?shù)摹?/p>