設計師圖解TCP鏈接過程

前言

      咱們知道Ip層包裹着tcp報文段把它從源Ip運送到目的Ip,若是過程當中出現差錯(16位的Ip檢驗和錯誤),Ip協議會直接丟棄該數據報而且不生成差錯報文。這種狀況tcp會發現數據丟失並進行重傳。
      這篇文章想探討一下TCP協議是經過什麼方式作到這些的,曾經作過設計師的我忍不住抄起老本行畫兩張圖。前端

IP、UDP、TCP差別

      IP是網路層協議,TCP和UDP都是運輸層協議,他們都是基於IP協議傳遞數據。接下來咱們經過幾個重要首部分析一下它們的異同。
      三個協議首部中都有16位檢驗和,可是,IP協議首部中的檢驗和只覆蓋IP首部,而TCP和UDP首部中的檢驗和是包含全部數據的,也就是說IP協議不關心數據是否正確,而TCP和UDP關心。
      IP首部中最重要的是32位源IP和目的IP地址,其它首部都是配合它順利的把數據從源IP傳到目的IP,TCP和UDP負責標記對應端口,那麼TCP和UDP的區別有是什麼呢?
      咱們先看UDP,它只有4個首部,前兩個肯定端口,後兩個保證數據的準確。IP協議把數據傳遞到目的IP地址的機器上,UDP經過首部中的長度和檢驗和校驗數據,若是校驗沒有經過,那麼UDP協議會直接丟棄數據,而不會向源IP和端口發送消息,因此說UDP協議是面向數據報的。
      UDP的缺點就是:咱們不能保證對方必定收到了我發送的數據。可是從首部中很容易能看出它的優勢:就是輕量、速度快。
      再看TCP,它除了關心數據以外明顯還關心更多的東西,其中最重要的就是關心鏈接的可靠性。下面咱們將主要經過TCP鏈接和斷開過程看看這些首部都是什麼做用。
      TCP首部中有6個標誌比特位,它們默認都是0,須要時設置爲1,經過它們完成詢問和應答。它們分別表示的含義以下:

URG: 緊急指針
ACK: 確認序號有效
PSH: 儘量快地將數據送往接收進程
RST: 重建鏈接
SYN: 同步序號用來發起一個鏈接
FIN: 發端完成發送任務
算法

鏈接過程和狀態

      我畫了一張圖來描述TCP鏈接的過程和狀態變化: 緩存

通常狀況(圖中紫色和綠色部分)

三次握手
第一條紫色箭頭(從上到下):

      服務器端默認處於 LISTEN 狀態監聽着某個端口。當客戶端想要鏈接這個端口的時候,客戶端首先會隨機生成一個初始序號(圖中的j),首部中的32位序號(如下簡稱序號)就變成j+1,同時首部中的SYN由0置爲1。客戶端發送完帶有這些首部的TCP段後狀態變爲 SYN_SENT 服務器

第一條綠色箭頭:

    當服務器收到客戶端第一個帶有SYN的TCP段的時候,客戶端由 LISTEN 狀態變爲 SYN_RCVD 狀態,併發送一段數據,其中首部ACK置爲1,32位確認號(如下簡稱確認號)爲客戶端發過來的序號(j)+1,含義就是客戶端的數據已經收到。同時SYN也會置爲1,序號爲k。含義是我也要與你創建鏈接。網絡

第二條紫色箭頭:

      當客戶端收到服務器的ACK後,狀態變動爲 ESTABLISLISHED ,同時發送數據,首部ACK爲k+1,含義是服務器的數據已經收到。這時客戶端已經創建鏈接,而服務器端是否能創建鏈接取決於它可否收到當前客戶端發送的這段帶有ACK的數據。若是服務器收到的話,它的狀態也會變成 ESTABLISLISHED
      到此,鏈接創建,能夠繼續交換數據。前端工程師

四次揮手
第三條紫色箭頭:

      在不考慮斷電等特殊狀況下,主動關閉的一方(圖中爲客戶端但不老是)發送FIN+序號m,狀態變動爲 FIN_WAIT_1 併發

第二條綠色箭頭:

      被動關閉的一方(圖中爲服務器但不老是)收到後狀態變動爲 CLOSE_WAIT 。同時裏當即發送確認號m+1,頭部ACK置爲1,這時服務端狀態變動爲 LAST_ACK ,很容易理解,這是最後一次確認,客戶端收到ACK後,狀態變動爲 FIN_WAIT_2 tcp

第三條綠色箭頭:

      服務器 LAST_ACK 後還可能繼續作其它的操做,作完以後,服務器也會發送FIN+序號n。工具

第四條紫色箭頭:

      客戶端收到FIN後,狀態變動爲 TIME_WAIT ,同時發送確認信息ACK n+1,服務器收到ACK後,狀態變動爲 CLOSE
      到此,鏈接斷開。優化

