1. 程式人生 > 實用技巧 >二、TCP/IP相關

二、TCP/IP相關

資料在網路中傳輸最終一定是通過物理介質傳輸。物理介質就是把電腦連線起來的物理手段,常見的有光纖、雙絞線,以及無線電波,它決定了電訊號(0和1)的傳輸方式,物理介質的不同決定了電訊號的傳輸頻寬、速率、傳輸距離以及抗干擾性等等。網路資料傳輸就像快遞郵寄,資料就是快件。只有路打通了,你的”快遞”才能送到,因此物理介質是網路通訊的基石。

寄快遞首先得稱重、確認體積(確認資料大小),貴重物品還得層層包裹填充物確保安全,封裝,然後填寫發件地址(源主機地址)和收件地址(目標主機地址),確認快遞方式。對於偏遠地區,快遞不能直達,還需要中途轉發。網路通訊也是一樣的道理,只不過把這些步驟都規定成了各種協議。

TCP/IP的模型的每一層都需要下一層所提供的協議來完成自己的目的。我們來看下資料是怎麼通過TCP/IP協議模型從一臺主機發送到另一臺主機的。

當用戶通過HTTP協議發起一個請求,應用層、傳輸層、網路互聯層和網路訪問層的相關協議依次對該請求進行包裝並攜帶對應的首部,最終在網路訪問層生成乙太網資料包,乙太網資料包通過物理介質傳輸給對方主機,對方接收到資料包以後,然後再一層一層採用對應的協議進行拆包,最後把應用層資料交給應用程式處理。

TCP/IP 與 HTTP

TCP/IP(Transmission Control Protocol/Internet Protocol,傳輸控制協議/網際協議)是指能夠在多個不同網路間實現資訊傳輸的協議簇。TCP/IP 協議不僅僅指的是 TCP 和 IP 兩個協議,而是指一個由FTP、SMTP、TCP、UDP、IP等協議構成的協議簇, 只是因為在TCP/IP協議中TCP協議和IP協議最具代表性,所以被稱為TCP/IP協議。

而HTTP是應用層協議,主要解決如何包裝資料。

“IP”代表網際協議,TCP 和 UDP 使用該協議從一個網路傳送資料包到另一個網路。把IP想像成一種高速公路,它允許其它協議在上面行駛並找到到其它電腦的出口。TCP和UDP是高速公路上的“卡車”,它們攜帶的貨物就是像HTTP,檔案傳輸協議FTP這樣的協議等。

TCP 與 UDP

都屬於傳輸層協議。

TCP(Transmission Control Protocol,傳輸控制協議)是面向連線的協議,也就是說,在收發資料前,必須和對方建立可靠的連線。一個TCP連線必須有三次握手、四次揮手。

UDP(User Data Protocol,使用者資料報協議)是一個非連線的協議,傳輸資料之前源端和終端不建立連線, 當它想傳送時就簡單地去抓取來自應用程式的資料,並儘可能快地把它扔到網路上

TCP UDP
連線性 面向連線 面向非連線
傳輸可靠性 可靠 不可靠
報文 面向位元組流 面向報文
效率 傳輸效率低 傳輸效率高
流量控制 滑動視窗
擁塞控制 慢開始、擁塞避免、快重傳、快恢復
傳輸速度
應用場合 對效率要求低,對準確性要求高或要求有連線的場景 對效率要求高,對準確性要求低

TCP和UDP協議的一些應用

TCP連線的建立與終止

TCP雖然是面向位元組流的,但TCP傳送的資料單元卻是報文段。一個TCP報文段分為首部和資料兩部分,而TCP的全部功能體現在它首部中的各欄位的作用。

TCP報文段首部的前20個位元組是固定的(下圖),後面有4n位元組是根據需要而增加的選項(n是整數)。因此TCP首部的最小長度是20位元組。

