什麼是 TCP ?
TCP 是面向鏈接的、可靠的、基於字節流的傳輸層通訊協議。javascript
什麼是 TCP 鏈接?
簡單來講就是,用於保證可靠性和流量控制維護的某些狀態信息,這些信息的組合,包括Socket、序列號和窗口大小稱爲鏈接。html
因此咱們能夠知道,創建一個 TCP 鏈接是須要客戶端與服務器端達成上述三個信息的共識。java
如何肯定惟一的一個 TCP 鏈接呢?
TCP 四元組能夠肯定惟一的一個鏈接,四元組包括以下:linux
源地址和目標地址的字段(32位)是在 IP 頭部中,做用是經過 IP 協議發送報文給對方主機。面試
源端口和目標端口的字段(16位)是在 TCP 頭部中,做用是告訴 TCP 協議應該把報文發給哪一個進程。算法
字段名稱 | 長度(比特) | 含 義 |
---|---|---|
源端口號 | 16 | 發送網絡包的程序的端口號 |
目標端口號 | 16 | 接收網絡包的程序的端口號 |
序號(Sequence Number) | 32 | Sequence Number是包的序號,用來解決網絡包亂序(reordering)問題。 |
確認序號(Acknowledgement Number) | 32 | Acknowledgement Number就是ACK——用於確認收到,用來解決不丟包的問題。 |
首部長度 | 4 | 表示數據部分的起始位置,也能夠認爲表示頭部的長度 |
保留 | 6 | 該字段爲保留,如今未使用 |
控制位 | 6 | 該字段中的每一個比特分別表示如下通訊控制含義。<br/>URG:爲1表示高優先級數據包,緊急指針字段有效。<br/>ACK:爲1表示確認序號字段有效,通常表示數據已被接收方收到。<br/>PSH:爲1表示是帶有PUSH標誌的數據,指示接收方應該儘快將這個報文段交給應用層而不用等待緩衝區裝滿。<br/>RST:爲1表示出現嚴重差錯。可能須要重現建立TCP鏈接。還能夠用於拒絕非法的報文段和拒絕鏈接請求。<br/>SYN:爲1表示這是鏈接請求或是鏈接接受請求,用於建立鏈接和使順序號同步。<br/>FIN:爲1表示發送方沒有數據要傳輸了,要求釋放鏈接。 |
窗口大小 | 16 | 接收方告知發送方窗口大小(即無需等待確承認一塊兒發送的數據量) |
校驗和 | 16 | 用來檢查是否出現錯誤 |
緊急指針 | 16 | 表示應緊急處理的數據位置 |
選項 | 可變長度 | 除了上面的固定頭部字段以外,還能夠添加可選字段,但除了鏈接操做以外,不多使用可選字段 |
第一個報文—— SYN 報文shell
client_isn
,在這裏是seq = x
),將此序號置於 TCP 頭字段的「序號」字段中,同時把 SYN
標誌位置爲 1
,表示 SYN
報文。接着把第一個 SYN 報文發送給服務端,表示向服務端發起鏈接,該報文不包含應用層數據,以後客戶端處於 SYN-SENT
狀態。第二個報文 —— SYN + ACK 報文瀏覽器
SYN
報文後,首先服務端也隨機初始化本身的序號(server_isn
,在這裏是seq = y
),將此序號填入 TCP 頭字段的「序號」字段中,其次把 TCP 頭字段的「確認序號」字段填入 client_isn + 1
, 接着把 SYN
和 ACK
標誌位置爲 1
。最後把該報文發給客戶端,該報文也不包含應用層數據,以後服務端處於 SYN-RCVD
狀態。第三個報文 —— ACK 報文緩存
ACK
標誌位置爲 1
,其次「確認序號」字段填入 server_isn + 1
,最後把報文發送給服務端,此次報文能夠攜帶客戶到服務器的數據,以後客戶端處於 ESTABLISHED
狀態。ESTABLISHED
狀態。注意:安全
咱們看到有兩個中間狀態,syn_sent 和 syn_rcvd,這兩個狀態叫着「半打開」狀態,就是向對方發送了,可是還沒來得及看到對方的迴應。
syn_sent 是主動打開方的「半打開」狀態,syn_rcvd 是被動打開方的「半打開」狀態。客戶端是主動打開方,服務器是被動打開方。
FIN
標誌位被置爲 1
的報文,也即 FIN
報文,以後客戶端進入 FIN_WAIT_1
狀態。ACK
應答報文,接着服務端進入 CLOSED_WAIT
狀態。ACK
應答報文後,以後進入 FIN_WAIT_2
狀態。FIN
報文,以後服務端進入 LAST_ACK
狀態。FIN
報文後,回一個 ACK
應答報文,以後進入 TIME_WAIT
狀態。ACK
應答報文後,就進入了 CLOSED
狀態,至此服務端已經完成鏈接的關閉。2MSL
(4分鐘)時間後,自動進入 CLOSED
狀態,至此客戶端也完成鏈接的關閉。你能夠看到,每一個方向都須要一個 FIN 和一個 ACK,所以一般被稱爲四次揮手。
這裏一點須要注意是:主動關閉鏈接的,纔有 TIME_WAIT 狀態。
四次揮手也並不老是四次揮手,中間的兩個動做有時候是能夠合併一塊兒進行的,這個時候就成了三次揮手,主動關閉方就會從fin_wait_1
狀態直接進入到time_wait
狀態,跳過了fin_wait_2
狀態。
重傳機制的一種方式,就是在發送數據時,設定一個定時器,當超過指定的時間後,沒有收到對方的 ACK
確認應答報文,就會重發該數據。
TCP 會在如下兩種狀況發生超時重傳:
TCP 還有另一種快速重傳(Fast Retransmit)機制,它不以時間爲驅動,而是以數據驅動重傳。
在上圖,發送方發出了 1,2,3,4,5 份數據:
快速重傳機制只解決了一個問題,就是超時時間的問題,可是它依然面臨着另一個問題。就是重傳的時候,是重傳以前的一個,仍是重傳全部的問題。
SACK
( Selective Acknowledgment 選擇性確認),這種方式須要在 TCP 頭部「選項」字段里加一個 SACK
,它能夠將緩存的地圖發送給發送方,這樣發送方就能夠知道哪些數據收到了,哪些數據沒收到,知道了這些信息,就能夠只重傳丟失的數據。
以下圖,發送方收到了三次一樣的 ACK 確認報文,因而就會觸發快速重發機制,經過 SACK
信息發現只有 200~299
這段數據丟失,則重發時,就只選擇了這個 TCP 段進行重複。
Duplicate SACK 又稱 D-SACK
,其主要使用了 SACK 來告訴「發送方」有哪些數據被重複接收了。
D-SACK
。可見,D-SACK
有這麼幾個好處:
爲解決這個問題,TCP 引入了窗口這個概念。即便在往返時間較長的狀況下,它也不會下降網絡通訊的效率。
那麼有了窗口,就能夠指定窗口大小,窗口大小就是指無需等待確認應答,而能夠繼續發送數據的最大值。
窗口的實現其實是操做系統開闢的一個緩存空間,發送方主機在等到確認應答返回以前,必須在緩衝區中保留已發送的數據。若是定期收到確認應答,此時數據就能夠從緩存區清除。
假設窗口大小爲 3
個 TCP 段,那麼發送方就能夠「連續發送」 3
個 TCP 段,而且中途如有 ACK 丟失,能夠經過「下一個確認應答進行確認」。以下圖:
只要發送方收到了 ACK 700 確認應答,就意味着 700 以前的全部數據「接收方」都收到了。這個模式就叫累計確認或者累計應答。
窗口大小由哪一方決定?
TCP 頭裏有一個字段叫 Window
,也就是窗口大小。
這個字段是接收端告訴發送端本身還有多少緩衝區能夠接收數據。因而發送端就能夠根據這個接收端的處理能力來發送數據,而不會致使接收端處理不過來。
因此,一般窗口的大小是由接收方的窗口大小來決定的。
發送方發送的數據大小不能超過接收方的窗口大小,不然接收方就沒法正常接收到數據。
發送方的滑動窗口
咱們先來看看發送方的窗口,下圖就是發送方緩存的數據,根據處理的狀況分紅四個部分,其中深藍色方框是發送窗口,紫色方框是可用窗口:
在下圖,當發送方把數據「所有」都一下發送出去後,可用窗口的大小就爲 0 了,代表可用窗口耗盡,在沒收到 ACK 確認以前是沒法繼續發送數據了。
在下圖,當收到以前發送的數據 32~36
字節的 ACK 確認應答後,若是發送窗口的大小沒有變化,則滑動窗口往右邊移動 5 個字節,由於有 5 個字節的數據被應答確認,接下來 52~56
字節又變成了可用窗口,那麼後續也就能夠發送 52~56
這 5 個字節的數據了。
程序是如何表示發送方的四個部分的呢?
TCP 滑動窗口方案使用三個指針來跟蹤在四個傳輸類別中的每個類別中的字節。其中兩個指針是絕對指針(指特定的序列號),一個是相對指針(須要作偏移)。
SND.WND
:表示發送窗口的大小(大小是由接收方指定的);SND.UNA
:是一個絕對指針,它指向的是已發送但未收到確認的第一個字節的序列號,也就是 #2 的第一個字節;SND.NXT
:也是一個絕對指針,它指向未發送但可發送範圍的第一個字節的序列號,也就是 #3 的第一個字節;SND.UNA
指針加上 SND.WND
大小的偏移量,就能夠指向 #4 的第一個字節了。那麼可用窗口大小的計算就能夠是:
可用窗口大 = SND.WND -(SND.NXT - SND.UNA)
接收方的滑動窗口
接下來咱們看看接收方的窗口,接收窗口相對簡單一些,根據處理的狀況劃分紅三個部分:
其中三個接收部分,使用兩個指針進行劃分:
RCV.WND
:表示接收窗口的大小,它會通告給發送方。RCV.NXT
:是一個指針,它指向指望從發送方發送來的下一個數據字節的序列號,也就是 #3 的第一個字節。RCV.NXT
指針加上 RCV.WND
大小的偏移量,就能夠指向 #4 的第一個字節了。接收窗口和發送窗口的大小是相等的嗎?
並非徹底相等,接收窗口的大小是約等於發送窗口的大小的。
由於滑動窗口並非一成不變的。好比,當接收方的應用進程讀取數據的速度很是快的話,這樣的話接收窗口能夠很快的就空缺出來。那麼新的接收窗口大小,是經過 TCP 報文中的 Windows 字段來告訴發送方。那麼這個傳輸過程是存在時延的,因此接收窗口和發送窗口是約等於的關係。
發送方不能無腦的發數據給接收方,要考慮接收方處理能力。
若是一直無腦的發數據給對方,但對方處理不過來,那麼就會致使觸發重發機制,從而致使網絡流量的無故的浪費。
爲了解決這種現象發生,TCP 提供一種機制可讓「發送方」根據「接收方」的實際接收能力控制發送的數據量,這就是所謂的流量控制。
根據上圖的流量控制,說明下每一個過程:
Usable
減小爲 120 字節,同時 SND.NXT
指針也向右偏移 80 字節後,指向 321,這意味着下次發送數據的時候,序列號是 321。RCV.NXT
也就指向 321,這意味着客戶端指望的下一個報文的序列號是 321,接着發送確認報文給服務端。RCV.NXT
也就指向 441,接着發送確認報文給服務端。SND.UNA
指針往右偏移後指向 321,因而可用窗口 Usable
增大到 80。SND.UNA
指針往右偏移後指向 441,因而可用窗口 Usable
增大到 200。SND.NXT
指向 601,因而可用窗口 Usable
減小到 40。RCV.NXT
也就是指向了 601,接着發送確認報文給服務端。SND.UNA
指針偏移了 160 後指向 601,可用窗口 Usable
也就增大至了 200。爲何要有擁塞控制呀,不是有流量控制了嗎?
前面的流量控制是避免「發送方」的數據填滿「接收方」的緩存,可是並不知道網絡的中發生了什麼。
通常來講,計算機網絡都處在一個共享的環境。所以也有可能會由於其餘主機之間的通訊使得網絡擁堵。
在網絡出現擁堵時,若是繼續發送大量數據包,可能會致使數據包時延、丟失等,這時 TCP 就會重傳數據,可是一重傳就會致使網絡的負擔更重,因而會致使更大的延遲以及更多的丟包,這個狀況就會進入惡性循環被不斷地放大….
因此,TCP 不能忽略網絡上發生的事,它被設計成一個無私的協議,當網絡發送擁塞時,TCP 會自我犧牲,下降發送的數據量。
因而,就有了擁塞控制,控制的目的就是避免「發送方」的數據填滿整個網絡。
爲了在「發送方」調節所要發送數據的量,定義了一個叫作「擁塞窗口」的概念。
什麼是擁塞窗口?和發送窗口有什麼關係呢?
擁塞窗口 cwnd是發送方維護的一個的狀態變量,它會根據網絡的擁塞程度動態變化的。
咱們在前面提到過發送窗口 swnd
和接收窗口 rwnd
是約等於的關係,那麼因爲加入了擁塞窗口的概念後,此時發送窗口的值是swnd = min(cwnd, rwnd),也就是擁塞窗口和接收窗口中的最小值。
擁塞窗口 cwnd
變化的規則:
cwnd
就會增大;cwnd
就減小;那麼怎麼知道當前網絡是否出現了擁塞呢?
其實只要「發送方」沒有在規定時間內接收到 ACK 應答報文,也就是發生了超時重傳,就會認爲網絡出現了用擁塞。
擁塞控制有哪些控制算法?
擁塞控制主要是四個算法:
慢啓動
TCP 在剛創建鏈接完成後,首先是有個慢啓動的過程,這個慢啓動的意思就是一點一點的提升發送數據包的數量,若是一上來就發大量的數據,這不是給網絡添堵嗎?
慢啓動的算法記住一個規則就行:當發送方每收到一個 ACK,擁塞窗口 cwnd 的大小就會加 1。
這裏假定擁塞窗口 cwnd
和發送窗口 swnd
相等,下面舉個栗子:
cwnd = 1
,表示能夠傳一個 MSS
大小的數據。能夠看出慢啓動算法,發包的個數是指數性的增加。
那慢啓動漲到何時是個頭呢?
有一個叫慢啓動門限 ssthresh
(slow start threshold)狀態變量。
cwnd
< ssthresh
時,使用慢啓動算法。cwnd
>= ssthresh
時,就會使用「擁塞避免算法」。擁塞避免
前面說道,當擁塞窗口 cwnd
「超過」慢啓動門限 ssthresh
就會進入擁塞避免算法。
通常來講 ssthresh
的大小是 65535
字節。
那麼進入擁塞避免算法後,它的規則是:每當收到一個 ACK 時,cwnd 增長 1/cwnd。
接上前面的慢啓動的栗子,現假定 ssthresh
爲 8
:
MSS
大小的數據,變成了線性增加。因此,咱們能夠發現,擁塞避免算法就是將本來慢啓動算法的指數增加變成了線性增加,仍是增加階段,可是增加速度緩慢了一些。
就這麼一直增加着後,網絡就會慢慢進入了擁塞的情況了,因而就會出現丟包現象,這時就須要對丟失的數據包進行重傳。
當觸發了重傳機制,也就進入了「擁塞發生算法」。
擁塞發生
當網絡出現擁塞,也就是會發生數據包重傳,重傳機制主要有兩種:
這兩種使用的擁塞發送算法是不一樣的,接下來分別來講說。
發生超時重傳的擁塞發生算法
這個時候,ssthresh 和 cwnd 的值會發生變化:
ssthresh
設爲 cwnd/2
,cwnd
重置爲 1
接着,就從新開始慢啓動,慢啓動是會忽然減小數據流的。這真是一旦「超時重傳」,立刻回到解放前。可是這種方式太激進了,反應也很強烈,會形成網絡卡頓。
發生快速重傳的擁塞發生算法
還有更好的方式,前面咱們講過「快速重傳算法」。當接收方發現丟了一箇中間包的時候,發送三次前一個包的 ACK,因而發送端就會快速地重傳,沒必要等待超時再重傳。
TCP 認爲這種狀況不嚴重,由於大部分沒丟,只丟了一小部分,則 ssthresh
和 cwnd
變化以下:
cwnd = cwnd/2
,也就是設置爲原來的一半;ssthresh = cwnd
;快速恢復
快速重傳和快速恢復算法通常同時使用,快速恢復算法是認爲,你還能收到 3 個重複 ACK 說明網絡也不那麼糟糕,因此沒有必要像 RTO
超時那麼強烈。
正如前面所說,進入快速恢復以前,cwnd
和 ssthresh
已被更新了:
cwnd = cwnd/2
,也就是設置爲原來的一半;ssthresh = cwnd
;而後,進入快速恢復算法以下:
cwnd = ssthresh + 3
( 3 的意思是確認有 3 個數據包被收到了);也就是沒有像「超時重傳」一晚上回到解放前,而是還在比較高的值,後續呈線性增加。
擁塞算法示意圖
爲何是三次握手,不是兩次?不是四次?
緣由一:避免歷史鏈接
簡單來講,三次握手的 首要緣由是爲了防止舊的重複鏈接初始化形成混亂。
客戶端連續發送屢次 SYN
創建鏈接的報文,在網絡擁堵狀況下:
SYN + ACK
報文給客戶端;RST
報文給服務端,表示停止這一次鏈接。若是是兩次握手鍊接,就不能判斷當前鏈接是不是歷史鏈接,三次握手則能夠在客戶端(發送方)準備發送第三次報文時,客戶端因有足夠的上下文來判斷當前鏈接是不是歷史鏈接:
RST
報文,以此停止歷史鏈接;ACK
報文,通訊雙方就會成功創建鏈接;因此,TCP 使用三次握手創建鏈接的最主要緣由是防止歷史鏈接初始化了鏈接。
緣由二:同步雙方初始序號
TCP 協議的通訊雙方, 都必須維護一個「序號」, 序列號是可靠傳輸的一個關鍵因素,它的做用:
可見,序號在 TCP 鏈接中佔據着很是重要的做用,因此當客戶端發送攜帶「初始序號」的 SYN
報文的時候,須要服務端回一個 ACK
應答報文,表示客戶端的 SYN 報文已被服務端成功接收,那當服務端發送「初始序號」給客戶端的時候,依然也要獲得客戶端的應答迴應,這樣一來一回,才能確保雙方的初始序列號能被可靠的同步。
四次握手其實也可以可靠的同步雙方的初始化序號,但因爲第二步和第三步能夠優化成一步,因此就成了「三次握手」。
而兩次握手只保證了一方的初始序列號能被對方成功接收,沒辦法保證雙方的初始序列號都能被確認接收。
緣由三:避免資源浪費
若是隻有「兩次握手」,當客戶端的 SYN
請求鏈接在網絡中阻塞,客戶端沒有接收到 ACK
報文,就會從新發送 SYN
,因爲沒有第三次握手,服務器不清楚客戶端是否收到了本身發送的創建鏈接的 ACK
確認信號,因此每收到一個 SYN
就只能先主動創建一個鏈接,這會形成什麼狀況呢?
若是客戶端的 SYN
阻塞了,重複發送屢次 SYN
報文,那麼服務器在收到請求後就會創建多個冗餘的無效連接,形成沒必要要的資源浪費。
即兩次握手會形成消息滯留狀況下,服務器重複接受無用的鏈接請求 SYN
報文,而形成重複分配資源。
小結
TCP 創建鏈接時,經過三次握手能防止歷史鏈接的創建,能減小雙方沒必要要的資源開銷,能幫助雙方同步初始化序列號。序列號可以保證數據包不重複、不丟棄和按序傳輸。
不使用「兩次握手」和「四次握手」的緣由:
什麼是 SYN 攻擊?如何避免 SYN 攻擊?
SYN 攻擊
咱們都知道 TCP 鏈接創建是須要三次握手,假設攻擊者短期僞造不一樣 IP 地址的 SYN
報文,服務端每接收到一個 SYN
報文,就進入SYN_RCVD
狀態,但服務端發送出去的 ACK + SYN
報文,沒法獲得未知 IP 主機的 ACK
應答,長此以往就會佔滿服務端的 SYN 接收隊列(未鏈接隊列),使得服務器不能爲正經常使用戶服務。
避免 SYN 攻擊方式一
其中一種解決方式是經過修改 Linux 內核參數,控制隊列大小和當隊列滿時應作什麼處理。
當網卡接收數據包的速度大於內核處理的速度時,會有一個隊列保存這些數據包。控制該隊列的最大值以下參數:
net.core.netdev_max_backlog
SYN_RCVD 狀態鏈接的最大個數:
net.ipv4.tcp_max_syn_backlog
超出處理能時,對新的 SYN 直接回報 RST,丟棄鏈接:
net.ipv4.tcp_abort_on_overflow
避免 SYN 攻擊方式二
咱們先來看下 Linux 內核的 SYN
(未完成鏈接創建)隊列與 Accpet
(已完成鏈接創建)隊列是如何工做的?
正常流程:
accpet()
socket 接口,從「 Accept 隊列」取出鏈接。應用程序過慢:
受到 SYN 攻擊:
tcp_syncookies
的方式能夠應對 SYN 攻擊的方法:
net.ipv4.tcp_syncookies = 1
tcp_syncookies 應對 SYN 攻擊
cookie
值,再以 SYN + ACK 中的「序列號」返回客戶端;accpet()
socket 接口,從「 Accept 隊列」取出的鏈接。爲何揮手須要四次?
FIN
時,僅僅表示客戶端再也不發送數據了可是還能接收數據。FIN
報文時,先回一個 ACK
應答報文,而服務端可能還有數據須要處理和發送,等服務端再也不發送數據時,才發送 FIN
報文給客戶端來表示贊成如今關閉鏈接。從上面過程可知,服務端一般須要等待完成數據的發送和處理,因此服務端的 ACK
和 FIN
通常都會分開發送,從而比三次握手致使多了一次。
爲何 TIME_WAIT 等待的時間是 2MSL?
MSL
是 Maximum Segment Lifetime,報文最大生存時間,它是任何報文在網絡上存在的最長時間,超過這個時間報文將被丟棄。由於 TCP 報文基因而 IP 協議的,而 IP 頭中有一個 TTL
字段,是 IP 數據報能夠通過的最大路由數,每通過一個處理他的路由器此值就減 1,當此值爲 0 則數據報將被丟棄,同時發送 ICMP 報文通知源主機。
MSL 與 TTL 的區別: MSL 的單位是時間,而 TTL 是通過路由跳數。因此 MSL 應該要大於等於 TTL 消耗爲 0 的時間,以確保報文已被天然消亡。
TIME_WAIT 等待 2 倍的 MSL,比較合理的解釋是: 網絡中可能存在來自發送方的數據包,當這些發送方的數據包被接收方處理後又會向對方發送響應,因此一來一回須要等待 2 倍的時間。
好比若是被動關閉方沒有收到斷開鏈接的最後的 ACK 報文,就會觸發超時重發 Fin 報文,另外一方接收到 FIN 後,會重發 ACK 給被動關閉方, 一來一去正好 2 個 MSL。
2MSL
的時間是從客戶端接收到 FIN 後發送 ACK 開始計時的。若是在 TIME-WAIT 時間內,由於客戶端的 ACK 沒有傳輸到服務端,客戶端又接收到了服務端重發的 FIN 報文,那麼 2MSL 時間將從新計時。
在 Linux 系統裏 2MSL
默認是 60
秒,那麼一個 MSL
也就是 30
秒。Linux 系統停留在 TIME_WAIT 的時間爲固定的 60 秒。
其定義在 Linux 內核代碼裏的名稱爲 TCP_TIMEWAIT_LEN:
#define TCP_TIMEWAIT_LEN (60*HZ) /* how long to wait to destroy TIME-WAIT state, about 60 seconds */
若是要修改 TIME_WAIT 的時間長度,只能修改 Linux 內核代碼裏 TCP_TIMEWAIT_LEN 的值,並從新編譯 Linux 內核。
爲何須要 TIME_WAIT 狀態?
主動發起關閉鏈接的一方,纔會有 TIME-WAIT
狀態。
須要 TIME-WAIT 狀態,主要是兩個緣由:
緣由一:防止舊鏈接的數據包
接收到歷史數據的異常
SEQ = 301
報文,被網絡延遲了。SEQ = 301
抵達了客戶端,那麼客戶端是有可能正常接收這個過時的報文,這就會產生數據錯亂等嚴重的問題。因此,TCP 就設計出了這麼一個機制,通過 2MSL
這個時間,足以讓兩個方向上的數據包都被丟棄,使得原來鏈接的數據包在網絡中都天然消失,再出現的數據包必定都是新創建鏈接所產生的。
緣由二:保證鏈接正確關閉
在 RFC 793 指出 TIME-WAIT 另外一個重要的做用是:
TIME-WAIT - represents waiting for enough time to pass to be sure the remote TCP received the acknowledgment of its connection termination request.
也就是說,TIME-WAIT 做用是等待足夠的時間以確保最後的 ACK 能讓被動關閉方接收,從而幫助其正常關閉。
假設 TIME-WAIT 沒有等待時間或時間太短,斷開鏈接會形成什麼問題呢?
ACK
報文若是在網絡中被丟失了,此時若是客戶端 TIME-WAIT
太短或沒有,則就直接進入了 CLOSED
狀態了,那麼服務端則會一直處在 LASE_ACK
狀態。SYN
請求報文後,服務端會發送 RST
報文給客戶端,鏈接創建的過程就會被終止。若是 TIME-WAIT 等待足夠長的狀況就會遇到兩種狀況:
ACK
報文,則服務端正常關閉鏈接。ACK
報文時,則會重發 FIN
關閉鏈接報文並等待新的 ACK
報文。因此客戶端在 TIME-WAIT
狀態等待 2MSL
時間後,就能夠保證雙方的鏈接均可以正常的關閉。
若是已經創建了鏈接,可是客戶端忽然出現故障了怎麼辦?
TCP 有一個機制是保活機制。這個機制的原理是這樣的:
定義一個時間段,在這個時間段內,若是沒有任何鏈接相關的活動,TCP 保活機制會開始做用,每隔一個時間間隔,發送一個探測報文,該探測報文包含的數據很是少,若是連續幾個探測報文都沒有獲得響應,則認爲當前的 TCP 鏈接已經死亡,系統內核將錯誤信息通知給上層應用程序。
在 Linux 內核能夠有對應的參數能夠設置保活時間、保活探測的次數、保活探測的時間間隔,如下都爲默認值:
net.ipv4.tcp_keepalive_time=7200 net.ipv4.tcp_keepalive_intvl=75 net.ipv4.tcp_keepalive_probes=9
也就是說在 Linux 系統中,最少須要通過 2 小時 11 分 15 秒才能夠發現一個「死亡」鏈接。
這個時間是有點長的,咱們也能夠根據實際的需求,對以上的保活相關的參數進行設置。
若是開啓了 TCP 保活,須要考慮如下幾種狀況:
第一種,對端程序是正常工做的。當 TCP 保活的探測報文發送給對端, 對端會正常響應,這樣 TCP 保活時間會被重置,等待下一個 TCP 保活時間的到來。
第二種,對端程序崩潰並重啓。當 TCP 保活的探測報文發送給對端後,對端是能夠響應的,但因爲沒有該鏈接的有效信息,會產生一個 RST 報文,這樣很快就會發現 TCP 鏈接已經被重置。
第三種,是對端程序崩潰,或對端因爲其餘緣由致使報文不可達。當 TCP 保活的探測報文發送給對端後,石沉大海,沒有響應,連續幾回,達到保活探測次數後,TCP 會報告該 TCP 鏈接已經死亡。
UDP 頭部格式
UDP 和 TCP 有什麼區別呢?
1. 鏈接
2. 服務對象
3. 可靠性
4. 擁塞控制、流量控制
5. 首部開銷
20
個字節,若是使用了「選項」字段則會變長的。6. 傳輸方式
7. 分片不一樣
UDP 和 TCP 應用場景
因爲 TCP 是面向鏈接,能保證數據的可靠性交付,所以常常用於:
FTP
文件傳輸HTTP
/ HTTPS
因爲 UDP 面向無鏈接,它能夠隨時發送數據,再加上UDP自己的處理既簡單又高效,所以常常用於:
DNS
、SNMP
等IP 在 TCP/IP 參考模型中處於第三層,也就是網絡層。
網絡層的主要做用是:實現主機與主機之間的通訊,也叫點對點(end to end)通訊。
IPv4表示
IP 地址(IPv4 地址)由 32
位正整數來表示,IP 地址在計算機是以二進制的方式處理的。
而人類爲了方便記憶採用了點分十進制的標記方式,也就是將 32 位 IP 地址以每 8 位爲組,共分爲 4
組,每組以「.
」隔開,再將每組轉換成十進制。
互聯網誕生之初,IP 地址顯得很充裕,因而計算機科學家們設計了分類地址。
IP 地址分類成了 5 種類型,分別是 A 類、B 類、C 類、D 類、E 類。
上圖中黃色部分爲分類號,用以區分 IP 地址類別。
什麼是 A、B、C 類地址?
其中對於 A、B、C 類主要分爲兩個部分,分別是網絡號和主機號。
A、B、C 分類地址最大主機個數是如何計算的呢?
最大主機個數,就是要看主機號的位數,如 C 類地址的主機號佔 8 位,那麼 C 類地址的最大主機個數:
爲何要減 2 呢?
由於在 IP 地址中,有兩個 IP 是特殊的,分別是主機號全爲 1 和 全爲 0 地址。
所以,在分配過程當中,應該去掉這兩種狀況。
廣播地址用於什麼?
廣播地址用於在同一個鏈路中相互鏈接的主機之間發送數據包。
當主機號全爲 1 時,就表示該網絡的廣播地址。例如把 172.20.0.0/16
用二進制表示以下:
10101100.00010100.00000000.00000000
將這個地址的主機部分所有改成 1,則造成廣播地址:
10101100.00010100.11111111.11111111
再將這個地址用十進制表示,則爲 172.20.255.255
。
廣播地址能夠分爲本地廣播和直接廣播兩種。
什麼是 D、E 類地址?
而 D 類和 E 類地址是沒有主機號的,因此不可用於主機 IP,D 類常被用於多播,E 類是預留的分類,暫時未使用。
多播地址用於什麼?
因爲廣播沒法穿透路由,若想給其餘網段發送一樣的包,就可使用能夠穿透路由的多播。
多播使用的 D 類地址,其前四位是 1110
就表示是多播地址,而剩下的 28 位是多播的組編號。
從 224.0.0.0 ~ 239.255.255.255 都是多播的可用範圍,其劃分爲如下三類:
IP 分類的優勢
無論是路由器仍是主機解析到一個 IP 地址時候,咱們判斷其 IP 地址的首位是否爲 0,爲 0 則爲 A 類地址,那麼就能很快的找出網絡地址和主機地址。
其他分類判斷方式參考以下圖:
因此,這種分類地址的優勢就是簡單明瞭、選路(基於網絡地址)簡單。
IP 分類的缺點
缺點一
同一網絡下沒有地址層次,好比一個公司裏用了 B 類地址,可是可能須要根據生產環境、測試環境、開發環境來劃分地址層次,而這種 IP 分類是沒有地址層次劃分的功能,因此這就缺乏地址的靈活性。
缺點二
A、B、C類有個尷尬處境,就是不能很好的與現實網絡匹配。
IPv6 的亮點
IPv6 不只僅只是可分配的地址變多了,它還有很是多的亮點。
40
字節,去掉了包頭校驗和,簡化了首部結構,減輕了路由器負荷,大大提升了傳輸的性能。IPv6 地址的標識方法
IPv4 地址長度共 32 位,是以每 8 位做爲一組,並用點分十進制的表示方式。
IPv6 地址長度是 128 位,是以每 16 位做爲一組,每組用冒號 「:」 隔開。
若是出現連續的 0 時還能夠將這些 0 省略,並用兩個冒號 「::」隔開。可是,一個 IP 地址中只容許出現一次兩個連續的冒號。
IPv6 地址的結構
IPv6 相似 IPv4,也是經過 IP 地址的前幾位標識 IP 地址的種類。
IPv6 的地址主要有如下類型地址:
IPv6 單播地址類型
對於一對一通訊的 IPv6 地址,主要劃分了三類單播地址,每類地址的有效範圍都不一樣。
IPv6 相比 IPv4 的首部改進:
40
字節。DNS 域名解析,DNS 能夠將域名網址自動轉換爲具體的 IP 地址。
域名解析的工做流程
瀏覽器首先看一下本身的緩存裏有沒有,若是沒有就向操做系統的緩存要,尚未就檢查本機域名解析文件 hosts
,若是仍是沒有,就會 DNS 服務器進行查詢,查詢的過程以下:
在傳輸一個 IP 數據報的時候,肯定了源 IP 地址和目標 IP 地址後,就會經過主機「路由表」肯定 IP 數據包下一跳。然而,網絡層的下一層是數據鏈路層,因此咱們還要知道「下一跳」的 MAC 地址。
因爲主機的路由表中能夠找到下一跳的 IP 地址,因此能夠經過 ARP 協議,求得下一跳的 MAC 地址。
ARP 如何知道對方 MAC 地址的呢?
操做系統一般會把第一次經過 ARP 獲取的 MAC 地址緩存起來,以便下次直接從緩存中找到對應 IP 地址的 MAC 地址。
不過,MAC 地址的緩存是有必定期限的,超過這個期限,緩存的內容將被清除。
參考:
35 張圖解:被問千百遍的 TCP 三次握手和四次揮手面試題
[網絡是怎麼鏈接的]