爲何揮手是4次而不是3次?

      揮手的時候有個問題,爲何ACK和FIN不能一塊兒發送呢?這是由TCP的半關閉(half-close)形成的。TCP鏈接是全雙工(即數據在兩個方向上能同時傳遞),所以每一個方向必須單獨地進行關閉。
      收到一個FIN只意味着對方不會再向我發送數據了,可是TCP仍然支持我繼續向對方發送數據(固然,在實際的應用中,直有不多的程序這樣作),若是咱們用Wireshark等工具抓包時也常常會看到揮手只有3次的狀況。

TIME_WAIT旁邊的2MSL是什麼?

       TIME_WAIT 狀態也稱爲2倍MSL等待狀態。MSL是TCP的最大報文生存時間。當一個TCP主動關閉併發送最後一個ACK(圖中右下角最後一條紫色線)後,必須在 TIME_WAIT 狀態等待兩倍的MSL時間,防止對方沒有收到這個ACK而後重發FIN。因此一去一回最多就是兩倍時間。
      操做系統默認240s(也就是2倍的兩分鐘)後會關閉處於 TIME_WAIT 的鏈接,在這以前端口一直會被佔用。
      在Linux服務器上能夠經過變動/etc/sysctl.conf文件去修改該缺省值(秒):net.ipv4.tcp_fin_timeout=30。但這一般也會出現一些問題。

同時打開(圖中紫色和橙色部分)

      兩個應用程序彼此同時打開的可能性很小。這時雙方都是客戶端也都是服務器。具體流程和通常狀況同樣,只不過揮手的時候是4次而不是3次。TCP協議認爲這種狀況是創建一條鏈接而不是兩條。

同時關閉(圖中紫色和橙色部分)

      爲了方便我把同時打開和同時關閉畫在了一塊兒,其實同時打開不必定同時關閉。不一樣時打開也有可能同時關閉。

TCP服務器處理端口號

      咱們先調用natstat命令看一下telnet服務器。-a:顯示全部;-n:以點分十進制表示IP;-f inet:只顯示TCP和UDP。首先,沒有鏈接的時候它是這樣的:

      表頭依次爲協議、接收請求數、發送請求數、本地地址、遠程地址和狀態。圖中數據顯示:當前本機的23端口正在處於 LISTEN狀態,監聽着任意IP發來的請求。下面再看一下當請求過來時的狀態:
      狀態一共有3條,這三條狀態對應着三個進程, LISTEN狀態的進程一直存在並接受SYN報文段,一旦握手成功,系統內核中的TCP模塊就建立一個處於 ESTABLISHED狀態的進程。

呼入鏈接請求隊列

      若是服務器在同一時間收到大量請求,而服務器當前沒有能力處理(或有更高優先級的進程)。TCP會先將這個請求緩存在一個固定長度的隊列中(隊列長度通常爲5,稱爲積壓值)。
      應用層會不斷消費該隊列,一旦產生積壓,服務器會中止接收SYN報文,而且不返回任何消息,這時客戶端會顯示超時。

延時確認時間

      一般TCP在接收到數據時並不當即發送ACK;而是推遲發送,以便將ACK與須要沿該方向發送的數據一塊兒發送(這種現象稱爲數據捎帶ACK)。絕大多數實現採用的時延爲200ms。
      200ms是最大時間,若是一直有數據等待發送,那麼就不存在延時確認時間。

Nagle算法

      Nagle算法是爲了解決小段問題。所謂「小段」,指的是小於最大MSS尺寸的數據塊。例如應用程序一次產生一字節數據(例如交互式輸入),這樣會致使網絡因爲太多的包而過載。
      Nagle算法的基本定義是任意時刻,最多隻能有一個未被確認的小段。規則以下:

一、若是包長度達到MSS,則容許發送;
二、若是該包含有FIN,則容許發送;
三、設置了TCP_NODELAY選項,則容許發送;
四、未設置TCP_CORK選項時,若全部發出去的小包均被確認,則容許發送;
五、上述條件都未知足,但發生了超時(通常爲200ms),則當即發送。

      Nagle算法的優勢是自適應:確認到達的越快,發送的就越快。
      Nagle算法的缺點是發送存在延時。咱們能夠經過TCP_NODELAY套接字選項來關閉Nagle算法。

總結

      對於前端工程師來說,瞭解TCP協議可以幫助咱們更好的理解並使用HTTP協議。也可以幫助咱們對網絡進行更加細緻的優化。個人理解也頗有限,但願可以和你們共同討論。

參考資料

  • 《Tcp/ip詳解-卷一》
  • 《圖解Tcp/ip》
相關文章
相關標籤/搜索