詳細(xì)剖析分布式微服務(wù)架構(gòu)下網(wǎng)絡(luò)通信的底層實(shí)現(xiàn)原理(圖解)

在分布式架構(gòu)中,網(wǎng)絡(luò)通信是底層基礎(chǔ),沒有網(wǎng)絡(luò),也就沒有所謂的分布式架構(gòu)。只有通過網(wǎng)絡(luò)才能使得一大片機(jī)器互相協(xié)作,共同完成一件事情。

同樣,在大規(guī)模的系統(tǒng)架構(gòu)中,應(yīng)用吞吐量上不去、網(wǎng)絡(luò)存在通信延遲、我們首先考慮的都是網(wǎng)絡(luò)問題,因此網(wǎng)絡(luò)的重要性不言而喻。

作為現(xiàn)代化應(yīng)用型程序員,要開發(fā)一個(gè)網(wǎng)絡(luò)通信的應(yīng)用,是非常簡(jiǎn)單的。不僅僅有成熟的api,還有非常方便的通信框架。

可能大家已經(jīng)忘記了網(wǎng)絡(luò)通信的重要性,本篇文章會(huì)詳細(xì)分析網(wǎng)絡(luò)通信的底層原理!!

1.1 理解通信的本質(zhì)

如圖1-1所示,當(dāng)我們通過瀏覽器訪問一個(gè)網(wǎng)址時(shí),一段時(shí)間后該網(wǎng)址會(huì)渲染出訪問的內(nèi)容,這個(gè)過程是怎么實(shí)現(xiàn)的呢?

image-20210821151126835

<center>圖1-1</center>

我想站在今天,在做的同學(xué)都知道,它是基于http協(xié)議來(lái)實(shí)現(xiàn)數(shù)據(jù)通信的,這里有兩個(gè)字很重要,就是“協(xié)議”。

兩個(gè)計(jì)算機(jī)之間要實(shí)現(xiàn)數(shù)據(jù)通信,必須遵循同一種協(xié)議,否則,就像一個(gè)中國(guó)人和一個(gè)外國(guó)人交流時(shí),一個(gè)講英語(yǔ)另一個(gè)講解中文,肯定是無(wú)法正常交流。在計(jì)算機(jī)中,協(xié)議非常常見。

1.1.1 協(xié)議的組成

我們寫的Java代碼,計(jì)算機(jī)能夠理解并且執(zhí)行,原因是人和計(jì)算機(jī)之間遵循了同一種語(yǔ)言,那就是Java,如圖1-2所示,.java文件最終編譯成.class文件這個(gè)過程,也同樣涉及到協(xié)議。

image-20210821151817974

<center>圖1-2 java編譯過程</center>

所以,在計(jì)算機(jī)中,協(xié)議是指大家需要共同遵循的規(guī)則,只有實(shí)現(xiàn)統(tǒng)一規(guī)則之后,才能實(shí)現(xiàn)不同節(jié)點(diǎn)之間的數(shù)據(jù)通信,從而讓計(jì)算機(jī)的應(yīng)用更加強(qiáng)大。

組成一個(gè)協(xié)議,需要具備三個(gè)要素:

  • 語(yǔ)法,就是這一段內(nèi)容要符合一定的規(guī)則和格式。例如,括號(hào)要成對(duì),結(jié)束要使用分號(hào)等。
  • 語(yǔ)義,就是這一段內(nèi)容要代表某種意義。例如數(shù)字減去數(shù)字是有意義的,數(shù)字減去文本一般來(lái)說就沒有意義。
  • 時(shí)序,就是先干啥,后干啥。例如,可以先加上某個(gè)數(shù)值,然后再減去某個(gè)數(shù)值。

1.1.2 http協(xié)議

理解了協(xié)議的作用,那協(xié)議是長(zhǎng)什么樣的呢?

那么再來(lái)看圖1-3的場(chǎng)景,人們通過瀏覽器訪問網(wǎng)站,用到了http協(xié)議。

image-20210821151126835

<center>圖1-3 http協(xié)議</center>

http協(xié)議包含包含幾個(gè)部分:

  • http請(qǐng)求組成
    • 狀態(tài)行
    • 請(qǐng)求頭
    • 消息主體
  • http響應(yīng)組成
    • 狀態(tài)行
    • 響應(yīng)頭
    • 響應(yīng)正文

Http響應(yīng)報(bào)文如圖1-4所示,那么這個(gè)協(xié)議的三要素分別是:

  • 語(yǔ)法: http協(xié)議的消息體由狀態(tài)、頭部、內(nèi)容組成。
  • 語(yǔ)義: 比如狀態(tài),200表示成功,404表示請(qǐng)求路徑不存在等,通信雙方必須遵循該語(yǔ)義。
  • 時(shí)序: 組成消息體的三部分的排列順序,必須要有request,才會(huì)產(chǎn)生response。

而瀏覽器按照http協(xié)議做好了相關(guān)的處理后,才能讓大家通過網(wǎng)址訪問網(wǎng)絡(luò)上的各種信息。

image-20210821155309605

<center>圖1-4</center>

1.1.3 常用的網(wǎng)絡(luò)協(xié)議

DNS協(xié)議、Http協(xié)議、SSH協(xié)議、TCP協(xié)議、FTP協(xié)議等,這些都是大家比較常用的協(xié)議類型。無(wú)論哪種協(xié)議,本質(zhì)上仍然是由協(xié)議的三要素組成,只是應(yīng)用場(chǎng)景不同。

DNS、HTTP、HTTPS 所在的層我們稱為應(yīng)用層。經(jīng)過應(yīng)用層封裝后,瀏覽器會(huì)將應(yīng)用層的包交給下一層去完成,通過 socket 編程來(lái)實(shí)現(xiàn)。下一層是傳輸層。傳輸層有兩種協(xié)議,一種是無(wú)連接的協(xié)議 UDP,一種是面向連接的協(xié)議 TCP。對(duì)于通信可靠性要求的場(chǎng)景來(lái)說,往往使用 TCP 協(xié)議。所謂的面向連接就是,TCP 會(huì)保證這個(gè)包能夠到達(dá)目的地。如果不能到達(dá),就會(huì)重新發(fā)送,直至到達(dá)。

1.3 TCP/IP通信原理分析

一次網(wǎng)絡(luò)通信到底是怎么完成的呢?

