TCP協議 - 面向鏈接

一.TCP特性概覽

1.面向鏈接

TCP是基於鏈接進行數據交互,通訊雙方在進行數據交互以前須要創建鏈接,該鏈接也只能用在雙方之間進行交互。這點不像UDP中的組播和廣播,能夠在同一組中多個主機交互數據。這也是致使TCP協議的複雜性的因素之一,創建鏈接涉及到三次握手和四次揮手的過程。服務器

2.可靠性保證

  1. 分段機制。TCP將須要傳輸的數據分紅合適的報文段,而後逐個傳輸
  2. 定時器的超時和重傳機制。TCP對每一個發送的報文段都設置一個定時器等待目標端返回確認收到的響應,若是未收到,則超時重傳
  3. 確認報文機制。接受方收到報文後會返回確認報文告訴發送方收到報文
  4. 校驗和機制。TCP保持首部和數據的校驗和,接收方將校驗段的校驗和,若是錯誤將丟棄不確認等待發送方超時重傳
  5. 有序機制。TCP將對報文段進行排序,保證報文段有序性
  6. 去重機制。TCP將對報文段進行去重
  7. 流控機制。因爲接收方和發送的TCP緩衝區容量不一致和處理速度,須要對發送方的流量進行控制,防止網絡阻塞

3.字節流

TCP雙方進行數據交互時,數據形式爲8bit組成的字節流,TCP也不在這些字節流中作任何特殊標識。網絡

這種字節流的特色表現以下:tcp

  1. TCP不關注數據的具體內容是二進制仍是ASCII字符
  2. 發送方發送的字節流和接收方接受的字節流總體表現一致,可是發送的次數和每次的大小與接受方接收的次數和大小並無關係。

二.TCP報文格式

TCP報文段分爲兩部分:性能

  • TCP首部。TCP首部是描述TCP報文段的信息,通常爲20個字節
  • 數據部分。數據部分包含應用層須要傳輸的數據

TCP首部中包含如下信息:優化

  1. 16位源端口號和16位目的端口號。網絡中進程通訊須要IP + PORT來肯定惟一進程。由於IP地址是有IP協議涉及,因此在TCP傳輸中只須要肯定接收方和目標方的端口號即可以創建鏈接,雙方的進程才能正常通訊
  2. 32位序號和32位確認序號。用於標識發送的字節流和指望接收的字節流
  3. 4位首部長度。表示TCP首部長度,首部中有可選收據長度
  4. 6位標誌位。用於標識不一樣的鏈接狀態
  5. 16位窗口大小。用於流量控制,接收方指望接受的數據大小
  6. 16位校驗和。TCP首部和TCP數據的校驗碼,由發送方計算生成,接收方用於校驗
  7. 16位緊急指針。用於發送緊急數據

三.創建和關閉TCP鏈接

這裏使用telent wwww.baidu.com 80和tcpdump -S -t host www.baidu.com演示創建和關閉TCP鏈接。並經過TCP創建鏈接和終止鏈接的時序圖以及報文分析。操作系統

其中創建TCP鏈接須要三次握手,關閉TCP鏈接須要四次揮手。設計

1.三次握手

這裏將通訊雙方分別稱爲發送端和接收端。三次握手是應用在發送端和接收端在數據交互前創建TCP鏈接的過程。這個過程須要三步驟才能完成:3d

  1. 發送端爲即將創建的鏈接生成一個SEQ做爲TCP報文段中的序號,並將SYN位置1,發送SYN包至接收方,而後進入SYN_SEND狀態
  2. 接收端在收到SYN包後,進入SYN_RCVD狀態,也生成一個SEQ做爲返回給接收端報文的序號,並將接收到的SEQ+1做爲返回報文的確認序號,發送SYN包至接收端,且SYN和ACK位置1。該包有兩個做用,做爲SYN包進行創建鏈接,同時做爲ACK包回覆發送端
  3. 接收端收到SYN包後,進入ESTABLISHED狀態,表示在發送端鏈接已經創建。而後將接收到的SYN包的SEQ+1,將結果做爲發送給接收端的ACK包的確認序號,且ACK位置1

由於創建鏈接的過程須要三次交互通訊,因此將其稱爲三次握手指針

從tcpdump抓取的報文段能夠看出,三次握手的過程:code

IP 10.1.133.253.58062 > 183.232.231.174.http: Flags [S], seq 1209507409, win 65535, options [mss 1460,nop,wscale 5,nop,nop,TS val 1469077440 ecr 0,sackOK,eol], length 0
IP 183.232.231.174.http > 10.1.133.253.58062: Flags [S.], seq 2198201899, ack 1209507410, win 8192, options [mss 1452,nop,wscale 5,nop,nop,nop,nop,nop,nop,nop,nop,nop,nop,nop,nop,sackOK,eol], length 0
IP 10.1.133.253.58062 > 183.232.231.174.http: Flags [.], ack 2198201900, win 8192, length 0

