有沒(méi)有做數(shù)學(xué)題掙錢(qián)的網(wǎng)站艾滋病多久可以查出來(lái)
一、套接字Socket
基于 TCP UDP 協(xié)議的 Socket 編程,在講 TCP 和 UDP 協(xié)議的時(shí)候,我們分客戶端和服務(wù)端,在寫(xiě)程序的時(shí)候,我們也同樣這樣分。
在網(wǎng)絡(luò)層,Socket 函數(shù)需要指定到底是 IPv4 還是 IPv6,分別對(duì)應(yīng)設(shè)置為 AF_INET 和 AF_INET6。另外,還要指定到底是 TCP 還是 UDP。還記得咱們前面講過(guò)的,TCP 協(xié)議是基于數(shù)據(jù)流的,所以設(shè)置為 SOCK_STREAM,而 UDP 是基于數(shù)據(jù)報(bào)的,因而設(shè)置為 SOCK_DGRAM。
監(jiān)聽(tīng)的 Socket 和真正用來(lái)傳數(shù)據(jù)的 Socket 是兩個(gè),一個(gè)叫作監(jiān)聽(tīng) Socket,一個(gè)叫作已連接 Socket
比喻:接待客人
想象你在家里準(zhǔn)備接待客人,你有一個(gè)門(mén)鈴和一個(gè)客廳。門(mén)鈴相當(dāng)于監(jiān)聽(tīng) Socket,而客廳相當(dāng)于已連接 Socket。
-
監(jiān)聽(tīng) Socket(門(mén)鈴):
- 你家的門(mén)鈴一直在等待有人按下它,這就像服務(wù)器上的監(jiān)聽(tīng) Socket 一直在等待新的連接請(qǐng)求。
- 當(dāng)有人按下門(mén)鈴時(shí),你知道有客人到訪了,但你還不知道是誰(shuí),也還沒(méi)有開(kāi)始與客人交談。
-
已連接 Socket(客廳):
- 當(dāng)你開(kāi)門(mén)迎接客人,并帶他們到客廳后,你就開(kāi)始與客人交流了,這時(shí)候的客人就相當(dāng)于已連接 Socket。
- 在客廳里,你可以與每個(gè)客人進(jìn)行獨(dú)立的對(duì)話,不會(huì)相互干擾。
技術(shù)解釋
在網(wǎng)絡(luò)編程中,特別是 TCP 服務(wù)器程序中,監(jiān)聽(tīng) Socket 和已連接 Socket 是兩個(gè)不同的概念和用途:
-
監(jiān)聽(tīng) Socket:
- 作用:用來(lái)監(jiān)聽(tīng)和接受新的連接請(qǐng)求。
- 創(chuàng)建:在服務(wù)器啟動(dòng)時(shí)創(chuàng)建,并綁定到特定的 IP 地址和端口。
- 工作方式:服務(wù)器調(diào)用
listen()
方法,使這個(gè) Socket 進(jìn)入監(jiān)聽(tīng)狀態(tài),等待客戶端的連接請(qǐng)求。當(dāng)有客戶端請(qǐng)求連接時(shí),服務(wù)器調(diào)用accept()
方法,從監(jiān)聽(tīng) Socket 接受連接請(qǐng)求。
-
已連接 Socket:
- 作用:用來(lái)與客戶端進(jìn)行實(shí)際的數(shù)據(jù)傳輸。
- 創(chuàng)建:當(dāng)服務(wù)器調(diào)用
accept()
方法并成功接收一個(gè)客戶端連接后,會(huì)生成一個(gè)新的已連接 Socket。這個(gè) Socket 專門(mén)用于與該客戶端進(jìn)行通信。 - 工作方式:服務(wù)器使用這個(gè)已連接 Socket 調(diào)用
send()
和recv()
方法,與客戶端交換數(shù)據(jù)。
基于 TCP 協(xié)議的 Socket 程序函數(shù)調(diào)用過(guò)程
write() 和 read():適用于所有類型的文件描述符,包括 Socket,功能簡(jiǎn)單直接。
send() 和 recv():專為網(wǎng)絡(luò) Socket 設(shè)計(jì),提供額外的功能和靈活性,通過(guò) flags 參數(shù)控制行為。
基于 UDP 協(xié)議的 Socket 程序函數(shù)調(diào)用過(guò)程
對(duì)于 UDP 來(lái)講,過(guò)程有些不一樣。UDP 是沒(méi)有連接的,所以不需要三次握手,也就不需要調(diào)用 listen 和 connect,但是,UDP 的的交互仍然需要 IP 和端口號(hào),因而也需要 bind。UDP 是沒(méi)有維護(hù)連接狀態(tài)的,因而不需要每對(duì)連接建立一組 Socket,而是只要有一個(gè) Socket,就能夠和多個(gè)客戶端通信。也正是因?yàn)闆](méi)有連接狀態(tài),每次通信的時(shí)候,都調(diào)用 sendto 和 recvfrom,都可以傳入 IP 地址和端口。
服務(wù)器如何接更多的項(xiàng)目?
會(huì)了這幾個(gè)基本的 Socket 函數(shù)之后,你就可以輕松地寫(xiě)一個(gè)網(wǎng)絡(luò)交互的程序了。就像上面的過(guò)程一樣,在建立連接后,進(jìn)行一個(gè) while 循環(huán)。客戶端發(fā)了收,服務(wù)端收了發(fā)。
當(dāng)然這只是萬(wàn)里長(zhǎng)征的第一步,因?yàn)槿绻褂眠@種方法,基本上只能一對(duì)一溝通。如果你是一個(gè)服務(wù)器,同時(shí)只能服務(wù)一個(gè)客戶,肯定是不行的。這就相當(dāng)于老板成立一個(gè)公司,只有自己一個(gè)人,自己親自上來(lái)服務(wù)客戶,只能干完了一家再干下一家,這樣賺不來(lái)多少錢(qián)。
那作為老板你就要想了,我最多能接多少項(xiàng)目呢?當(dāng)然是越多越好。
我們先來(lái)算一下理論值,也就是最大連接數(shù),系統(tǒng)會(huì)用一個(gè)四元組來(lái)標(biāo)識(shí)一個(gè) TCP 連接。
{本機(jī) IP, 本機(jī)端口, 對(duì)端 IP, 對(duì)端端口}
服務(wù)器通常固定在某個(gè)本地端口上監(jiān)聽(tīng),等待客戶端的連接請(qǐng)求。因此,服務(wù)端端 TCP 連接四元組中只有對(duì)端 IP, 也就是客戶端的 IP 和對(duì)端的端口,也即客戶端的端口是可變的,因此,最大 TCP 連接數(shù) = 客戶端 IP 數(shù)×客戶端端口數(shù)。對(duì) IPv4,客戶端的 IP 數(shù)最多為 2 的 32 次方,客戶端的端口數(shù)最多為 2 的 16 次方,也就是服務(wù)端單機(jī)最大 TCP 連接數(shù),約為 2 的 48 次方。
當(dāng)然,服務(wù)端最大并發(fā) TCP 連接數(shù)遠(yuǎn)不能達(dá)到理論上限。首先主要是文件描述符限制,按照上面的原理,Socket 都是文件,所以首先要通過(guò) ulimit 配置文件描述符的數(shù)目;另一個(gè)限制是內(nèi)存,按上面的數(shù)據(jù)結(jié)構(gòu),每個(gè) TCP 連接都要占用一定內(nèi)存,操作系統(tǒng)是有限的。
所以,作為老板,在資源有限的情況下,要想接更多的項(xiàng)目,就需要降低每個(gè)項(xiàng)目消耗的資源數(shù)目。
方式一:將項(xiàng)目外包給其他公司(多進(jìn)程方式)
方式二:將項(xiàng)目轉(zhuǎn)包給獨(dú)立的項(xiàng)目組(多線程方式)
上面這種方式你應(yīng)該也能發(fā)現(xiàn)問(wèn)題,如果每次接一個(gè)項(xiàng)目,都申請(qǐng)一個(gè)新公司,然后干完了,就注銷掉這個(gè)公司,實(shí)在是太麻煩了。畢竟一個(gè)新公司要有新公司的資產(chǎn),有新的辦公家具,每次都買了再賣,不劃算。
于是你應(yīng)該想到了,我們可以使用線程。相比于進(jìn)程來(lái)講,這樣要輕量級(jí)的多。如果創(chuàng)建進(jìn)程相當(dāng)于成立新公司,購(gòu)買新辦公家具,而創(chuàng)建線程,就相當(dāng)于在同一個(gè)公司成立項(xiàng)目組。一個(gè)項(xiàng)目做完了,那這個(gè)項(xiàng)目組就可以解散,組成另外的項(xiàng)目組,辦公家具可以共用。
上面基于進(jìn)程或者線程模型的,其實(shí)還是有問(wèn)題的。新到來(lái)一個(gè) TCP 連接,就需要分配一個(gè)進(jìn)程或者線程。一臺(tái)機(jī)器無(wú)法創(chuàng)建很多進(jìn)程或者線程。有個(gè)C10K,它的意思是一臺(tái)機(jī)器要維護(hù) 1 萬(wàn)個(gè)連接,就要?jiǎng)?chuàng)建 1 萬(wàn)個(gè)進(jìn)程或者線程,那么操作系統(tǒng)是無(wú)法承受的。如果維持 1 億用戶在線需要 10 萬(wàn)臺(tái)服務(wù)器,成本也太高了。
方式三:一個(gè)項(xiàng)目組支撐多個(gè)項(xiàng)目(IO 多路復(fù)用,一個(gè)線程維護(hù)多個(gè) Socket)
當(dāng)然,一個(gè)項(xiàng)目組可以看多個(gè)項(xiàng)目了。這個(gè)時(shí)候,每個(gè)項(xiàng)目組都應(yīng)該有個(gè)項(xiàng)目進(jìn)度墻,將自己組看的項(xiàng)目列在那里,然后每天通過(guò)項(xiàng)目墻看每個(gè)項(xiàng)目的進(jìn)度,一旦某個(gè)項(xiàng)目有了進(jìn)展,就派人去盯一下。
由于 Socket 是文件描述符,因而某個(gè)線程盯的所有的 Socket,都放在一個(gè)文件描述符集合 fd_set 中,這就是項(xiàng)目進(jìn)度墻,然后調(diào)用 select 函數(shù)來(lái)監(jiān)聽(tīng)文件描述符集合是否有變化。一旦有變化,就會(huì)依次查看每個(gè)文件描述符。那些發(fā)生變化的文件描述符在 fd_set 對(duì)應(yīng)的位都設(shè)為 1,表示 Socket 可讀或者可寫(xiě),從而可以進(jìn)行讀寫(xiě)操作,然后再調(diào)用 select,接著盯著下一輪的變化。。
方式四:一個(gè)項(xiàng)目組支撐多個(gè)項(xiàng)目(IO 多路復(fù)用,從“派人盯著”到“有事通知”)
上面 select 函數(shù)還是有問(wèn)題的,因?yàn)槊看?Socket 所在的文件描述符集合中有 Socket 發(fā)生變化的時(shí)候,都需要通過(guò)輪詢的方式,也就是需要將全部項(xiàng)目都過(guò)一遍的方式來(lái)查看進(jìn)度,這大大影響了一個(gè)項(xiàng)目組能夠支撐的最大的項(xiàng)目數(shù)量。因而使用 select,能夠同時(shí)盯的項(xiàng)目數(shù)量由 FD_SETSIZE 限制。
如果改成事件通知的方式,情況就會(huì)好很多,項(xiàng)目組不需要通過(guò)輪詢挨個(gè)盯著這些項(xiàng)目,而是當(dāng)項(xiàng)目進(jìn)度發(fā)生變化的時(shí)候,主動(dòng)通知項(xiàng)目組,然后項(xiàng)目組再根據(jù)項(xiàng)目進(jìn)展情況做相應(yīng)的操作。
能完成這件事情的函數(shù)叫 epoll,它在內(nèi)核中的實(shí)現(xiàn)不是通過(guò)輪詢的方式,而是通過(guò)注冊(cè) callback 函數(shù)的方式,當(dāng)某個(gè)文件描述符發(fā)送變化的時(shí)候,就會(huì)主動(dòng)通知。
假設(shè)進(jìn)程打開(kāi)了 Socket m, n, x 等多個(gè)文件描述符,現(xiàn)在需要通過(guò) epoll 來(lái)監(jiān)聽(tīng)是否這些 Socket 都有事件發(fā)生。其中 epoll_create 創(chuàng)建一個(gè) epoll 對(duì)象,也是一個(gè)文件,也對(duì)應(yīng)一個(gè)文件描述符,同樣也對(duì)應(yīng)著打開(kāi)文件列表中的一項(xiàng)。在這項(xiàng)里面有一個(gè)紅黑樹(shù),在紅黑樹(shù)里,要保存這個(gè) epoll 要監(jiān)聽(tīng)的所有 Socket。
當(dāng) epoll_ctl 添加一個(gè) Socket 的時(shí)候,其實(shí)是加入這個(gè)紅黑樹(shù),同時(shí)紅黑樹(shù)里面的節(jié)點(diǎn)指向一個(gè)結(jié)構(gòu),將這個(gè)結(jié)構(gòu)掛在被監(jiān)聽(tīng)的 Socket 的事件列表中。當(dāng)一個(gè) Socket 來(lái)了一個(gè)事件的時(shí)候,可以從這個(gè)列表中得到 epoll 對(duì)象,并調(diào)用 call back 通知它。
這種通知方式使得監(jiān)聽(tīng)的 Socket 數(shù)據(jù)增加的時(shí)候,效率不會(huì)大幅度降低,能夠同時(shí)監(jiān)聽(tīng)的 Socket 的數(shù)目也非常的多了。上限就為系統(tǒng)定義的、進(jìn)程打開(kāi)的最大文件描述符個(gè)數(shù)。因而,epoll 被稱為解決 C10K 問(wèn)題的利器
名詞解釋
文件描述符限制是指一個(gè)操作系統(tǒng)中能夠同時(shí)打開(kāi)的文件和網(wǎng)絡(luò)連接的數(shù)量上限。為了理解這個(gè)概念,我們可以先了解什么是文件描述符,然后解釋為什么它會(huì)限制并發(fā) TCP 連接數(shù)。
什么是文件描述符?
在操作系統(tǒng)中,每個(gè)文件(包括網(wǎng)絡(luò)連接)在打開(kāi)時(shí),都會(huì)被分配一個(gè)唯一的標(biāo)識(shí)符,這個(gè)標(biāo)識(shí)符就叫做文件描述符(File Descriptor,簡(jiǎn)稱 FD)。文件描述符是一個(gè)非負(fù)整數(shù),用來(lái)引用一個(gè)打開(kāi)的文件或網(wǎng)絡(luò)連接。
- 文件:任何類型的文件,比如文本文件、圖片文件等。
- 網(wǎng)絡(luò)連接:TCP 連接、UDP 連接等。
- 其他資源:如管道、設(shè)備等。
文件描述符限制
操作系統(tǒng)對(duì)每個(gè)進(jìn)程能夠同時(shí)打開(kāi)的文件描述符數(shù)量有限制,這是出于資源管理和安全的考慮。這個(gè)限制通??梢苑譃閮蓚€(gè)層次:
- 軟限制:用戶或進(jìn)程可以更改的限制,一般默認(rèn)較小,但可以通過(guò)修改系統(tǒng)設(shè)置或在程序中動(dòng)態(tài)調(diào)整。
- 硬限制:系統(tǒng)級(jí)的限制,只有管理員可以更改,通常比軟限制要大。
舉個(gè)例子
假設(shè)你在編寫(xiě)一個(gè)服務(wù)器程序,這個(gè)服務(wù)器需要處理很多客戶端的連接,每個(gè)連接對(duì)應(yīng)一個(gè)文件描述符。
- 默認(rèn)限制:操作系統(tǒng)可能默認(rèn)限制每個(gè)進(jìn)程只能打開(kāi) 1024 個(gè)文件描述符。如果你有超過(guò) 1024 個(gè)客戶端同時(shí)連接到服務(wù)器,新的連接將無(wú)法建立,因?yàn)槲募枋龇呀?jīng)用完。
- 調(diào)整限制:你可以通過(guò)修改系統(tǒng)配置來(lái)增加文件描述符的限制。例如,在 Linux 系統(tǒng)中,你可以通過(guò)修改
/etc/security/limits.conf
文件或使用ulimit
命令來(lái)調(diào)整這個(gè)限制。
為什么文件描述符限制會(huì)影響并發(fā)連接數(shù)?
每個(gè) TCP 連接在服務(wù)器端都需要一個(gè)文件描述符來(lái)表示和管理。如果文件描述符用完了,服務(wù)器將無(wú)法接受新的連接,即使硬件和其他資源還能夠處理更多的連接。這就導(dǎo)致了并發(fā) TCP 連接數(shù)遠(yuǎn)不能達(dá)到理論上的上限。
應(yīng)用層協(xié)議
二、HTTP協(xié)議:看個(gè)新聞原來(lái)這么麻煩
HTTP 是基于 TCP 協(xié)議的,要先建立 TCP 連接
建立了連接以后,瀏覽器就要發(fā)送 HTTP 的請(qǐng)求。
HTTP 請(qǐng)求的創(chuàng)建
第一部分:請(qǐng)求行
GET POST PUT DELETE
POST 往往是用來(lái)創(chuàng)建一個(gè)資源的,而 PUT 往往是用來(lái)修改一個(gè)資源的。
第二部分:首部字段
例如,Accept-Charset,表示客戶端可以接受的字符集。防止傳過(guò)來(lái)的是另外的字符集,從而導(dǎo)致出現(xiàn)亂碼。
再如,Content-Type是指正文的格式。例如,我們進(jìn)行 POST 的請(qǐng)求,如果正文是 JSON,那么我們就應(yīng)該將這個(gè)值設(shè)置為 JSON。
在 HTTP 協(xié)議中,Cache-Control
和 If-Modified-Since
是用于控制緩存行為和條件請(qǐng)求的頭字段。讓我們通俗易懂地解釋它們的作用和工作方式。
Cache-Control 頭字段用于指定緩存機(jī)制的指令,這些指令告訴瀏覽器和中間緩存服務(wù)器如何緩存 HTTP 響應(yīng)。它可以幫助提高網(wǎng)站性能和減少帶寬消耗。
常見(jiàn)指令
-
public:響應(yīng)可以被任何緩存(包括瀏覽器、代理服務(wù)器等)緩存。
- 例子:
Cache-Control: public
- 例子:
-
private:響應(yīng)只能被用戶的瀏覽器緩存,不能被共享緩存(如代理服務(wù)器)緩存。
- 例子:
Cache-Control: private
- 例子:
-
no-cache:緩存可以存儲(chǔ)響應(yīng),但在使用前必須先驗(yàn)證其有效性(向服務(wù)器發(fā)送請(qǐng)求確認(rèn))。
- 例子:
Cache-Control: no-cache
- 例子:
-
no-store:不允許緩存響應(yīng),所有內(nèi)容每次都必須從服務(wù)器獲取。
- 例子:
Cache-Control: no-store
- 例子:
-
max-age:指定響應(yīng)在緩存中可以保存的最大時(shí)間(以秒為單位),在此時(shí)間內(nèi)緩存內(nèi)容被認(rèn)為是新鮮的。
- 例子:
Cache-Control: max-age=3600
(緩存內(nèi)容在1小時(shí)內(nèi)有效)
- 例子:
場(chǎng)景:用戶訪問(wèn)網(wǎng)頁(yè)
-
第一次訪問(wèn):
- 用戶瀏覽器向服務(wù)器請(qǐng)求網(wǎng)頁(yè)。
- 服務(wù)器返回網(wǎng)頁(yè)內(nèi)容,并在響應(yīng)頭中包含
Cache-Control: max-age=3600
和Last-Modified
頭字段。 - 瀏覽器將網(wǎng)頁(yè)緩存1小時(shí)。
-
在1小時(shí)內(nèi)再次訪問(wèn):
- 瀏覽器檢查緩存,發(fā)現(xiàn)緩存仍然有效(未超過(guò)
max-age
)。 - 瀏覽器直接從緩存中加載網(wǎng)頁(yè),無(wú)需向服務(wù)器發(fā)送請(qǐng)求。
- 瀏覽器檢查緩存,發(fā)現(xiàn)緩存仍然有效(未超過(guò)
-
超過(guò)1小時(shí)再次訪問(wèn):
- 瀏覽器向服務(wù)器發(fā)送請(qǐng)求,包含If-Modified-Since頭字段,指示上次接收到的
Last-Modified
時(shí)間。 - 服務(wù)器檢查資源是否自該時(shí)間以來(lái)有修改:
- 如果沒(méi)有修改,返回
304 Not Modified
,瀏覽器使用緩存內(nèi)容。 - 如果有修改,返回新的網(wǎng)頁(yè)內(nèi)容和新的
Last-Modified
時(shí)間,瀏覽器更新緩存。
- 如果沒(méi)有修改,返回
- 瀏覽器向服務(wù)器發(fā)送請(qǐng)求,包含If-Modified-Since頭字段,指示上次接收到的
HTTP 請(qǐng)求的發(fā)送
就是 TCP 傳輸
HTTP 2.0
HTTP/2 和 HTTP/1.1 是兩個(gè)版本的超文本傳輸協(xié)議,它們有許多不同之處,主要目的是提高性能和效率。以下是 HTTP/2 和 HTTP/1.1 的主要區(qū)別,通俗易懂地解釋這些技術(shù)細(xì)節(jié):
1. 多路復(fù)用
- HTTP/1.1:每個(gè)請(qǐng)求-響應(yīng)對(duì)都需要一個(gè)獨(dú)立的 TCP 連接。這意味著如果一個(gè)網(wǎng)頁(yè)上有多個(gè)資源(如圖片、CSS 文件、JavaScript 文件等),每個(gè)資源的請(qǐng)求通常需要單獨(dú)的連接,導(dǎo)致了“隊(duì)頭阻塞”(Head-of-Line Blocking)問(wèn)題:一個(gè)請(qǐng)求阻塞了,后續(xù)請(qǐng)求也無(wú)法進(jìn)行。
- HTTP/2:引入了多路復(fù)用技術(shù),多個(gè)請(qǐng)求和響應(yīng)可以在一個(gè)單一的 TCP 連接中同時(shí)進(jìn)行。這樣可以有效地利用網(wǎng)絡(luò)資源,減少延遲。
2. 二進(jìn)制分幀
- HTTP/1.1:使用純文本格式來(lái)傳輸數(shù)據(jù),包括請(qǐng)求和響應(yīng)頭部。這種格式在解析時(shí)效率較低。
- HTTP/2:使用二進(jìn)制分幀層(Binary Framing Layer),將所有傳輸?shù)男畔?#xff08;頭部和數(shù)據(jù))編碼為二進(jìn)制格式。這種方式更高效、解析更快,并且更容易實(shí)現(xiàn)多路復(fù)用。
3. 頭部壓縮
- HTTP/1.1:每次請(qǐng)求都會(huì)攜帶完整的頭部信息,頭部信息往往很大且包含重復(fù)的內(nèi)容,浪費(fèi)了帶寬。
- HTTP/2:使用 HPACK 壓縮算法對(duì)頭部信息進(jìn)行壓縮,大大減少了頭部的大小和冗余信息,提高了傳輸效率。
4. 服務(wù)器推送
- HTTP/1.1:只有客戶端可以主動(dòng)請(qǐng)求資源,服務(wù)器只能被動(dòng)響應(yīng)。
- HTTP/2:引入了服務(wù)器推送功能,服務(wù)器可以在客戶端請(qǐng)求某個(gè)資源時(shí),主動(dòng)推送其他相關(guān)資源到客戶端,這樣客戶端就不需要再單獨(dú)請(qǐng)求這些資源了。例如,當(dāng)客戶端請(qǐng)求一個(gè) HTML 頁(yè)面時(shí),服務(wù)器可以提前推送相關(guān)的 CSS 和 JavaScript 文件。
5. 流量控制
- HTTP/1.1:沒(méi)有針對(duì)流量控制的機(jī)制,所有請(qǐng)求-響應(yīng)對(duì)共享帶寬,可能導(dǎo)致性能不穩(wěn)定。
- HTTP/2:引入了流量控制機(jī)制,可以更好地管理和分配帶寬,確保各個(gè)請(qǐng)求的傳輸速度和效率。
6. 優(yōu)先級(jí)和依賴關(guān)系
- HTTP/1.1:沒(méi)有內(nèi)置的請(qǐng)求優(yōu)先級(jí)機(jī)制,所有請(qǐng)求的處理順序主要取決于到達(dá)服務(wù)器的順序。
- HTTP/2:允許客戶端為每個(gè)請(qǐng)求分配優(yōu)先級(jí),并建立依賴關(guān)系,使得重要的資源可以優(yōu)先傳輸,優(yōu)化了頁(yè)面加載順序和速度。
QUIC
盡管 HTTP/2 引入了多路復(fù)用技術(shù),使得多個(gè)流可以在一個(gè) TCP 連接上并行傳輸,但由于底層使用的是 TCP 協(xié)議,TCP 必須保證數(shù)據(jù)包按順序和完整性傳輸。如果某個(gè)數(shù)據(jù)包出現(xiàn)問(wèn)題,整個(gè)連接上的所有數(shù)據(jù)傳輸都會(huì)被阻塞,直到問(wèn)題數(shù)據(jù)包被正確重傳和接收。這就意味著,即使在 HTTP/2 中,某個(gè)流的數(shù)據(jù)包出現(xiàn)問(wèn)題,其他流的數(shù)據(jù)傳輸也會(huì)受到影響,無(wú)法完全避免隊(duì)頭阻塞的問(wèn)題
于是,就又到了從 TCP 切換到 UDP。這就是 Google 的 QUIC 協(xié)議
機(jī)制一:自定義連接機(jī)制
我們都知道,一條 TCP 連接是由四元組標(biāo)識(shí)的,分別是源 IP、源端口、目的 IP、目的端口。一旦一個(gè)元素發(fā)生變化時(shí),就需要斷開(kāi)重連,重新連接。在移動(dòng)互聯(lián)情況下,當(dāng)手機(jī)信號(hào)不穩(wěn)定或者在 WIFI 和 移動(dòng)網(wǎng)絡(luò)切換時(shí),都會(huì)導(dǎo)致重連,從而進(jìn)行再次的三次握手,導(dǎo)致一定的時(shí)延。
這在 TCP 是沒(méi)有辦法的,但是基于 UDP,就可以在 QUIC 自己的邏輯里面維護(hù)連接的機(jī)制,不再以四元組標(biāo)識(shí),而是以一個(gè) 64 位的隨機(jī)數(shù)作為 ID 來(lái)標(biāo)識(shí),而且 UDP 是無(wú)連接的,所以當(dāng) IP 或者端口變化的時(shí)候,只要 ID 不變,就不需要重新建立連接。