涉及到網(wǎng)絡(luò)通信,那我們一定會(huì)提到一個(gè)網(wǎng)絡(luò)模型的概念,如圖1-5所示。表示TCP/IP的四層概念模型和OSI七層網(wǎng)絡(luò)模型,它是一種概念模型,由國(guó)際標(biāo)準(zhǔn)化組織提出來(lái)的,試圖讓全世界范圍內(nèi)的計(jì)算機(jī)能基于該網(wǎng)絡(luò)標(biāo)準(zhǔn)實(shí)現(xiàn)互聯(lián)。

image-20210821170017901

<center>圖1-5</center>

網(wǎng)絡(luò)模型為什么要分層呢?其實(shí)從我們現(xiàn)在的業(yè)務(wù)分層架構(gòu)中就不難發(fā)現(xiàn),任何系統(tǒng)一旦變得復(fù)雜,就都會(huì)采用分層設(shè)計(jì)。它的主要好處是

  • 實(shí)現(xiàn)高內(nèi)聚低耦合
  • 每一層有自己?jiǎn)我坏穆氊?zé)
  • 提高可復(fù)用性和降低維護(hù)成本

1.2.1 http通信過程的發(fā)送數(shù)據(jù)包

由于我們的課程并不是專門來(lái)講網(wǎng)絡(luò),所以只是提及一下網(wǎng)絡(luò)分層模型,為了讓大家更簡(jiǎn)單的理解網(wǎng)絡(luò)分層模型的工作原理,我們?nèi)匀灰砸淮尉W(wǎng)絡(luò)通信的數(shù)據(jù)包傳輸為例進(jìn)行分析,如圖1-6所示。

image-20210821175033443

<center>圖1-6</center>

圖1-6的工作流程描述如下:

  • 假設(shè)我們要登錄某一個(gè)網(wǎng)站,此時(shí)基于Http協(xié)議會(huì)構(gòu)建一個(gè)http協(xié)議報(bào)文,這個(gè)報(bào)文中按照http協(xié)議的規(guī)范組裝,其中包括要傳輸?shù)挠脩裘兔艽a。這個(gè)是屬于應(yīng)用層協(xié)議。

  • 經(jīng)過應(yīng)用層封裝后,瀏覽器會(huì)把應(yīng)用層的包交給TCP/IP四層模型中的下一層,也就是傳輸層來(lái)完成,傳輸層有兩種協(xié)議:

    • TCP協(xié)議,可靠的通信協(xié)議,該協(xié)議會(huì)確保數(shù)據(jù)包能達(dá)到目的地
    • UDP協(xié)議,不可靠通信協(xié)議,可能會(huì)存在數(shù)據(jù)丟失

    在http通信中使用了TCP協(xié)議,TCP協(xié)議會(huì)有兩個(gè)端口,一個(gè)是瀏覽器監(jiān)聽的端口,一個(gè)是目標(biāo)服務(wù)器進(jìn)程的端口。操作系統(tǒng)會(huì)根據(jù)端口來(lái)判斷這個(gè)數(shù)據(jù)包應(yīng)該分發(fā)給那個(gè)進(jìn)程。

  • 傳輸層封裝完成后,該數(shù)據(jù)包會(huì)技術(shù)交給網(wǎng)絡(luò)層來(lái)處理,網(wǎng)絡(luò)層協(xié)議是IP協(xié)議,IP協(xié)議中會(huì)包含源IP地址(也就是客戶端及其的IP)和目標(biāo)服務(wù)器的IP地址。

  • 操作系統(tǒng)知道了目標(biāo)IP地址后,就開始根據(jù)這個(gè)IP來(lái)尋找目標(biāo)機(jī)器,而目標(biāo)服務(wù)器一定是部署在不同的地方,這種跨網(wǎng)絡(luò)節(jié)點(diǎn)的訪問,需要經(jīng)過網(wǎng)關(guān)(所謂網(wǎng)關(guān)就是一個(gè)網(wǎng)絡(luò)到另外一個(gè)網(wǎng)絡(luò)的關(guān)口)。

    所以數(shù)據(jù)包首先需要先通過自己當(dāng)前所在網(wǎng)絡(luò)的網(wǎng)關(guān)出去,然后訪問到目標(biāo)服務(wù)器,但是在數(shù)據(jù)包傳輸?shù)侥繕?biāo)服務(wù)器之前,需要再組裝MAC頭信息。

    Mac頭包含本地的Mac地址和目標(biāo)服務(wù)器的Mac地址,這個(gè)MAC地址怎么獲得的呢?

    • 獲取本機(jī)MAC地址的方法是,操作系統(tǒng)會(huì)發(fā)送一個(gè)廣播消息詢問網(wǎng)關(guān)地址(192.168.1.1)是誰(shuí)?收到該廣播消息的網(wǎng)關(guān)會(huì)回應(yīng)一個(gè)MAC地址。這個(gè)廣播消息是基于ARP協(xié)議實(shí)現(xiàn)的(這個(gè)協(xié)議簡(jiǎn)單來(lái)說就是已知目標(biāo)機(jī)器的ip,需要獲得目標(biāo)機(jī)器的mac地址。(發(fā)送一個(gè)廣播消息,這個(gè)ip是誰(shuí)的,請(qǐng)來(lái)認(rèn)領(lǐng)。認(rèn)領(lǐng)ip的機(jī)器會(huì)發(fā)送一個(gè)mac地址的響應(yīng)))。

      為了避免每次都用 ARP 請(qǐng)求,機(jī)器本地也會(huì)進(jìn)行 ARP 緩存。當(dāng)然機(jī)器會(huì)不斷地上線下線,IP 也可能會(huì)變,所以 ARP 的 MAC 地址緩存過一段時(shí)間就會(huì)過期。

    • 獲取遠(yuǎn)程機(jī)器的MAC地址的方法也同樣是基于ARP協(xié)議實(shí)現(xiàn)的。

完成MAC地址組裝后,一個(gè)完整的數(shù)據(jù)包就構(gòu)成了。這個(gè)時(shí)候會(huì)把這個(gè)數(shù)據(jù)包給到網(wǎng)卡,網(wǎng)卡再把這個(gè)數(shù)據(jù)包發(fā)出去,由于這個(gè)數(shù)據(jù)包中包含MAC地址,因此它能夠到達(dá)網(wǎng)關(guān)進(jìn)行傳輸。網(wǎng)關(guān)收到包之后,會(huì)根據(jù)路由信息,判斷下一步應(yīng)該怎么走。網(wǎng)關(guān)往往是一個(gè)路由器,到某個(gè) IP 地址應(yīng)該怎么走,這個(gè)叫作路由表。

1.2.2 http通信過程中的接收數(shù)據(jù)包