TCP報文首部

  • 源埠和目的埠,各佔2個位元組,分別寫入源埠和目的埠;
  • 序列號(Sequence number),佔4位元組。序號範圍是【0,2^32 - 1】,共2^32個序號。序號增加到 2^32-1後,下一個序號就又回到 0。TCP是面向位元組流的。在一個TCP連線中傳送的位元組流中的每一個位元組都按順序編號。整個要傳送的位元組流的起始序號必須在連線建立時設定。首部中的序號欄位值則是指的是本報文段所傳送的資料的第一個位元組的序號。例如,一報文段的序號是301,而接待的資料共有100位元組。這就表明:本報文段的資料的第一個位元組的序號是301,最後一個位元組的序號是400。顯然,下一個報文段(如果還有的話)的資料序號應當從401開始,即下一個報文段的序號欄位值應為401。這個欄位的序號也叫“報文段序號”;
  • 確認號(Acknowledge number),佔4個位元組,是期望收到對方下一個報文的第一個資料位元組的序號。例如,B收到了A傳送過來的報文,其序列號欄位是501,而資料長度是200位元組,這表明B正確的收到了A傳送的到序號700為止的資料。因此,B期望收到A的下一個資料序號是701,於是B在傳送給A的確認報文段中把確認號置為701;
  • 資料偏移,佔4位,它指出TCP報文段的資料起始處距離TCP報文段的起始處有多遠。
  • 保留,佔6位,保留為今後使用,但目前應置為0;
  • 緊急URG(URGent),當URG=1,表明緊急指標欄位有效。告訴系統此報文段中有緊急資料;
  • 確認ACK(ACKnowledgment),僅當ACK=1時,確認號欄位才有效。TCP規定,在連線建立後所有報文的傳輸都必須把ACK置1
  • 推送PSH(PuSH) ,當兩個應用程序進行互動式通訊時,有時在一端的應用程序希望在鍵入一個命令後立即就能收到對方的響應,這時候就將PSH=1;
  • 復位RST(ReSeT),當RST=1,表明TCP連線中出現嚴重差錯,必須釋放連線,然後再重新建立連線;
  • 同步SYN(SYNchronization),在連線建立時用來同步序號。當SYN=1,ACK=0,表明是連線請求報文,若同意連線,則響應報文中應該使SYN=1,ACK=1
  • 終止FIN(FINis),用來釋放連線。當FIN=1,表明此報文的傳送方的資料已經發送完畢,並且要求釋放
    • 視窗,佔2位元組,指的是通知接收方,傳送本報文你需要有多大的空間來接受;
  • 檢驗和,佔2位元組,校驗首部和資料這兩部分;
  • 緊急指標,佔2位元組,指出本報文段中的緊急資料的位元組數;
  • 選項,長度可變,定義一些其他的可選的引數

TCP是一種面向連線的單播協議,在傳送資料前,通訊雙方必須在彼此間建立一條連線。所謂的“連線”,其實是客戶端和伺服器的記憶體裡儲存的一份關於對方的資訊,如ip地址、埠號等。

TCP 三次握手

所謂三次握手(Three-way Handshake),是指建立一個 TCP 連線時,需要客戶端和伺服器總共傳送3個包。

三次握手的目的是連線伺服器指定埠,建立 TCP 連線,並同步連線雙方的序列號和確認號,交換 TCP 視窗大小資訊。

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

    建立連線。客戶端傳送連線請求報文段,這是報文首部中的同步位SYN=1,同時選擇一個初始序列號 seq=x ,此時,客戶端程序進入了 SYN-SENT(同步已傳送狀態)狀態。TCP規定,SYN報文段(SYN=1的報文段)不能攜帶資料,但需要消耗掉一個序號;

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

    伺服器收到客戶端的SYN報文段,如果同意連線,則發出確認報文。確認報文中應該 ACK=1,SYN=1,確認號ACKnum=x+1,同時,自己還要傳送SYN請求資訊,SYN=1,為自己初始化一個序列號 seq=y,伺服器端將上述所有資訊放到一個報文段(即SYN+ACK報文段)中,一併傳送給客戶端,此時,TCP伺服器程序進入了SYN-RCVD(同步收到)狀態。這個報文也不能攜帶資料,但是同樣要消耗一個序號

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

    客戶端收到伺服器的SYN+ACK報文段,再次傳送確認包(ACK),SYN 標誌位為0,ACK 標誌位為1,確認號 ACKnum = y+1,這個報文段傳送完畢以後,客戶端和伺服器端都進入ESTABLISHED(已建立連線)狀態,完成TCP三次握手。

