1 客戶端的協議棧向服務器端發送了SYN包,並告訴服務器端當前發送序列號j,客戶端進入SYNC_SENT狀態;服務器
2 服務器端的協議棧收到這個包以後,和客戶端進行ACK應答,應答的值爲j+1,表示對SYN包j的確認,同時服務器也發送一個SYN包,告訴客戶端當前個人發送序列號併發
爲k,服務器端進入SYNC_RCVD狀態;編碼
3 客戶端協議棧收到ACK以後,使得應用程序從connect調用返回,表示客戶端到服務器端的單向鏈接創建成功,客戶端的狀態爲ESTABLISHED,同時客戶端協議棧也設計
會對服務器端的SYN包進行應答,應答數據爲k+1;blog
應答包到達服務器端後,服務器端協議棧使得accept阻塞調用返回,這個時候服務器端到客戶端的單向鏈接也創建成功,服務器端也進入ESTABLISHED狀態。ip
形象一點的比喻是這樣的,有A和B想進行通話:內存
A先對B說:「喂,你在麼?我在的,個人口令是j。」資源
B收到以後大聲回答:「我收到你的口令j並準備好了,你準備好了嗎?個人口令是k。」it
A收到以後也大聲回答:「我收到你的口令k並準備好了,咱們開始吧。」基礎
能夠看到,這樣的應答過程總共進行了三次,這就是TCP鏈接創建之因此被叫爲「三次握手」的緣由了。
咱們能夠給 UDP 找一個相似的例子,這個例子就是郵寄明信片。在這個例子中,發信方在明信片中填上了接收方的地址和郵編,投遞到郵局的郵筒以後,就能夠無論
了。發信方也能夠給這個接收方再郵寄第二張、第三張,甚至是第四張明信片,可是這幾張明信片之間是沒有任何關係的,他們的到達順序也是不保證的,有可能最後寄出的
第四張明信片最早到達接收者的手中,由於沒有序號,接收者也不知道這是第四張寄出的明信片;並且,即便接收方沒有收到明信片,也沒有辦法從新郵寄一遍該明信片。
TCP 是一個面向鏈接的協議,TCP 在 IP 報文的基礎上,增長了諸如重傳、確認、有序傳輸、擁塞控制等能力,通訊的雙方是在一個肯定的上下文中工做的。
而 UDP 則不一樣,UDP 沒有這樣一個肯定的上下文,它是一個不可靠的通訊協議,沒有重傳和確認,沒有有序控制,也沒有擁塞控制。咱們能夠簡單地理解爲,在 IP 報
文的基礎上,UDP 增長的能力有限。
UDP 不保證報文的有效傳遞,不保證報文的有序,也就是說使用 UDP 的時候,咱們須要作好丟包、重傳、報文組裝等工做。
首先,一方應用程序調用 close,咱們稱該方爲主動關閉方,該端的 TCP 發送一個 FIN 包,表示須要關閉鏈接。以後主動關閉方進入 FIN_WAIT_1 狀態。
接着,接收到這個 FIN 包的對端執行被動關閉。這個 FIN 由 TCP 協議棧處理,咱們知道,TCP 協議棧爲 FIN 包插入一個文件結束符 EOF 到接收緩衝區中,應用程序可
以經過 read 調用來感知這個 FIN 包。必定要注意,這個 EOF 會被放在已排隊等候的其餘已接收的數據以後,這就意味着接收端應用程序須要處理這種異常狀況,由於 EOF
表示在該鏈接上再無額外數據到達。此時,被動關閉方進入 CLOSE_WAIT 狀態。
接下來,被動關閉方將讀到這個 EOF,因而,應用程序也調用 close 關閉它的套接字,這致使它的 TCP 也發送一個 FIN 包。這樣,被動關閉方將進入 LAST_ACK 狀態。
最終,主動關閉方接收到對方的 FIN 包,並確認這個 FIN 包。主動關閉方進入 TIME_WAIT 狀態,而接收到 ACK 的被動關閉方則進入 CLOSED 狀態。進過 2MSL 時間
以後,主動關閉方也進入 CLOSED 狀態。
你能夠看到,每一個方向都須要一個 FIN 和一個 ACK,所以一般被稱爲四次揮手。
固然,這中間使用 shutdown,執行一端到另外一端的半關閉也是能夠的。
TCP 鏈接終止時,主機 1 先發送 FIN 報文,主機 2 進入 CLOSE_WAIT 狀態,併發送一個 ACK 應答,同時,主機 2 經過 read 調用得到 EOF,並將此結果通知應用程
序進行主動關閉操做,發送 FIN 報文。主機 1 在接收到 FIN 報文後發送 ACK 應答,此時主機 1 進入 TIME_WAIT 狀態。
主機 1 在 TIME_WAIT 停留持續時間是固定的,是最長分節生命期 MSL(maximum segment lifetime)的兩倍,通常稱之爲 2MSL。和大多數 BSD 派生的系統同樣,
Linux 系統裏有一個硬編碼的字段,名稱爲TCP_TIMEWAIT_LEN,其值爲 60 秒。也就是說,Linux 系統停留在 TIME_WAIT 的時間爲固定的 60 秒。
過了這個時間以後,主機 1 就進入 CLOSED 狀態。只有發起鏈接終止的一方會進入 TIME_WAIT 狀態。
爲何不直接進入 CLOSED 狀態,而要停留在 TIME_WAIT 這個狀態
首先,這樣作是爲了確保最後的 ACK 能讓被動關閉方接收,從而幫助其正常關閉。
TCP 在設計的時候,作了充分的容錯性設計,好比,TCP 假設報文會出錯,須要重傳。在這裏,若是圖中主機 1 的 ACK 報文沒有傳輸成功,那麼主機 2 就會從新發送 FIN 報文。
若是主機 1 沒有維護 TIME_WAIT 狀態,而直接進入 CLOSED 狀態,它就失去了當前狀態的上下文,只能回覆一個 RST 操做,從而致使被動關閉方出現錯誤。
如今主機 1 知道本身處於 TIME_WAIT 的狀態,就能夠在接收到 FIN 報文以後,從新發出一個 ACK 報文,使得主機 2 能夠進入正常的 CLOSED 狀態。
過多的 TIME_WAIT 的主要危害有兩種。
第一是內存資源佔用,這個目前看來不是太嚴重,基本能夠忽略。
第二是對端口資源的佔用,一個 TCP 鏈接至少消耗一個本地端口。要知道,端口資源也是有限的,通常能夠開啓的端口爲 32768~61000 ,也能夠經過
net.ipv4.ip_local_port_range指定,若是 TIME_WAIT 狀態過多,會致使沒法建立新鏈接。這個也是咱們在一開始講到的那個例子。