當(dāng)數(shù)據(jù)包發(fā)送到網(wǎng)關(guān)后,會(huì)根據(jù)網(wǎng)關(guān)的路由信息判斷該數(shù)據(jù)包要傳輸?shù)侥莻€(gè)網(wǎng)段上。數(shù)據(jù)從客戶端發(fā)送到目標(biāo)服務(wù)器,可能會(huì)經(jīng)過多個(gè)網(wǎng)關(guān),所以數(shù)據(jù)包根據(jù)網(wǎng)關(guān)路由進(jìn)入到下一個(gè)網(wǎng)關(guān)后,繼續(xù)根據(jù)下一個(gè)網(wǎng)關(guān)的MAC地址尋找下下一個(gè)網(wǎng)關(guān),直到到達(dá)目標(biāo)網(wǎng)絡(luò)服務(wù)器上。

這個(gè)時(shí)候服務(wù)器收到包之后,最后一個(gè)網(wǎng)關(guān)知道這個(gè)網(wǎng)絡(luò)包就是要去當(dāng)前局域網(wǎng)的,于是拿著目標(biāo)IP通過ARP協(xié)議大喊一聲這是誰(shuí)? 目標(biāo)服務(wù)器就會(huì)給網(wǎng)關(guān)回復(fù)一個(gè)MAC地址。 然后網(wǎng)絡(luò)包在最后那個(gè)網(wǎng)關(guān)修改目標(biāo)的MAC地址,通過這個(gè)MAC地址,網(wǎng)絡(luò)包找到了目標(biāo)服務(wù)器。

當(dāng)目標(biāo)服務(wù)器和MAC地址對(duì)上后,開始取出MAC頭信息,接著把數(shù)據(jù)包發(fā)送給操作系統(tǒng)的網(wǎng)絡(luò)層。網(wǎng)絡(luò)層會(huì)取出IP頭信息,IP頭里面會(huì)寫上一層封裝的是TCP協(xié)議,于是交給傳輸層來(lái)處理,實(shí)現(xiàn)過程如圖1-7所示。

在這一層中,對(duì)于收到的每個(gè)數(shù)據(jù)包都會(huì)有一個(gè)回復(fù),表示服務(wù)器端已經(jīng)收到了該數(shù)據(jù)包。如果過一段時(shí)間客戶端沒有收到該確認(rèn)包,發(fā)送端的 TCP 層會(huì)重新發(fā)送這個(gè)包,還是上面的過程,直到最終收到回復(fù)。

這個(gè)重試是TCP協(xié)議層來(lái)實(shí)現(xiàn)的,不需要我們應(yīng)用來(lái)主動(dòng)發(fā)起。

image-20210822152538600

<center>圖1-7</center>

為什么有了MAC層還要走IP層呢?

之前我們提到,mac地址是唯一的,那理論上,在任何兩個(gè)設(shè)備之間,我應(yīng)該都可以通過mac地址發(fā)送數(shù)據(jù),為什么還需要ip地址?

mac地址就好像個(gè)人的身份證號(hào),人的身份證號(hào)和人戶口所在的城市,出生的日期有關(guān),但是和人所在的位置沒有關(guān)系,人是會(huì)移動(dòng)的,知道一個(gè)人的身份證號(hào),并不能找到它這個(gè)人,mac地址類似,它是和設(shè)備的生產(chǎn)者,批次,日期之類的關(guān)聯(lián)起來(lái),知道一個(gè)設(shè)備的mac,并不能在網(wǎng)絡(luò)中將數(shù)據(jù)發(fā)送給它,除非它和發(fā)送方的在同一個(gè)網(wǎng)絡(luò)內(nèi)。

所以要實(shí)現(xiàn)機(jī)器之間的通信,我們還需要有ip地址的概念,ip地址表達(dá)的是當(dāng)前機(jī)器在網(wǎng)絡(luò)中的位置,類似于城市名+道路號(hào)+門牌號(hào)的概念。通過ip層的尋址,我們能知道按何種路徑在全世界任意兩臺(tái)Internet上的的機(jī)器間傳輸數(shù)據(jù)。

1.4 詳解TCP可靠性通信特性

我們知道,TCP協(xié)議是屬于可靠性通信協(xié)議,它能夠確保數(shù)據(jù)包不被丟失。首先我們先了解一下TCP的三次握手和四次揮手。

1.4.1 TCP的三次握手

兩個(gè)節(jié)點(diǎn)需要進(jìn)行數(shù)據(jù)通信,首先得先建立連接。而在建立連接時(shí),TCP采用了三次握手來(lái)實(shí)現(xiàn)連接建立。如圖1-8所示。

image-20210822161148923

<center>圖1-8</center>

第一次握手(SYN=1, seq=x)

客戶端發(fā)送一個(gè) TCP的 SYN 標(biāo)志位置1的包,指明客戶端打算連接的服務(wù)器的端口,以及初始序號(hào) X,保存在包頭的序列號(hào)(Sequence Number)字段里。發(fā)送完畢后,客戶端進(jìn)入 SYN_SEND 狀態(tài)。

第二次握手(SYN=1, ACK=1, seq=y, ACK num=x+1):

服務(wù)器發(fā)回確認(rèn)包(ACK)應(yīng)答。即 SYN 標(biāo)志位和 ACK 標(biāo)志位均為1。服務(wù)器端選擇自己 ISN 序列號(hào),放到Seq 域里,同時(shí)將確認(rèn)序號(hào)(Acknowledgement Number)設(shè)置為客戶的 ISN 加1,即X+1。 發(fā)送完畢后,服務(wù)器端進(jìn)入 SYN_RCVD 狀態(tài)。

第三次握手(ACK=1,ACK num=y+1)

客戶端再次發(fā)送確認(rèn)包(ACK),SYN標(biāo)志位為0,ACK標(biāo)志位為1,并且把服務(wù)器發(fā)來(lái) ACK的序號(hào)字段+1,放在確定字段中發(fā)送給對(duì)方,并且在數(shù)據(jù)段放寫ISN發(fā)完畢后,客戶端進(jìn)入 ESTABLISHED 狀態(tài),當(dāng)服務(wù)器端接收到這個(gè)包時(shí),也進(jìn)入 ESTABLISHED 狀態(tài),TCP握手結(jié)束。

1.4.2 TCP為什么是三次握手?

TCP是全雙工,如果沒有第三次的握手,服務(wù)端不能確認(rèn)客戶端是否ready,不知道什么時(shí)候可以往客戶端發(fā)數(shù)據(jù)包。三次的握手剛好兩邊都互相確認(rèn)對(duì)方已經(jīng)ready。