為什麼需要三次握手呢?兩次不行嗎?

為了防止已失效的連線請求報文段突然又傳送到了服務端,因而產生錯誤。

具體例子:“已失效的連線請求報文段”的產生在這樣一種情況下:client發出的第一個連線請求報文段並沒有丟失,而是在某個網路結點長時間的滯留了,以致延誤到連線釋放以後的某個時間才到達server。本來這是一個早已失效的報文段。但server收到此失效的連線請求報文段後,就誤認為是client再次發出的一個新的連線請求。於是就向client發出確認報文段,同意建立連線。假設不採用“三次握手”,那麼只要server發出確認,新的連線就建立了。由於現在client並沒有發出建立連線的請求,因此不會理睬server的確認,也不會向server傳送資料。但server卻以為新的運輸連線已經建立,並一直等待client發來資料。這樣,server的很多資源就白白浪費掉了。採用“三次握手”的辦法可以防止上述現象發生。例如剛才那種情況,client不會向server的確認發出確認。server由於收不到確認,就知道client並沒有要求建立連線。”

TCP 四次揮手

TCP 的連線的拆除需要傳送四個包,因此稱為四次揮手(Four-way handshake),也叫做改進的三次握手。客戶端或伺服器均可主動發起揮手動作

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

    主機1(可以使客戶端,也可以是伺服器端),設定seq=x,向主機2傳送一個FIN報文段;此時,主機1進入FIN_WAIT_1狀態;這表示主機1沒有資料要傳送給主機2了;

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

    主機2收到了主機1傳送的FIN報文段,向主機1回一個ACK報文段,Acknnum=x+1,主機1進入FIN_WAIT_2狀態;主機2告訴主機1,我“同意”你的關閉請求;

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

    主機2向主機1傳送FIN報文段,請求關閉連線,同時主機2進入LAST_ACK狀態

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

    主機1收到主機2傳送的FIN報文段,向主機2傳送ACK報文段,然後主機1進入TIME_WAIT狀態;主機2收到主機1的ACK報文段以後,就關閉連線;此時,主機1等待2MSL後依然沒有收到回覆,則證明Server端已正常關閉,那好,主機1也可以關閉連線了,進入CLOSED狀態。

    主機 1 等待了某個固定時間(兩個最大段生命週期,2MSL,2 Maximum Segment Lifetime)之後,沒有收到伺服器端的 ACK ,認為伺服器端已經正常關閉連線,於是自己也關閉連線,進入CLOSED狀態。

為什麼連線的時候是三次握手,關閉的時候卻是四次握手?

因為當Server端收到Client端的SYN連線請求報文後,可以直接傳送SYN+ACK報文。其中ACK報文是用來應答的,SYN報文是用來同步的。但是關閉連線時,當Server端收到FIN報文時,很可能並不會立即關閉SOCKET,所以只能先回復一個ACK報文,告訴Client端,"你發的FIN報文我收到了"。只有等到我Server端所有的報文都發送完了,我才能傳送FIN報文,因此不能一起傳送。故需要四步握手。

由於 TCP 協議是全雙工的,也就是說客戶端和服務端都可以發起斷開連線。兩邊各發起一次斷開連線的申請,加上各自的兩次確認,看起來就像執行了四次揮手。

為什麼TIME_WAIT狀態需要經過2MSL(最大報文段生存時間)才能返回到CLOSE狀態?

雖然按道理,四個報文都發送完畢,我們可以直接進入CLOSE狀態了,但是我們必須假象網路是不可靠的,有可以最後一個ACK丟失。所以TIME_WAIT狀態就是用來重發可能丟失的ACK報文。

還有一個原因,防止類似與“三次握手”中提到了的“已經失效的連線請求報文段”出現在本連線中。客戶端傳送完最後一個確認報文後,在這個2MSL時間中,就可以使本連線持續的時間內所產生的所有報文段都從網路中消失。這樣新的連線中不會出現舊連線的請求報文。