報文格式爲:源IP.端口 > 目標IP.端口 狀態位標誌 序號 確認序號 窗口大小 可選項。

Note:爲何須要三次握手?
發送端和接收端各發送一次,須要告訴對方須要創建鏈接同時包含窗口大小等信息,這是必不可少的二次。因爲接收端沒法肯定發送端是否接受到本身的請求,因此須要發送端發一次回覆進行確認。可是接收端爲何不須要回復發送端的ACK,由於這是一個循環依賴的問題,沒有盡頭。因此至少須要三次握手才能基本創建鏈接。

2.四次揮手

在數據交互完成後,須要關閉鏈接釋放系統資源,如內存,文件句柄等。由於TCP鏈接時全雙工模式,須要關閉雙向的數據數據發送。爲了保證釋放資源的可靠性,須要四次握手。四次握手的過程當中,發送端和接受端都要關閉各自鏈接釋放資源,從而避免半關閉狀態。四次握手過程:

  1. 接收端主動發起關閉,向發送端發送FIN包,進入FIN_WAIT狀態,表示接收端方向上將不會再有數據發出,可是一樣還能夠收數據
  2. 爲了確保發送端收到了FIN包,發送端須要返回ACK包,確認序號爲FIN包的SEQ+1,同時進入CLOSE_WAIT。由於TCP鏈接時全雙工模式,因此發送端這裏不必定須要關閉鏈接,它仍然能夠發送數據
  3. 待發送端將再也不有數據須要發送時,將發送FIN包至接收端,進入LAST_ACK狀態
  4. 爲了確保接收端收到FIN包,須要回覆ACK包,同時進入TIME_WAIT狀態。

由於關閉鏈接的過程有雙方之間的四次交互過程,因此將其稱爲四次揮手

IP 183.232.231.174.http > 10.1.133.253.58062: Flags [F.], seq 2198201900, ack 1209507427, win 776, length 0
IP 10.1.133.253.58062 > 183.232.231.174.http: Flags [.], ack 2198201901, win 8192, length 0
IP 10.1.133.253.58062 > 183.232.231.174.http: Flags [F.], seq 1209507427, ack 2198201901, win 8192, length 0
IP 183.232.231.174.http > 10.1.133.253.58062: Flags [.], ack 1209507428, win 776, length 0

3.2MSL的TIME_WAIT

TIME_WAIT是主動發起關閉TCP鏈接在四次揮手過程當中出現的鏈接狀態。當主動發起關閉鏈接的一方在接收到被動關閉方請求的FIN包後,將響應ACK包後,將進入該狀態。該狀態的持續時間爲2MSL時間,處於該狀態的TCP鏈接將等待關閉。

MSL(Maximum Segment Lifetime)是報文段在網絡中存活的最長時間,超過這個時間的報文將會被丟棄。

即主動關閉TCP鏈接一方在發送ACK包時,必須等待TIME_WAIT狀態2倍MSL時間。TCP協議這樣處理主要有如下兩方面緣由:

  1. 確保被動關閉一方未能接收到主動關閉鏈接一方發的ACK包時可以重發FIN包。由於由於網絡問題,被動關閉一方可能沒法接收到最後的ACK包,而不能正常關閉鏈接。這時被動關閉方能夠從新發一個FIN包給主動關閉方,因爲主動關閉方處於TIME_WAIT狀態,仍然能夠恢復ACK包。因爲ACK包和FIN包都在網絡中停留最長時間爲MSL,因此TIME_WAIT爲2MSL,保證儘量最長的時間接受被動關閉方的FIN包。若是主動關閉方在恢復最後的ACK包後,不通過TIME_WAIT狀態而直接CLOSED,那麼可能存在被動關閉方未能接收到ACK包,從而超時重傳FIN包,這時主動關閉方的TCP鏈接已經關閉,在接收到這個FIN包後尋找不到對應的鏈接,從而回復RST復位包,被動關閉方接收到RST包後會處理錯誤從而致使沒法正常關閉鏈接釋放資源。

  2. 避免前一個鏈接產生的報文因爲在網絡中停留從而與新鏈接的報文耦合在一塊兒產生數據錯誤。都知道表明TCP鏈接的標識是(源IP,源端口號,目的IP,目的端口號)。由於TCP鏈接時全雙工的雙向通訊,關閉時須要雙端都關閉保證雙向都沒有數據流通。當主動方發起關閉,只能保證主動方將沒有任何數據向外發送。此時被動仍然可能會發送報文,加入這些報文在網絡節點中停留,而後主動方又當即與被動方創建新的TCP鏈接且新的鏈接和上次TCP鏈接的標識是同樣的,此時在網絡中停留的報文也恰好抵達主動方,主動方處理該報文必然會發生錯誤。主動關閉方在停留2MSL的TIME_WAIT狀態,保證TCP鏈接中網絡報文儘量的過時。

總結來講,主動發起關閉TCP鏈接的一方停留在TIME_WAIT狀態2倍的MSL時間主要爲了:

  • 爲了被動關閉一方可以重傳FIN包,保證正常的關閉TCP鏈接釋放資源
  • 避免上一次TCP鏈接的網絡中殘留報文耦合干擾新的TCP鏈接