我們假設(shè)網(wǎng)絡(luò)的不可靠性,

A發(fā)起一個(gè)連接,當(dāng)發(fā)起一個(gè)請(qǐng)求沒有得到反饋的時(shí)候,會(huì)有很多可能性,比如請(qǐng)求包丟失,或者超時(shí),或者B沒有響應(yīng)

由于A不能確認(rèn)結(jié)果,于是再發(fā),當(dāng)有一個(gè)請(qǐng)求包到了B之后,A并不知道這個(gè)數(shù)據(jù)包已經(jīng)到了B,所以可能還會(huì)重試。

所以B收到請(qǐng)求之后,知道了A的存在并且要和我建立連接,這個(gè)時(shí)候B會(huì)發(fā)送ack給到A,告訴A我收到了請(qǐng)求包。

對(duì)于B來(lái)說,這個(gè)應(yīng)答包也是一個(gè)網(wǎng)絡(luò)通信,我怎么知道能不能到達(dá)A呢?所以這個(gè)時(shí)候B不能很主觀的認(rèn)為連接已經(jīng)建立好了,還需要等到A再次發(fā)送應(yīng)答包來(lái)確認(rèn)。

1.4.3 TCP的四次揮手

如圖1-9所示,TCP的連接斷開,會(huì)通過所謂的四次揮手完成。

四次揮手表示TCP斷開連接的時(shí)候,需要客戶端和服務(wù)端總共發(fā)送4個(gè)包以確認(rèn)連接的斷開;客戶端或服務(wù)器均可主動(dòng)發(fā)起揮手動(dòng)作(因?yàn)門CP是一個(gè)全雙工協(xié)議),在 socket 編程中,任何一方執(zhí)行 close() 操作即可產(chǎn)生揮手操作。

image-20210822162756038

<center>圖1-9</center>

上述交互過程如下:

  • 斷開的時(shí)候,我們可以看到,當(dāng)A客戶端說說“我要斷開連接”,就進(jìn)入 FIN_WAIT_1 的狀態(tài)。

  • B 服務(wù)端收到“我要斷開連接”的消息后,發(fā)送"知道了"給到A客戶端,就進(jìn)入 CLOSE_WAIT 的狀態(tài)。

  • A 收到“B 說知道了”,就進(jìn)入 FIN_WAIT_2 的狀態(tài),如果這個(gè)時(shí)候 B 服務(wù)器掛掉了,則 A 將永遠(yuǎn)在這個(gè)狀態(tài)。TCP 協(xié)議里面并沒有對(duì)這個(gè)狀態(tài)的處理,但是 Linux 有,可以調(diào)整 tcp_fin_timeout 這個(gè)參數(shù),設(shè)置一個(gè)超時(shí)時(shí)間。

  • 如果 B 服務(wù)器正常,則發(fā)送了“B 要關(guān)閉連接”的請(qǐng)求到達(dá) A 時(shí),A 發(fā)送“知道 B 也要關(guān)閉連接”的 ACK 后,從 FIN_WAIT_2 狀態(tài)結(jié)束。

  • 按說這個(gè)時(shí)候 A 可以退出了,但是最后的這個(gè) ACK 萬(wàn)一 B 收不到呢?則 B 會(huì)重新發(fā)一個(gè)“B 要關(guān)閉連接”,這個(gè)時(shí)候 A 已經(jīng)跑路了的話,B 就再也收不到 ACK 了,因而 TCP 協(xié)議要求 A 最后等待一段時(shí)間 TIME_WAIT,這個(gè)時(shí)間要足夠長(zhǎng),長(zhǎng)到如果 B 沒收到 ACK 的話,“B 說不玩了”會(huì)重發(fā)的,A 會(huì)重新發(fā)一個(gè) ACK 并且足夠時(shí)間到達(dá) B。

這個(gè)等待實(shí)現(xiàn)是2MSL,MSL 是 Maximum Segment Lifetime,報(bào)文最大生存時(shí)間,它是任何報(bào)文在網(wǎng)絡(luò)上存在的最長(zhǎng)時(shí)間,超過這個(gè)時(shí)間報(bào)文將被丟棄(此時(shí)A直接進(jìn)入CLOSE狀態(tài))。協(xié)議規(guī)定 MSL 為 2 分鐘,實(shí)際應(yīng)用中常用的是 30 秒,1 分鐘和 2 分鐘等。

第一次揮手(FIN=1,seq=x)

假設(shè)客戶端想要關(guān)閉連接,客戶端發(fā)送一個(gè) FIN 標(biāo)志位置為1的包,表示自己已經(jīng)沒有數(shù)據(jù)可以發(fā)送了,但是仍然可以接受數(shù)據(jù)。發(fā)送完畢后,客戶端進(jìn)入 FIN_WAIT_1 狀態(tài)。

第二次揮手(ACK=1,ACKnum=x+1)

服務(wù)器端確認(rèn)客戶端的 FIN包,發(fā)送一個(gè)確認(rèn)包,表明自己接受到了客戶端關(guān)閉連接的請(qǐng)求,但還沒有準(zhǔn)備好關(guān)閉連接。發(fā)送完畢后,服務(wù)器端進(jìn)入 CLOSE_WAIT 狀態(tài),客戶端接收到這個(gè)確認(rèn)包之后,進(jìn)入 FIN_WAIT_2 狀態(tài),等待服務(wù)器端關(guān)閉連接。

第三次揮手(FIN=1,seq=w)

服務(wù)器端準(zhǔn)備好關(guān)閉連接時(shí),向客戶端發(fā)送結(jié)束連接請(qǐng)求,F(xiàn)IN置為1。發(fā)送完畢后,服務(wù)器端進(jìn)入 LAST_ACK 狀態(tài),等待來(lái)自客戶端的最后一個(gè)ACK。

第四次揮手(ACK=1,ACKnum=w+1)

客戶端接收到來(lái)自服務(wù)器端的關(guān)閉請(qǐng)求,發(fā)送一個(gè)確認(rèn)包,并進(jìn)入 TIME_WAIT狀態(tài),等待可能出現(xiàn)的要求重傳的 ACK包。服務(wù)器端接收到這個(gè)確認(rèn)包之后,關(guān)閉連接,進(jìn)入 CLOSED 狀態(tài)。

【問題1】為什么連接的時(shí)候是三次握手,關(guān)閉的時(shí)候卻是四次握手?

