做IT相關(guān)的工作,,肯定都離不開網(wǎng)絡(luò),,網(wǎng)絡(luò)中最重要的協(xié)議是TCP,。無論是實際工作還是筆試面試,你看哪里能少得了TCP?我看過RFC中與TCP相關(guān)的文檔,,也看過linux中TCP相關(guān)的源碼,,也看過不少框架中的TCP相關(guān)的代碼,對TCP是有點感覺了,。
一直想找個時間來分享下TCP相關(guān)的知識,,如果大家有疑問,歡迎相互交流,。其實,,搞透了TCP之后,發(fā)現(xiàn)它也就那么回事,。
考慮最簡單的情況:兩臺主機之間的通信,。這個時候只需要一條網(wǎng)線把兩者連起來,規(guī)定好彼此的硬件接口,,如都用USB,、電壓10v、頻率2.4GHz等,,這一層就是物理層,,這些規(guī)定就是物理層協(xié)議。
我們當然不滿足于只有兩臺電腦連接,,因此我們可以使用交換機把多個電腦連接起來,,如下圖:
這樣連接起來的網(wǎng)絡(luò),稱為局域網(wǎng),,也可以稱為以太網(wǎng)(以太網(wǎng)是局域網(wǎng)的一種),。在這個網(wǎng)絡(luò)中,我們需要標識每個機器,,這樣才可以指定要和哪個機器通信,。這個標識就是硬件地址MAC。硬件地址隨機器的生產(chǎn)就被確定,永久性唯一,。在局域網(wǎng)中,,我們需要和另外的機器通信時,只需要知道他的硬件地址,,交換機就會把我們的消息發(fā)送到對應(yīng)的機器,。
這里我們可以不管底層的網(wǎng)線接口如何發(fā)送,把物理層抽離,,在他之上創(chuàng)建一個新的層次,,這就是數(shù)據(jù)鏈路層。
我們依然不滿足于局域網(wǎng)的規(guī)模,,需要把所有的局域網(wǎng)聯(lián)系起來,,這個時候就需要用到路由器來連接兩個局域網(wǎng):
但是如果我們還是使用硬件地址來作為通信對象的唯一標識,那么當網(wǎng)絡(luò)規(guī)模越來越大,,需要記住所有機器的硬件地址是不現(xiàn)實的,;同時,一個網(wǎng)絡(luò)對象可能會頻繁更換設(shè)備,,這個時候硬件地址表維護起來更加復(fù)雜,。這里使用了一個新的地址來標記一個網(wǎng)絡(luò)對象:IP地址。
通過一個簡單的寄信例子來理解IP地址,。
我住在北京市,,我朋友A住在上海市,我要給朋友A寫信:
寫完信,,我會在信上寫好我朋友A的地址,,并放到北京市郵局(給信息附加目標IP地址,并發(fā)送給路由器)
郵局會幫我把信運輸?shù)缴虾J挟數(shù)剜]局(信息會經(jīng)過路由傳遞到目標IP局域網(wǎng)的路由器)
上海市當?shù)芈酚善鲿臀野研沤唤o朋友A(局域網(wǎng)內(nèi)通信)
因此,,這里IP地址就是一個網(wǎng)絡(luò)接入地址(朋友A的住址),,我只需要知道目標IP地址,,路由器就可以把消息給我?guī)У健?strong>在局域網(wǎng)中,,就可以動態(tài)維護一個MAC地址與IP地址的映射關(guān)系,根據(jù)目的IP地址就可以尋找到機器的MAC地址進行發(fā)送,。
這樣我們不需管理底層如何去選擇機器,,我們只需要知道IP地址,就可以和我們的目標進行通信,。這一層就是網(wǎng)絡(luò)層,。網(wǎng)絡(luò)層的核心作用就是提供主機之間的邏輯通信。這樣,,在網(wǎng)絡(luò)中的所有主機,,在邏輯上都連接起來了,上層只需要提供目標IP地址和數(shù)據(jù),網(wǎng)絡(luò)層就可以把消息發(fā)送到對應(yīng)的主機,。
一個主機有多個進程,,進程之間進行不同的網(wǎng)絡(luò)通信,如邊和朋友開黑邊和女朋友聊微信,。我的手機同時和兩個不同機器進行通信,。那么當我的手機收到數(shù)據(jù)時,如何區(qū)分是微信的數(shù)據(jù),,還是王者的數(shù)據(jù),?那么就必須在網(wǎng)絡(luò)層之上再添加一層:運輸層 :
運輸層通過socket(套接字),將網(wǎng)絡(luò)信息進行進一步的拆分,,不同的應(yīng)用進程可以獨立進行網(wǎng)絡(luò)請求,,互不干擾。這就是運輸層的最本質(zhì)特點:提供進程之間的邏輯通信,。這里的進程可以是主機之間,,也可以是同個主機,所以在android中,,socket通信也是進程通信的一種方式,。
現(xiàn)在不同的機器上的應(yīng)用進程之間可以獨立通信了,那么我們就可以在計算機網(wǎng)絡(luò)上開發(fā)出形形式式的應(yīng)用:如web網(wǎng)頁的http,,文件傳輸ftp等等,。這一層稱為應(yīng)用層。
應(yīng)用層還可以進一步拆分出表示層,、會話層,,但他們的本質(zhì)特點都沒有改變:完成具體的業(yè)務(wù)需求 。和下面的四層相比,,他們并不是必須的,,可以歸屬到應(yīng)用層中。
最后對計網(wǎng)分層進行小結(jié):
最底層物理層,,負責兩個機器之間通過硬件的直接通信,;
數(shù)據(jù)鏈路層使用硬件地址在局域網(wǎng)中進行尋址,實現(xiàn)局域網(wǎng)通信,;
網(wǎng)絡(luò)層通過抽象IP地址實現(xiàn)主機之間的邏輯通信,;
運輸層在網(wǎng)絡(luò)層的基礎(chǔ)上,對數(shù)據(jù)進行拆分,,實現(xiàn)應(yīng)用進程的獨立網(wǎng)絡(luò)通信,;
應(yīng)用層在運輸層的基礎(chǔ)上,根據(jù)具體的需求開發(fā)形形式式的功能,。
這里需要注意的是,,分層并不是在物理上的分層,,而是邏輯上的分層。通過對底層邏輯的封裝,,使得上層的開發(fā)可以直接依賴底層的功能而無需理會具體的實現(xiàn),,簡便了開發(fā)。
這種分層的思路,,也就是責任鏈設(shè)計模式,,通過層層封裝,把不同的職責獨立起來,,更加方便開發(fā),、維護等等。okHttp中的攔截器設(shè)計模式,,也是這種責任鏈模式,。
/ 運輸層 /
本文主要是講解TCP,這里需要增加一些運輸層的知識,。
本質(zhì):提供進程通信
在運輸層之下的網(wǎng)絡(luò)層,,是不知道該數(shù)據(jù)包屬于哪個進程,他只負責數(shù)據(jù)包的接收與發(fā)送,。運輸層則負責接收不同進程的數(shù)據(jù)交給網(wǎng)絡(luò)層,,同時把網(wǎng)絡(luò)層的數(shù)據(jù)拆分交給不同的進程。從上往下匯聚到網(wǎng)絡(luò)層,,稱為多路復(fù)用,,從下往上拆分,稱為多路拆分,。
運輸層的表現(xiàn),,受網(wǎng)絡(luò)層的限制。這很好理解,,網(wǎng)絡(luò)層是運輸層的底層支持,。所以運輸層是無法決定自己帶寬、時延等的上限,。但可以基于網(wǎng)絡(luò)層開發(fā)更多的特性:如可靠傳輸,。網(wǎng)絡(luò)層只負責盡力把數(shù)據(jù)包從一端發(fā)送到另一端,而不保證數(shù)據(jù)可以到達且完整,。
底層實現(xiàn):socket
前面講到,,最簡單的運輸層協(xié)議,,就是提供進程之間的獨立通信 ,,但底層的實現(xiàn),是socket之間的獨立通信,。在網(wǎng)絡(luò)層中,,IP地址是一個主機邏輯地址,,而在運輸層中,socket是一個進程的邏輯地址,;當然,,一個進程可以擁有多個socket。應(yīng)用進程可以通過監(jiān)聽socket,,來獲取這個socket接受到的消息,。
socket并不是一個實實在在的東西,而是運輸層抽象出來的一個對象,。運輸層增加了端口這個概念,,來區(qū)分不同的socket。端口可以理解為一個主機上有很多的網(wǎng)絡(luò)通信口,,每個端口都有一個端口號,,端口的數(shù)量由運輸層協(xié)議確定。
不同的運輸層協(xié)議對socket有不同的定義方式,。在UDP協(xié)議中,,使用目標IP+目標端口號來定義一個socket;在TCP中使用目標IP+目標端口號+源IP+源端口號來定義一個socket,。我們只需要在運輸層報文的頭部附加上這些信息,,目標主機就會知道我們要發(fā)送給哪個socket,對應(yīng)監(jiān)聽該socket的進程就可獲得信息,。
運輸層協(xié)議
運輸層的協(xié)議就是大名鼎鼎的TCP和UDP,。其中,UDP是最精簡的運輸層協(xié)議,,只實現(xiàn)了進程間的通信,;而TCP在UDP的基礎(chǔ)上,實現(xiàn)了可靠傳輸,、流量控制,、擁塞控制、面向連接等等特性,,同時也更加復(fù)雜,。
當然除此之外,還有更多更優(yōu)秀的運輸層協(xié)議,,但目前廣為使用的,,就是TCP和UDP。UDP在后面也會總結(jié)到,。
/ TCP協(xié)議首部 /
TCP協(xié)議,,表現(xiàn)在報文上,就是會在應(yīng)用層傳輸下來的數(shù)據(jù)前附加上一個TCP首部,,這個首部附加了TCP信息,,先來整體看一下這個首部的結(jié)構(gòu):
這張圖是來自一位大學(xué)老師的課件,, 非常好用,所以一直拿來學(xué)習(xí),。最下面部分表示了報文之間的關(guān)系,,TCP數(shù)據(jù)部分就是應(yīng)用層傳下來的數(shù)據(jù)。
TCP首部固定長度是20字節(jié),,下面還有4字節(jié)是可選的,。內(nèi)容很多,但其中有一些我們比較熟悉的:源端口,,目標端口,。嗯?socket不是還需要IP進行定位嗎,?IP地址在網(wǎng)絡(luò)層被附加了,。其他的內(nèi)容后面都會慢慢講解,作為一篇總結(jié)文章,,這里放出查閱表,,方便復(fù)習(xí):
選項字段中包含以下其他選項:
講完下面內(nèi)容,再回來看這些字段就熟悉了,。
/ TCP面向字節(jié)流特性 /
TCP并不是把應(yīng)用層傳輸過來的數(shù)據(jù)直接加上首部然后發(fā)送給目標,,而是把數(shù)據(jù)看成一個字節(jié) 流,給他們標上序號之后分部分發(fā)送,。這就是TCP的 面向字節(jié)流特性:
TCP會以流的形式從應(yīng)用層讀取數(shù)據(jù)并存放在自己的發(fā)送緩存區(qū)中,,同時為這些字節(jié)標上序號
TCP會從發(fā)送方緩沖區(qū)選擇適量的字節(jié)組成TCP報文,通過網(wǎng)絡(luò)層發(fā)送給目標
目標會讀取字節(jié)并存放在自己的接收方緩沖區(qū)中,,并在合適的時候交付給應(yīng)用層
面向字節(jié)流的好處是無需一次存儲過大的數(shù)據(jù)占用太多內(nèi)存,,壞處是無法知道這些字節(jié)代表的意義,例如應(yīng)用層發(fā)送一個音頻文件和一個文本文件,,對于TCP來說就是一串字節(jié)流,,沒有意義可言,這會導(dǎo)致粘包以及拆包問題,,后面講,。
/ 可靠傳輸原理 /
前面講到,TCP是可靠傳輸協(xié)議,,也就是,,一個數(shù)據(jù)交給他,他肯定可以完整無誤地發(fā)送到目標地址,,除非網(wǎng)絡(luò)炸了,。他實現(xiàn)的網(wǎng)絡(luò)模型如下:
對于應(yīng)用層來說,他就是一個可靠傳輸?shù)牡讓又С址?wù),;而運輸層底層采用了網(wǎng)絡(luò)層的不可靠傳輸,。雖然在網(wǎng)絡(luò)層甚至數(shù)據(jù)鏈路層就可以使用協(xié)議來保證數(shù)據(jù)傳輸?shù)目煽啃裕@樣網(wǎng)絡(luò)的設(shè)計會更加復(fù)雜,、效率會隨之降低,。把數(shù)據(jù)傳輸?shù)目煽啃员WC放在運輸層,會更加合適,。
可靠傳輸原理的重點總結(jié)一下有:滑動窗口,、超時重傳、累積確認,、選擇確認,、連續(xù)ARQ。
停止等待協(xié)議
要實現(xiàn)可靠傳輸,,最簡便的方法就是:我發(fā)送一個數(shù)據(jù)包給你,,然后你跟我回復(fù)收到,我繼續(xù)發(fā)送下一個數(shù)據(jù)包,。傳輸模型如下:
這種“一來一去”的方法來保證傳輸可靠就是停止等待協(xié)議(stop-and-wait),。不知道還記不記得前面TCP首部有一個ack字段,當他設(shè)置為1的時候,,表示這個報文是一個確認收到報文,。
然后再來考慮一種情況:丟包。網(wǎng)絡(luò)環(huán)境不可靠,,導(dǎo)致每一次發(fā)送的數(shù)據(jù)包可能會丟失,,如果機器A發(fā)送了數(shù)據(jù)包丟失了,那么機器B永遠接收不到數(shù)據(jù),,機器A永遠在等待,。解決這個問題的方法是:超時重傳。當機器A發(fā)出一個數(shù)據(jù)包時便開始計時,,時間到還沒收到確認回復(fù),,就可以認為是發(fā)生了丟包,便再次發(fā)送,,也就是重傳,。
但重傳會導(dǎo)致另一種問題:如果原先的數(shù)據(jù)包并沒有丟失,只是在網(wǎng)絡(luò)中待的時間比較久,,這個時候機器B會受到兩個數(shù)據(jù)包,,那么機器B是如何辨別這兩個數(shù)據(jù)包是屬于同一份數(shù)據(jù)還是不同的數(shù)據(jù)?這就需要前面講過的方法:給數(shù)據(jù)字節(jié)進行編號,。這樣接收方就可以根據(jù)數(shù)據(jù)的字節(jié)編號,,得出這些數(shù)據(jù)是接下來的數(shù)據(jù),還是重傳的數(shù)據(jù),。
在TCP首部有兩個字段:序號和確認號,,他們表示發(fā)送方數(shù)據(jù)第一個字節(jié)的編號,,和接收方期待的下一份數(shù)據(jù)的第一個字節(jié)的編號。前面講到TCP是面向字節(jié)流,,但是他并不是一個字節(jié)一個字節(jié)地發(fā)送,,而是一次截取一整段。截取的長度受多種因素影響,,如緩存區(qū)的數(shù)據(jù)大小,、數(shù)據(jù)鏈路層限制的幀大小等。
連續(xù)ARQ協(xié)議
停止等待協(xié)議已經(jīng)可以滿足可靠傳輸了,,但有一個致命缺點:效率太低,。發(fā)送方發(fā)送一個數(shù)據(jù)包之后便進入等待,這個期間并沒有干任何事,,浪費了資源,。解決的方法是:連續(xù)發(fā)送數(shù)據(jù)包。模型如下:
和停止等待最大的不同就是,,他會源源不斷地發(fā)送,,接收方源源不斷收到數(shù)據(jù)之后,逐一進行確認回復(fù),。這樣便極大地提高了效率,。但同樣,帶來了一些額外的問題:
發(fā)送是否可以無限發(fā)送直到把緩沖區(qū)所有數(shù)據(jù)發(fā)送完,?不可以,。因為需要考慮接收方緩沖區(qū)以及讀取數(shù)據(jù)的能力。如果發(fā)送太快導(dǎo)致接收方無法接受,,那么只是會頻繁進行重傳,,浪費了網(wǎng)絡(luò)資源。所以發(fā)送方發(fā)送數(shù)據(jù)的范圍,,需要考慮到接收方緩沖區(qū)的情況,。這就是TCP的流量控制。解決方法是:滑動窗口,?;灸P腿缦拢?/p>
發(fā)送方需要根據(jù)接收方的緩沖區(qū)大小,設(shè)置自己的可發(fā)送窗口大小,,處于窗口內(nèi)的數(shù)據(jù)表示可發(fā)送,,之外的數(shù)據(jù)不可發(fā)送。
當窗口內(nèi)的數(shù)據(jù)接收到確認回復(fù)時,,整個窗口會往前移動,,直到發(fā)送完成所有的數(shù)據(jù)
在TCP的首部有一個窗口大小字段,他表示接收方的剩余緩沖區(qū)大小,讓發(fā)送方可以調(diào)整自己的發(fā)送窗口大小,。通過滑動窗口,,就可以實現(xiàn)TCP的流量控制,不至于發(fā)送太快,,導(dǎo)致太多的數(shù)據(jù)丟失,。
連續(xù)ARQ帶來的第二個問題是:網(wǎng)絡(luò)中充斥著和發(fā)送數(shù)據(jù)包一樣數(shù)據(jù)量的確認回復(fù)報文,因為每一個發(fā)送數(shù)據(jù)包,,必須得有一個確認回復(fù),。提高網(wǎng)絡(luò)效率的方法是:累積確認 ,。接收方不需要逐個進行回復(fù),,而是累積到一定量的數(shù)據(jù)包之后,告訴發(fā)送方,,在此數(shù)據(jù)包之前的數(shù)據(jù)全都收到,。例如,收到 1234,,接收方只需要告訴發(fā)送方我收到4了,,那么發(fā)送方就知道1234都收到了。
第三個問題是:如何處理丟包情況,。在停止等待協(xié)議中很簡單,,直接一個超時重傳就解決了。但,,連續(xù)ARQ中不太一樣,。例如:接收方收到了 123 567,六個字節(jié),,編號為4的字節(jié)丟失了,。按照累積確認的思路,只能發(fā)送3的確認回復(fù),,567都必須丟掉,,因為發(fā)送方會進行重傳。這就是GBN(go-back-n)思路,。
但是我們會發(fā)現(xiàn),,只需要重傳4即可,這樣不是很浪費資源,,所以就有了:選擇確認SACK,。在TCP報文的選項字段,可以設(shè)置已經(jīng)收到的報文段,,每一個報文段需要兩個邊界來進行確定,。這樣發(fā)送方,就可以根據(jù)這個選項字段只重傳丟失的數(shù)據(jù)了。
可靠傳輸小結(jié)
到這里關(guān)于TCP的可靠傳輸原理就已經(jīng)介紹的差不多,。最后進行一個小結(jié):
通過連續(xù)ARQ協(xié)議與發(fā)送-確認回復(fù)模式來保證每一個數(shù)據(jù)包都到達接收方
通過給字節(jié)編號的方法,,來標記每一個數(shù)據(jù)是屬于重傳還是新的數(shù)據(jù)
通過超時重傳的方式,來解決數(shù)據(jù)包在網(wǎng)絡(luò)中丟失的問題
通過滑動窗口來實現(xiàn)流量控制
通過累積確認+選擇確認的方法來提高確認回復(fù)與重傳的效率
當然,,這只是可靠傳輸?shù)谋揭唤?,感興趣可以再深入去研究(和面試官聊天已經(jīng)差不多了)。
/ 擁塞控制 /
擁塞控制考慮的是另外一個問題:避免網(wǎng)絡(luò)過分擁擠導(dǎo)致丟包嚴重,,網(wǎng)絡(luò)效率降低 ,。
拿現(xiàn)實的交通舉例子:
高速公路同一時間可通行的汽車數(shù)量是一定的,當節(jié)假日時,,就會發(fā)生嚴重的堵車,。在TCP中,數(shù)據(jù)包超時,,會進行重傳,,也就是會進來更多的汽車,這時候更堵,,最后導(dǎo)致的結(jié)果就是:丟包-重傳-丟包-重傳,。最后整個網(wǎng)絡(luò)癱瘓了。
這里的擁塞控制和前面的流量控制不是一個東西,,流量控制是擁塞控制的手段:為了避免擁塞,,必須對流量進行控制。擁塞控制目的是:限制每個主機的發(fā)送的數(shù)據(jù)量,,避免網(wǎng)絡(luò)擁塞效率下降,。就像廣州等地,限制車牌號出行是一個道理,。不然大家都堵在路上,,誰都別想走。
擁塞控制的解決方法是流量控制,,流量控制的實現(xiàn)是滑動窗口,,所以擁塞控制最終也是通過限制發(fā)送方的滑動窗口大小來限制流量 。當然,,擁塞控制的手段不只是流量控制,,導(dǎo)致?lián)砣囊蛩赜校郝酚善骶彺妗?、處理器處理速度等等,。提升硬件能力(?車道改成8車道)是其中一個方法,但畢竟硬件提升是有瓶頸的,,沒辦法不斷提升,,還是需要從tcp本身來增加算法,解決擁塞。
擁塞控制的重點有4個:慢開始,、快恢復(fù),、快重傳、擁塞避免,。這里依舊獻祭出大學(xué)老師的ppt圖片:
Y軸表示的是發(fā)送方窗口大小,,X軸表示的是發(fā)送的輪次(不是字節(jié)編號)。
最開始的時候,,會把窗口設(shè)置一個較小的值,,然后每輪變?yōu)樵瓉淼膬杀丁_@是慢開始,。
當窗口值到達ssthresh值,,這個值是需要通過實時網(wǎng)絡(luò)情況設(shè)置的一個窗口限制值,開始進入擁塞避免,,每輪把窗口值提升1,,慢慢試探網(wǎng)絡(luò)的底線,。
如果發(fā)生了數(shù)據(jù)超時,,表示極可能發(fā)生了擁塞,然后回到慢開始,,重復(fù)上面的步驟,。
如果收到三個相同的確認回復(fù),表示現(xiàn)在網(wǎng)絡(luò)的情況不太好,,把ssthresh的值設(shè)置為原來的一半,,繼續(xù)擁塞避免。這部分稱為快恢復(fù),。
如果收到丟包信息,,應(yīng)該盡快把丟失的包重傳一次,這是快重傳,。
當然,,窗口的最終上限是不能無限上漲的,他不能超過接收方的緩存區(qū)大小,。
通過這個算法,,就可以在很大程度上,避免網(wǎng)絡(luò)擁擠,。
除此之外,,還可以讓路由器在緩存即將滿的時候,告知發(fā)送方我快滿了,,而不是等到出現(xiàn)了超時再進行處理,,這是主動隊列管理AQM。此外還有很多方法,但是上面的算法是重點,。
/ 面向連接 /
這一小節(jié)講的就是無人不曉的TCP三次握手與四次揮手這些,,經(jīng)過前面的內(nèi)容,這一小節(jié)其實已經(jīng)很好理解,。
TCP是面向連接的,,那連接是什么?這里的連接并不是實實在在的連接,,而是通信雙方彼此之間的一個記錄,。TCP是一個全雙工通信,也就是可以互相發(fā)送數(shù)據(jù),,所以雙方都需要記錄對方的信息,。根據(jù)前面的可靠傳輸原理,TCP通信雙方需要為對方準備一個接收緩沖區(qū)可以接收對方的數(shù)據(jù),、記住對方的socket知道怎么發(fā)送數(shù)據(jù),、記住對方的緩沖區(qū)來調(diào)整自己的窗口大小等等,這些記錄,,就是一個連接,。
在運輸層小節(jié)中講到,運輸層雙方通信的地址是采用socket來定義的,,TCP也不例外,。TCP的每一個連接只能有兩個對象,也就是兩個socket,,而不能有三個,。所以socket的定義需要源IP、源端口號,、目標IP,、目標端口號四個關(guān)鍵因素,才不會發(fā)生混亂,。
假如TCP和UDP一樣只采用目標IP+目標端口號來定義socket,,那么就會出現(xiàn)多個發(fā)送方同時發(fā)送到同一個目標socket的情況。這個時候TCP無法區(qū)分這些數(shù)據(jù)是否來自不同的發(fā)送方,,就會導(dǎo)致出現(xiàn)錯誤,。
既然是連接,就有兩個關(guān)鍵要點:建立連接,、斷開連接,。
建立連接
建立連接的目的就是交換彼此的信息,然后記住對方的信息,。所以雙方都需要發(fā)送彼此的信息給對方:
但前面的可靠傳輸原理告訴我們,,數(shù)據(jù)在網(wǎng)絡(luò)中傳輸是不可靠的,,需要對方給予我們一個確認回復(fù),才可以保證消息正確到達,。如下圖:
機器B的確認收到和機器B信息可以進行合并,,減少次數(shù);而且發(fā)送機器B給機器A本身就代表了機器B已經(jīng)收到了消息,,所以最后的示例圖是:
步驟如下:
機器A發(fā)送syn包向機器B請求建立TCP連接,,并附加上自身的接收緩沖區(qū)信息等,機器A進入SYN_SEND狀態(tài),,表示請求已經(jīng)發(fā)送正在等待回復(fù),;
機器B收到請求之后,根據(jù)機器A的信息記錄下來,,并創(chuàng)建自身的接收緩存區(qū),,向機器A發(fā)送syn+ack的合成包,同時自身進入SYN_RECV狀態(tài),,表示已經(jīng)準備好了,,等待機器A 的回復(fù)就可以向A發(fā)送數(shù)據(jù);
機器A收到回復(fù)之后記錄機器B 的信息,,發(fā)送ack信息,,自身進入ESTABLISHED狀態(tài),表示已經(jīng)完全準備好了,,可以進行發(fā)送和接收,;
機器B收到ACK數(shù)據(jù)之后,,進入ESTABLISHED狀態(tài),。
三次消息的發(fā)送,稱為三次握手,。
斷開連接
斷開連接和三次握手類似,,直接上圖:
1. 機器A發(fā)送完數(shù)據(jù)之后,向機器B請求斷開連接,,自身進入FIN_WAIT_1狀態(tài),,表示數(shù)據(jù)發(fā)送完成且已經(jīng)發(fā)送FIN包(FIN標志位為1);
2. 機器B收到FIN包之后,,回復(fù)ack包表示已經(jīng)收到,,但此時機器B可能還有數(shù)據(jù)沒發(fā)送完成,自身進入CLOSE_WAIT狀態(tài),,表示對方已發(fā)送完成且請求關(guān)閉連接,,自身發(fā)送完成之后可以關(guān)閉連接;
3. 機器B數(shù)據(jù)發(fā)送完成之后,,發(fā)送FIN包給機器B ,,自身進入LAST_ACK狀態(tài),,表示等待一個ACK包即可關(guān)閉連接;
4. 機器A收到FIN包之后,,知道機器B也發(fā)送完成了,,回復(fù)一個ACK包,并進入TIME_WAIT狀態(tài)
TIME_WAIT狀態(tài)比較特殊,。當機器A收到機器B的FIN包時,,理想狀態(tài)下,確實是可以直接關(guān)閉連接了,;但是:
我們知道網(wǎng)絡(luò)是不穩(wěn)定的,,可能機器B 發(fā)送了一些數(shù)據(jù)還沒到達(比FIN包慢);
同時回復(fù)的ACK包可能丟失了,,機器B會重傳FIN包,;
如果此時機器A馬上關(guān)閉連接,會導(dǎo)致數(shù)據(jù)不完整,、機器B無法釋放連接等問題,。所以此時機器A需要等待2個報文生存最大時長,確保網(wǎng)絡(luò)中沒有任何遺留報文了,,再關(guān)閉連接
5. 最后,,機器A等待兩個報文存活最大時長之后,機器B 接收到ACK報文之后,,均關(guān)閉連接,,進入CLASED狀態(tài)
雙方之間4次互相發(fā)送報文來斷開連接的過程,就是四次揮手,。
現(xiàn)在,,對于為什么握手是三次揮手是四次、一定要三次/四次嗎,、為什么要停留2msl再關(guān)閉連接等等這些問題,,就都解決了。
/ UDP協(xié)議 /
運輸層協(xié)議除了TCP,,還有大名鼎鼎的UDP,。如果說TCP憑借他完善穩(wěn)定的功能獨樹一幟,那UDP就是精簡主義亂拳打死老師傅,。
UDP只實現(xiàn)了運輸層最少的功能:進程間通信,。對于應(yīng)用層傳下來的數(shù)據(jù),UDP只是附加一個首部就直接交給網(wǎng)絡(luò)層了,。UDP的頭部非常簡單,,只有三部分:
源端口、目標端口:端口號用來區(qū)分主機的不同進程
校驗碼:用于校驗數(shù)據(jù)包在傳輸?shù)倪^程中沒有出現(xiàn)錯誤,,例如某個1變成了0
長度:報文的長度
所以UDP的功能也只有兩個:校驗數(shù)據(jù)報是否發(fā)生錯誤,、區(qū)分不同的進程通信,。
但,TCP的功能雖然多,,但同時也是要付出相對應(yīng)的代價,。例如面向連接的特性,在建立和斷開連接的時候會有開銷,;擁塞控制的特性,,會限制傳輸?shù)纳舷薜鹊取O旅鎭砹_列一下UDP的優(yōu)缺點:
UDP的缺點
無法保證消息完整,、正確到達,,UDP是一個不可靠的傳輸協(xié)議;
缺少擁塞控制容易互相競爭資源導(dǎo)致網(wǎng)絡(luò)系統(tǒng)癱瘓
UDP的優(yōu)點
效率更快,;不需要建立連接以及擁塞控制
連接更多的客戶,;沒有連接狀態(tài),不需要為每個客戶創(chuàng)建緩存等
分組首部字節(jié)少,,開銷?。籘CP首部固定首部是20字節(jié),,而UDP只有8字節(jié),;更小的首部意味著更大比例的數(shù)據(jù)部分
在一些需要高效率允許可限度誤差的場景下可以使用。如直播場景,,并不需要保證每個數(shù)據(jù)包都完整到達,,允許一定的丟包率,這個時候TCP的可靠特性反而成為了累贅,;精簡的UDP更高的效率是更加適合的選擇
可以進行廣播,;UDP并不是面向連接的,所以可以同時對多個進程進行發(fā)送報文
UDP適用場景
UDP適用于對傳輸模型需要應(yīng)用層高度自定義,、允許出現(xiàn)丟包,、需要高效率的場景、需要廣播,;例如
視屏直播
DNS
RIP路由選擇協(xié)議
/ 其他補充 /
分塊傳輸
我們可以發(fā)現(xiàn),運輸層在傳輸數(shù)據(jù)的時候,,并不是把整個數(shù)據(jù)包加個首部直接發(fā)送過去,,而是會拆分成多個報文分開發(fā)送;那他這樣做原因是什么,?
有讀者可能會想到:數(shù)據(jù)鏈路層限制了數(shù)據(jù)長度只能有1460,。那數(shù)據(jù)鏈路層為什么要這么限制?他的本質(zhì)原因就是:網(wǎng)絡(luò)是不穩(wěn)定的,。如果報文太長,,那么極有可能在傳輸一般的時候突然中斷了,,這個時候就要整個數(shù)據(jù)重傳,效率就降低了,。把數(shù)據(jù)拆分成多個數(shù)據(jù)報,,那么當某個數(shù)據(jù)報丟失,只需要重傳該數(shù)據(jù)報即可,。
那是不是拆分得越細越好,?報文中數(shù)據(jù)字段長度太低,會使得首部的占比太大,,這樣首部就會成為網(wǎng)絡(luò)傳輸最大的負擔了,。例如1000字節(jié),每個報文首部是40字節(jié),,如果拆分成10個報文,,那么只需要傳輸400字節(jié)的首部;而如果拆分成1000個,,那么需要傳輸40000字節(jié)的首部,,效率就極大地降低了。
路由轉(zhuǎn)換
先看下圖:
正常情況下,,主機A的數(shù)據(jù)包可以又 1-3-6-7路徑進行傳送
如果路由3壞掉了,,那么可以從 1-4-6-7進行傳送
如果4也壞掉了,那么只能從2-5-6-7傳送
如果5壞掉了,,那么就中斷線路了
可以看出來,,使用路由轉(zhuǎn)發(fā)的好處是:提高網(wǎng)絡(luò)的容錯率,本質(zhì)原因依舊是網(wǎng)絡(luò)是不穩(wěn)定的 ,。即使壞掉幾個路由器,,網(wǎng)絡(luò)依舊暢通。但是如果壞掉路由器6那就直接導(dǎo)致主機A和主機B無法通信,,所以要避免這種核心路由器的存在,。
使用路由的好處還有:分流。如果一條線路太擁堵,,可以從別的路線進行傳輸,,提高效率。
粘包與拆包
在面向字節(jié)流那一小節(jié)講過,,TCP不懂這些數(shù)據(jù)流的意義,,他只知道從應(yīng)用層拿到數(shù)據(jù)流,切割成一份份報文,,然后發(fā)送給目標對象,。而如果應(yīng)用層傳輸下來的是兩個數(shù)據(jù)包,那么極有可能出現(xiàn)這種情況:
應(yīng)用層需要向目標進程發(fā)送兩份數(shù)據(jù),,一份音頻,,一份文本
TCP只知道接收到一個流,,并把流拆分成4段進行發(fā)送
中間第二個報文的數(shù)據(jù)就出現(xiàn)兩個文件的數(shù)據(jù)混在一起,這就是粘包
目標進程應(yīng)用層在接收到數(shù)據(jù)之后,,需要把這些數(shù)據(jù)拆分成正確的兩個文件,,就是拆包
粘包與拆包都是應(yīng)用層需要解決的問題,可以在每個文件的最后附加上一些特殊的字節(jié),,如換行符,;或者控制每個報文只包含一個文件的數(shù)據(jù),不足的用0補充等等,。
惡意攻擊
TCP的面向連接特點可能會被惡意的人利用,,對服務(wù)器進行攻擊。
前面我們知道,,當我們向一個主機發(fā)送syn包請求創(chuàng)建連接時,,服務(wù)器會為我們創(chuàng)建緩沖區(qū)等,然后向我們返回syn+ack報文,;如果我們偽造IP和端口,,向一個服務(wù)器進行海量的請求,會使得服務(wù)器創(chuàng)建了大量的創(chuàng)建一半的TCP連接,,使得其無法正常響應(yīng)用戶的請求,,導(dǎo)致服務(wù)器癱瘓。
解決的方法可以有限制IP的創(chuàng)建連接數(shù),、讓創(chuàng)建一半的tcp連接在更短的時間內(nèi)自行關(guān)閉,、延緩接收緩沖區(qū)內(nèi)存的分配等等。
長連接
我們向服務(wù)器的每一次請求都需要創(chuàng)建一個TCP連接,,服務(wù)器返回數(shù)據(jù)之后就會關(guān)閉連接,;如果在短時間內(nèi)有大量的請求,那么頻繁創(chuàng)建TCP連接關(guān)閉TCP連接是一個很浪費資源的行為,。所以我們可以讓TCP連接不要關(guān)閉,,在這個期間進行請求,提高效率,。
需要注意長連接維持時間,、創(chuàng)建條件等,避免被惡意利用創(chuàng)建大量的長連接,,消耗殆盡服務(wù)器的資源,。
/ 最后 /
以前學(xué)習(xí)的時候覺得這些東西好像沒什么卵用,貌似就是用來考試的,。事實上,在沒應(yīng)用到的時候,,對這些知識很難有更深層次的認知,,例如現(xiàn)在我看上面的總結(jié),,很多只是表面上的認知,不知道他背后代表的真正含義,。
但當我學(xué)習(xí)的更加廣泛,、深入,會對這些知識有越來越深刻的認識,。有那么幾個瞬間覺得:哦原來那個東西是這樣運用,,那個東西是這樣的啊,原來學(xué)了是真的有用,。
現(xiàn)在可能學(xué)了之后沒有什么感覺,,但是當用到或者學(xué)到相關(guān)的應(yīng)用時,會有一個頓悟感,,會瞬間收獲很多,。
更多信息可以來這里獲取==>>電子技術(shù)應(yīng)用-AET<<
電子技術(shù)應(yīng)用專欄作家 一口Linux
原文鏈接:https://mp.weixin.qq.com/s/obavhPkWFG2ybxXCdXfiMA