當2MSL事後,TCP鏈接將正式被關閉CLOSED釋放資源。可是停留在TIME_WAIT狀態的TCP鏈接有如下的特徵:

  1. 處於這種狀態的TCP鏈接將會仍然佔用該端口號。好比創建TCP鏈接的源端口好爲52222,當發起方主動關閉TCP鏈接,當處於TIME_WAIT狀態式,該端口將被佔用

  2. TCP鏈接沒有被關閉,因此資源仍然沒有被釋放,好比:內存、CPU、IO等

如下是筆者獲取本機的網絡鏈接,經過netstat -an發現:

其中端口爲61321的TCP鏈接處於TIME_WAIT狀態,而後經過Java的ServerSocket監聽61321端口:

ServerSocket serverSocket = new ServerSocket();
SocketAddress address = new InetSocketAddress("10.1.133.253", 61321);
serverSocket.setReuseAddress(false);
serverSocket.bind(address);

在運行以上代碼時拋出如下異常:

對於以上問題,能夠經過設置Socket參數SO_REUSEADDR來改變以上的行爲,若是該參數爲TRUE,則能夠重複使用綁定端口,從而避免TIME_WAIT致使端口被佔用的問題。

對於客戶端這兩點通常影響不是很大,可是對於服務端而言,若是出現大量的TIME_WAIT狀態的鏈接,可能會形成服務端的性能大幅度降低,處理能力減弱,甚至嚴重服務器崩潰癱瘓。由於大量的網絡鏈接沒有及時釋放,佔用大量的系統資源,致使沒法處理新的鏈接和請求。

對於這種問題通常的解決方式爲兩種方式:

  • 從操做系統層進行優化。經過配置系統參數,從而解決該問題

  • 客戶端發起鏈接關閉,避免服務端主動關閉鏈接。好比:客戶端使用完Socket鏈接後,主動進行關閉釋放資源,這樣服務端將不會存在TIME_WAIT

修改/etc/sysctl.conf配置,加入:

net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_fin_timeout = 30

net.ipv4.tcp_tw_reuse = 1表示開啓重用,能夠將TIME_WAIT狀態的Socket從新用於新的TCP鏈接,默認爲0表示關閉;
net.ipv4.tcp_tw_recycle = 1表示開啓接快速回收TIME_WAIT狀態的TCP連,默認爲0表示關閉;
net.ipv4.tcp_fin_timeout = 30修改默認的TIME_WAIT的時間,改成30s;

4.RST報文的做用

在前文中介紹了TCP報文格式時,提到幾種狀態標誌位,其中有RST標誌位。當該標誌位爲1時表示報文段是RST報文。在TCP的設計中RST報文有如下幾種做用:

  1. 不存在的端口的鏈接請求

當想沒有使用的端口上發送報文時,目標端將會恢復RST報文。這個能夠用檢測目標端口是否被監聽。

  1. 異常終止一個鏈接

當應用發生異常時,終止鏈接釋放資源,此時將發生RST報文到對端。而非經過FIN這種正常關閉鏈接,能夠達到資源快速釋放。好比對端可能會出現:

read error: Connection reset by peer

這種狀況代表,對端發生異常,及時關閉TCP鏈接回覆了RST報文。

  1. 檢測半打開的鏈接

TCP是全雙工的雙向通訊模式,當關閉釋放資源時,須要雙端都關閉鏈接進行資源釋放。這樣的狀況就存在一端關閉了TCP鏈接,而另外一端沒有關閉鏈接。這種狀態被稱爲半關閉狀態
對於高訪問量的服務型應用而言,常常都會使用鏈接池這種長鏈接方式,這種狀況常常會出現半關閉狀態。應爲通訊雙端沒法感知彼此鏈接狀態的變化,大多數應用都經過心跳檢測,斷連重連解決。

心跳檢測的原理即經過持續隔斷的發送心跳數據至對端,對端回覆相應的數據來檢測鏈接的正常有效性。當出現回覆超時,或者鏈接被對端斷開的情形式能夠認爲鏈接失效,這時能夠關閉該端鏈接,並進行重連。

其中鏈接被對端斷開的情形,就和第一種狀況相似,對端已經不存在該鏈接的信息,從而回復RST報文,表示該鏈接已經被對端關閉。

該種狀況與第一種的區別在於,第一種狀況是嘗試與未監聽的端口上創建鏈接過程被回覆RST報文。而該中狀況是在已創建的鏈接上發送報文,因爲對端關閉,從而回復RST報文。

總結

本篇文章介紹了TCP的基礎特性,鏈接創建和終止的過程以及相關狀態的含義,其中針對2MSL狀態和RST報文段的做用作了說明。這些內容主要都介紹TCP的面向鏈接的特性,對於TCP另外一個很是重要的特徵-可靠性後續文章介紹。

相關文章
相關標籤/搜索