答:三次握手是因?yàn)橐驗(yàn)楫?dāng)Server端收到Client端的SYN連接請(qǐng)求報(bào)文后,可以直接發(fā)送SYN+ACK報(bào)文。其中ACK報(bào)文是用來(lái)應(yīng)答的,SYN報(bào)文是用來(lái)同步的。但是關(guān)閉連接時(shí),當(dāng)Server端收到FIN報(bào)文時(shí),很可能并不會(huì)立即關(guān)閉SOCKET(因?yàn)榭赡苓€有消息沒處理完),所以只能先回復(fù)一個(gè)ACK報(bào)文,告訴Client端,"你發(fā)的FIN報(bào)文我收到了"。只有等到我Server端所有的報(bào)文都發(fā)送完了,我才能發(fā)送FIN報(bào)文,因此不能一起發(fā)送。故需要四步握手。

【問題2】為什么TIME_WAIT狀態(tài)需要經(jīng)過2MSL(最大報(bào)文段生存時(shí)間)才能返回到CLOSE狀態(tài)?

答:雖然按道理,四個(gè)報(bào)文都發(fā)送完畢,我們可以直接進(jìn)入CLOSE狀態(tài)了,但是我們必須假象網(wǎng)絡(luò)是不可靠的,有可以最后一個(gè)ACK丟失。所以TIME_WAIT狀態(tài)就是用來(lái)重發(fā)可能丟失的ACK報(bào)文。

1.4.4 TCP協(xié)議的報(bào)文傳輸

連接建立好之后,就開始進(jìn)行數(shù)據(jù)包的傳輸了。那TCP作為一個(gè)可靠的通信協(xié)議,如何保證消息傳輸?shù)目煽啃阅兀?/p>

TCP采用了消息確認(rèn)的方式來(lái)保證數(shù)據(jù)報(bào)文傳輸?shù)陌踩?,也就是說客戶端發(fā)送了數(shù)據(jù)包到服務(wù)端后,服務(wù)端會(huì)返回一個(gè)確認(rèn)消息給到客戶端,如果客戶端沒有收到確認(rèn)包,則會(huì)重新再發(fā)送。

為了保證順序性,每一個(gè)包都有一個(gè) ID。在建立連接的時(shí)候,會(huì)商定起始的 ID 是什么,然后按照 ID 一個(gè)個(gè)發(fā)送。為了保證不丟包,對(duì)于發(fā)送的包都要進(jìn)行應(yīng)答,但是這個(gè)應(yīng)答也不是一個(gè)一個(gè)來(lái)的,而是會(huì)應(yīng)答某個(gè)之前的 ID,表示都收到了,這種模式稱為累計(jì)確認(rèn)或者累計(jì)應(yīng)答(cumulative acknowledgment)

如圖1-10所示,為了記錄所有發(fā)送的包和接收的包,TCP協(xié)議在發(fā)送端和接收端分別拿會(huì)有發(fā)送緩沖區(qū)和接收緩沖區(qū),TCP的全雙工的工作模式及TCP的滑動(dòng)窗口就是依賴于這兩個(gè)獨(dú)立的Buffer和該Buffer的填充狀態(tài)。

接收緩沖區(qū)把數(shù)據(jù)緩存到內(nèi)核,若應(yīng)用進(jìn)程一直沒有調(diào)用Socket的read方法進(jìn)行讀取,那么該數(shù)據(jù)會(huì)一直被緩存在接收緩沖區(qū)內(nèi)。不管進(jìn)程是否讀取Socket,對(duì)端發(fā)來(lái)的數(shù)據(jù)都會(huì)經(jīng)過內(nèi)核接收并緩存到Socket的內(nèi)核接收緩沖區(qū)。

read所要做的工作,就是把內(nèi)核接收緩沖區(qū)中的數(shù)據(jù)復(fù)制到應(yīng)用層用戶的Buffer里。進(jìn)程調(diào)用Socket的send發(fā)送數(shù)據(jù)的時(shí)候,一般情況下是將數(shù)據(jù)從應(yīng)用層用戶的Buffer里復(fù)制到Socket的內(nèi)核發(fā)送緩沖區(qū),然后send就會(huì)在上層返回。換句話說,send返回時(shí),數(shù)據(jù)不一定會(huì)被發(fā)送到對(duì)端。

image-20210822173920975

<center>圖1-10</center>

發(fā)送端/接收端的緩沖區(qū)中是按照包的 ID 一個(gè)個(gè)排列,根據(jù)處理的情況分成四個(gè)部分。

  • 第一部分:發(fā)送了并且已經(jīng)確認(rèn)的。
  • 第二部分:發(fā)送了并且尚未確認(rèn)的。需要等待確認(rèn)后,才能移除。
  • 第三部分:沒有發(fā)送,但是已經(jīng)等待發(fā)送的。
  • 第四部分:沒有發(fā)送,并且暫時(shí)還不會(huì)發(fā)送的。

這里的第三部分和第四部分之所以做一個(gè)區(qū)分,其實(shí)是因?yàn)門CP采用做了流量控制,這里采用了滑動(dòng)窗口的方式來(lái)實(shí)現(xiàn)流量整形,避免出現(xiàn)數(shù)據(jù)擁堵的情況。

image-20210822173129991

<center>圖1-11</center>

為了更好的理解數(shù)據(jù)包的通信過程,我們通過下面這個(gè)網(wǎng)址來(lái)演示一下

https://media.pearsoncmg.com/aw/ecs_kurose_compnetwork_7/cw/content/interactiveanimations/selective-repeat-protocol/index.html

1.4.5 滑動(dòng)窗口協(xié)議

上述地址中動(dòng)畫演示的部分,其實(shí)就是數(shù)據(jù)包發(fā)送和確認(rèn)機(jī)制,同時(shí)還涉及到互動(dòng)窗口協(xié)議。

滑動(dòng)窗口(Sliding window)是一種流量控制技術(shù)。早期的網(wǎng)絡(luò)通信中,通信雙方不會(huì)考慮網(wǎng)絡(luò)的擁擠情況直接發(fā)送數(shù)據(jù)。由于大家不知道網(wǎng)絡(luò)擁塞狀況,同時(shí)發(fā)送數(shù)據(jù),導(dǎo)致中間節(jié)點(diǎn)阻塞掉包,誰(shuí)也發(fā)不了數(shù)據(jù),所以就有了滑動(dòng)窗口機(jī)制來(lái)解決此問題;發(fā)送和接受方都會(huì)維護(hù)一個(gè)數(shù)據(jù)幀的序列,這個(gè)序列被稱作窗口

發(fā)送窗口

就是發(fā)送端允許連續(xù)發(fā)送的幀的序號(hào)表。

發(fā)送端可以不等待應(yīng)答而連續(xù)發(fā)送的最大幀數(shù)稱為發(fā)送窗口的尺寸。

接收窗口

接收方允許接收的幀的序號(hào)表,凡落在 接收窗口內(nèi)的幀,接收方都必須處理,落在接收窗口外的幀被丟棄。

接收方每次允許接收的幀數(shù)稱為接收窗口的尺寸。

1.5 理解阻塞通信的本質(zhì)

理解了TCP通信的原理后,在Java中我們會(huì)采用Socket套接字來(lái)實(shí)現(xiàn)網(wǎng)絡(luò)通信,下面這段代碼演示了Socket通信的案例。

public class ServerSocketExample {

    public static void main(String[] args) throws IOException {
        final int DEFAULT_PORT = 8080;
        ServerSocket serverSocket = null;
        serverSocket = new ServerSocket(DEFAULT_PORT);
        System.out.println("啟動(dòng)服務(wù),監(jiān)聽端口:" + DEFAULT_PORT);
        while (true) {
            Socket socket = serverSocket.accept();
            System.out.println("客戶端:" + socket.getPort() + "已連接");
            new Thread(new Runnable() {
                Socket socket;
                public Runnable setSocket(Socket s){
                    this.socket=s;
                    return this;
                }
                @Override
                public void run() {
                    try {
                        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                        String clientStr = null; //讀取一行信息
                        clientStr = bufferedReader.readLine();
                        System.out.println("客戶端發(fā)了一段消息:" + clientStr);
                        BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
                        bufferedWriter.write("我已經(jīng)收到你的消息了");
                        bufferedWriter.flush(); //清空緩沖區(qū)觸發(fā)消息發(fā)送
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }.setSocket(socket)).start();

        }
    }
}

在我們講Redis的專題中詳細(xì)講到過,上述通信是BIO模型,也就是阻塞通信模型,阻塞主要體現(xiàn)的點(diǎn)是

  • accept,阻塞等待客戶端連接
  • io阻塞,阻塞等待客戶端的數(shù)據(jù)傳輸。

相信大家和我一樣有一些以后,這個(gè)阻塞和喚醒到底是怎么回事,下面我們簡(jiǎn)單來(lái)了解一下。

1.5.1 阻塞操作的本質(zhì)

阻塞是指進(jìn)程在等待某個(gè)事件發(fā)生之前的等待狀態(tài),它是屬于操作系統(tǒng)層面的調(diào)度,我們通過下面操作來(lái)追蹤Java程序中有多少程序,每一個(gè)線程對(duì)內(nèi)核產(chǎn)生了哪些操作。

strace,Linux操作系統(tǒng)中的指令

  1. 把ServerSocketExample.java,去掉package導(dǎo)入頭,拷貝到linux服務(wù)器的 /data/app目錄下。

  2. 使用javac ServerSocketExample.java進(jìn)行編譯,得到.class文件

  3. 使用下面這個(gè)命令來(lái)追蹤(打開一個(gè)新窗口)

    按照strace官網(wǎng)的描述, strace是一個(gè)可用于診斷、調(diào)試和教學(xué)的Linux用戶空間跟蹤器。我們用它來(lái)監(jiān)控用戶空間進(jìn)程和內(nèi)核的交互,比如系統(tǒng)調(diào)用、信號(hào)傳遞、進(jìn)程狀態(tài)變更等。

    strace -ff -o out java ServerSocketExample
    
    • -f 跟蹤目標(biāo)進(jìn)程,以及目標(biāo)進(jìn)程創(chuàng)建的所有子進(jìn)程
    • -o 把strace的輸出單獨(dú)寫到指定的文件
  4. 上述指令執(zhí)行完成后,會(huì)在/data/app目錄下得到很多out.*的文件,每個(gè)文件代表一個(gè)線程。因?yàn)镴ava本身是多線程的。

    [root@localhost app]# ll
    total 748
    -rw-r--r--. 1 root root  14808 Aug 23 12:51 out.33320 //最小的表示主線程
    -rw-r--r--. 1 root root 186893 Aug 23 12:51 out.33321
    -rw-r--r--. 1 root root    961 Aug 23 12:51 out.33322
    -rw-r--r--. 1 root root    917 Aug 23 12:51 out.33323
    -rw-r--r--. 1 root root    833 Aug 23 12:51 out.33324
    -rw-r--r--. 1 root root    819 Aug 23 12:51 out.33325
    -rw-r--r--. 1 root root  23627 Aug 23 12:53 out.33326
    -rw-r--r--. 1 root root   1326 Aug 23 12:51 out.33327
    -rw-r--r--. 1 root root   1144 Aug 23 12:51 out.33328
    -rw-r--r--. 1 root root   1270 Aug 23 12:51 out.33329
    -rw-r--r--. 1 root root   8136 Aug 23 12:53 out.33330
    -rw-r--r--. 1 root root   8158 Aug 23 12:53 out.33331
    -rw-r--r--. 1 root root   6966 Aug 23 12:53 out.33332
    -rw-r--r--. 1 root root   1040 Aug 23 12:51 out.33333
    -rw-r--r--. 1 root root 445489 Aug 23 12:53 out.33334
    
  5. 打開out.33321這個(gè)文件(主線程后面的一個(gè)文件),shift+g到該文件的尾部,可以看到如下內(nèi)容。

    下面這些方法,都是屬于系統(tǒng)調(diào)用,也就是調(diào)用操作系統(tǒng)提供的內(nèi)核指令觸發(fā)相關(guān)的操作。

    # 創(chuàng)建socket fd 
    socket(AF_INET6, SOCK_STREAM, IPPROTO_IP) = 5 
    ....
    # 綁定8888端口
    bind(5, {sa_family=AF_INET6, sin6_port=htons(8888), inet_pton(AF_INET6, "::", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, 28) = 0
    # 創(chuàng)建一個(gè)socket并監(jiān)聽申請(qǐng)的連接, 5表示sockfd,50表示等待隊(duì)列的最大長(zhǎng)度
    listen(5, 50)                           = 0
    mprotect(0x7f21d00df000, 4096, PROT_READ|PROT_WRITE) = 0
    write(1, "\345\220\257\345\212\250\346\234\215\345\212\241\357\274\214\347\233\221\345\220\254\347\253\257\345\217\243\357\274\23288"..., 34) = 34
    write(1, "\n", 1)                       = 1
    lseek(3, 58916778, SEEK_SET)            = 58916778
    read(3, "PK\3\4\n\0\0\10\0\0U\23\213O\336\274\205\24X8\0\0X8\0\0\25\0\0\0", 30) = 30
    lseek(3, 58916829, SEEK_SET)            = 58916829
    read(3, "\312\376\272\276\0\0\0004\1\367\n\0\6\1\37\t\0\237\1 \t\0\237\1!\t\0\237\1\"\t\0"..., 14424) = 14424
    # poll, 把當(dāng)前的文件指針掛到等待隊(duì)列,文件指針指的是fd=5,簡(jiǎn)單來(lái)說就是讓當(dāng)前進(jìn)程阻塞,直到有事件觸發(fā)喚醒
    * events: 表示請(qǐng)求事件,POLLIN(普通或優(yōu)先級(jí)帶數(shù)據(jù)可讀)、POLLERR,發(fā)生錯(cuò)誤。
    poll([{fd=5, events=POLLIN|POLLERR}], 1, -1
    

從這個(gè)代碼中可以看到,Socket的accept方法最終是調(diào)用系統(tǒng)的poll函數(shù)來(lái)實(shí)現(xiàn)線程阻塞的。

通過在linux服務(wù)器輸入 man 2 poll

man: 幫助手冊(cè)

2: 表示系統(tǒng)調(diào)用相關(guān)的函數(shù)

DESCRIPTION
       poll()  performs  a  similar  task  to  select(2): it waits for one of a set of file
       descriptors to become ready to perform I/O.

poll類似于select函數(shù),它可以等待一組文件描述符中的IO就緒事件

  1. 通過下面命令訪問socket server。

    telnet 192.168.221.128 8888
    

    這個(gè)時(shí)候通過tail -f out.33321這個(gè)文件,發(fā)現(xiàn)被阻塞的poll()方法,被POLLIN事件喚醒了,表示監(jiān)聽到了一次連接。

    poll([{fd=5, events=POLLIN|POLLERR}], 1, -1) = 1 ([{fd=5, revents=POLLIN}])
    accept(5, {sa_family=AF_INET6, sin6_port=htons(53778), inet_pton(AF_INET6, "::ffff:192.168.221.1", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, [28]) = 6
    

1.5.2 阻塞被喚醒的過程

如圖1-12所示,網(wǎng)絡(luò)數(shù)據(jù)包通過網(wǎng)線傳輸?shù)侥繕?biāo)服務(wù)器的網(wǎng)卡,再通過2所示的硬件電路傳輸,最終把數(shù)據(jù)寫入到內(nèi)存中的某個(gè)地址上,接著網(wǎng)卡通過中斷信號(hào)通知CPU有數(shù)據(jù)到達(dá),操作系統(tǒng)就知道當(dāng)前有新的數(shù)據(jù)包傳遞過來(lái),于是CPU開始執(zhí)行中斷程序,中斷程序的主要邏輯是

  • 先把網(wǎng)卡接收到的數(shù)據(jù)寫入到對(duì)應(yīng)的Socket接收緩沖區(qū)中
  • 再喚醒被阻塞在poll()方法上的線程
img

<center>圖1-12</center>

1.5.3 阻塞的整體原理分析

操作系統(tǒng)為了支持多任務(wù)處理,所以實(shí)現(xiàn)了進(jìn)程調(diào)度功能,運(yùn)行中的進(jìn)程表示獲得了CPU的使用權(quán),當(dāng)進(jìn)程(線程)因?yàn)槟承┎僮鲗?dǎo)致阻塞時(shí),就會(huì)釋放CPU使用權(quán),使得操作系統(tǒng)能夠多任務(wù)的執(zhí)行。

當(dāng)多個(gè)進(jìn)程是運(yùn)行狀態(tài)等待CPU調(diào)度時(shí),這些進(jìn)程會(huì)保存到一個(gè)可運(yùn)行隊(duì)列中,如圖1-13所示。

image-20210823143314215

<center>圖1-13</center>
當(dāng)進(jìn)程A執(zhí)行創(chuàng)建Socket語(yǔ)句時(shí),在Linux操作系統(tǒng)中會(huì)創(chuàng)建一個(gè)由文件系統(tǒng)管理的Socket對(duì)象,這個(gè)Socket對(duì)象包含發(fā)送緩沖區(qū)、接收緩沖區(qū)、等待隊(duì)列等,其中等待隊(duì)列是非常重要的結(jié)構(gòu),它指向所有需要等待當(dāng)前Socket事件的進(jìn)程,如圖1-14所示。

當(dāng)進(jìn)程A調(diào)用poll()方法阻塞時(shí),操作系統(tǒng)會(huì)把當(dāng)前進(jìn)程A從工作隊(duì)列移動(dòng)到Socket的等待隊(duì)列中(將進(jìn)程A的指針指向等待隊(duì)列,后續(xù)需要進(jìn)行喚醒),此時(shí)A被阻塞,CPU繼續(xù)執(zhí)行下一個(gè)進(jìn)程。

image-20210823145055784

<center>圖1-14</center>

當(dāng)Socket收到數(shù)據(jù)時(shí),等待該Socket FD的進(jìn)程會(huì)收到被喚醒,如圖1-15所示,計(jì)算機(jī)通過網(wǎng)卡接收到客戶端傳過來(lái)的數(shù)據(jù),網(wǎng)卡會(huì)把這個(gè)數(shù)據(jù)寫入到內(nèi)存,然后再通過中斷信號(hào)通知CPU有數(shù)據(jù)到達(dá),于是CPU開始執(zhí)行中斷程序。

當(dāng)發(fā)生了中斷,就意味著需要操作系統(tǒng)的介入,開展管理工作。由于操作系統(tǒng)的管理工作(如進(jìn)程切換、分配IO設(shè)備)需要使用特權(quán)指令,因此CPU要從用戶態(tài)轉(zhuǎn)換為核心態(tài)。中斷就可以使CPU從用戶態(tài)轉(zhuǎn)換為核心態(tài),使操作系統(tǒng)獲得計(jì)算機(jī)的控制權(quán)。因此,有了中斷,才能實(shí)現(xiàn)多道程序并發(fā)執(zhí)行。

此處的中斷程序主要有兩項(xiàng)功能,先將網(wǎng)絡(luò)數(shù)據(jù)寫入到對(duì)應(yīng) Socket 的接收緩沖區(qū)里面(步驟 ④),再喚醒進(jìn)程 A(步驟 ⑤),重新將進(jìn)程 A 放入工作隊(duì)列中。

image-20210823150147501

<center>圖1-15</center>

1.5 Linux中的select/poll模型本質(zhì)

前面在1.4節(jié)中講的其實(shí)是Recv()方法,它只能監(jiān)視單個(gè)Socket。而在實(shí)際應(yīng)用中,這種單Socket監(jiān)聽很明顯會(huì)影響到客戶端連接數(shù),所以我們需要尋找一種能夠同時(shí)監(jiān)聽多個(gè)Socket的方法,而select/poll就是在這個(gè)背景下產(chǎn)生的,其中poll方法在前面的案例中就講過,默認(rèn)情況下使用poll模型。

先來(lái)了解一下select模型,由于在前面的分析中我們知道Recv()只能實(shí)現(xiàn)對(duì)單個(gè)socket的監(jiān)聽,當(dāng)客戶端連接數(shù)較多的時(shí)候,會(huì)導(dǎo)致吞吐量非常低,所以我們想,能不能實(shí)現(xiàn)同時(shí)監(jiān)聽多個(gè)socket,只要任何一個(gè)socket連接存在IO就緒事件,就觸發(fā)進(jìn)程的喚醒。

如圖1-16所示,假設(shè)程序同時(shí)監(jiān)聽socket1和socket2這兩個(gè)socket連接,那么當(dāng)應(yīng)用程序調(diào)用select方法后,操作系統(tǒng)會(huì)把進(jìn)程A分別指向這連個(gè)個(gè)socket的等待隊(duì)列中。當(dāng)任何一個(gè)Socket收到數(shù)據(jù)后,中斷程序會(huì)喚醒對(duì)應(yīng)的進(jìn)程。

當(dāng)進(jìn)程 A 被喚醒后,它知道至少有一個(gè) Socket 接收了數(shù)據(jù)。程序只需遍歷一遍 Socket 列表,就可以得到就緒的 Socket。

image-20210823153708311

<center>圖1-16</center>

select模式有二個(gè)問題,

  • 就是每次調(diào)用select都需要將進(jìn)程加入到所有監(jiān)視器socket的等待隊(duì)列,每次喚醒都需要從等待隊(duì)列中移除,這里涉及到兩次遍歷,有一定的性能開銷。
  • 進(jìn)程被喚醒后,并不知道哪些socket收到了數(shù)據(jù),所以還需要遍歷一次所有的socket,得到就緒的socket列表

由于這兩個(gè)問題產(chǎn)生的性能影響,所以select默認(rèn)規(guī)定只能監(jiān)視1024個(gè)socket,雖然可以通過修改監(jiān)視的文件描述符數(shù)量,但是這樣會(huì)降低效率。而poll模式和select基本是一樣,最大的區(qū)別是poll沒有最大文件描述符限制。

1.6 Linux中的epoll模型

有沒有更加高效的方法,能夠減少遍歷也能達(dá)到同時(shí)監(jiān)聽多個(gè)fd的目的呢?epoll模型就可以解決這個(gè)問題。

epoll 其實(shí)是event poll的組合,它和select最大的區(qū)別在于,epoll會(huì)把哪個(gè)socket發(fā)生了什么樣的IO事件通知給應(yīng)用程序,所以epoll實(shí)際上就是事件驅(qū)動(dòng),具體原理如圖1-17所示。

在epoll中提供了三個(gè)方法分別是epoll_create、epoll_ctl、epoll_wait。具體執(zhí)行流程如下

  • 首先調(diào)用epoll_create方法,在內(nèi)核創(chuàng)建一個(gè)eventpoll對(duì)象,這個(gè)對(duì)象會(huì)維護(hù)一個(gè)epitem集合,它是一個(gè)紅黑樹結(jié)構(gòu)。這個(gè)集合簡(jiǎn)單理解成fd集合。
  • 接著調(diào)用epoll_ctl函數(shù)將當(dāng)前fd封裝成epitem加入到eventpoll對(duì)象中,并給這個(gè)epitem加入一個(gè)回調(diào)函數(shù)注冊(cè)到內(nèi)核。當(dāng)這個(gè)fd收到網(wǎng)絡(luò)IO事件時(shí),會(huì)把該fd對(duì)應(yīng)的epitem加入到eventpoll中的就緒列表rdlist(雙向鏈表)中。同時(shí)再喚醒被阻塞的進(jìn)程A。
  • 進(jìn)程A繼續(xù)調(diào)用epoll_wait方法,直接讀取epoll中就緒隊(duì)列rdlist中的epitem,如果rdlist隊(duì)列為空,則阻塞等待或者等待超時(shí)。

從epoll的原理中可以得知,由于rdlist的存在,使得進(jìn)程A被喚醒后知道哪些Socket(fd)發(fā)生了IO事件,從而在不需要遍歷的情況下獲取所有就緒的socket連接。

image-20210823212943850

<center>圖1-17</center>

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1.網(wǎng)絡(luò)協(xié)議在分布式中地位 分布式環(huán)境下重要的特點(diǎn):任務(wù)分解和網(wǎng)絡(luò)通信,其中網(wǎng)絡(luò)協(xié)議在分布式環(huán)境中承擔(dān)著不可缺少的...
    老街碼屋閱讀 1,497評(píng)論 0 1
  • TCP/IP TCP/IP(Transmission Control Protocol/Internet Prot...
    猿日記閱讀 491評(píng)論 0 1
  • 網(wǎng)絡(luò)的知識(shí)比較復(fù)雜。我曾經(jīng)看過一本書叫做計(jì)算機(jī)網(wǎng)絡(luò)自頂向下方法,很可惜自己沒有耐心把它看完,只是看了前面的幾個(gè)章節(jié)...
    Mrsunup閱讀 970評(píng)論 0 0
  • Valentine 轉(zhuǎn)載請(qǐng)標(biāo)明出處。 一個(gè)http請(qǐng)求,在整個(gè)網(wǎng)絡(luò)中的請(qǐng)求過程 當(dāng)應(yīng)用程序TCP傳送數(shù)據(jù)時(shí),數(shù)據(jù)被...
    valentine_liang閱讀 627評(píng)論 0 3
  • 網(wǎng)絡(luò)如何通信 我們要理解網(wǎng)絡(luò)中進(jìn)程如何通信,得解決兩個(gè)問題:a、我們要如何標(biāo)識(shí)一臺(tái)主機(jī),即怎樣確定我們將要通信的進(jìn)...
    青菜白玉堂閱讀 594評(píng)論 0 0

友情鏈接更多精彩內(nèi)容