三次握手程序
1 客戶端的協議堆疊向服務器端發送了SYN包,并告訴服務器端當前發送序列號j,客戶端進入SYNC_SENT狀態;
2 服務器端的協議堆疊收到這個包之后,和客戶端進行ACK應答,應答的值為j+1,表示對SYN包j的確認,同時服務器也發送一個SYN包,告訴客戶端當前我的發送序列號
為k,服務器端進入SYNC_RCVD狀態;
3 客戶端協議堆疊收到ACK之后,使得應用程式從connect呼叫回傳,表示客戶端到服務器端的單向連接建立成功,客戶端的狀態為ESTABLISHED,同時客戶端協議堆疊也
會對服務器端的SYN包進行應答,應答資料為k+1;
應答包到達服務器端后,服務器端協議堆疊使得accept阻塞呼叫回傳,這個時候服務器端到客戶端的單向連接也建立成功,服務器端也進入ESTABLISHED狀態,
形象一點的比喻是這樣的,有A和B想進行通話:
A先對B說:“喂,你在么?我在的,我的口令是j,”
B收到之后大聲回答:“我收到你的口令j并準備好了,你準備好了嗎?我的口令是k,”
A收到之后也大聲回答:“我收到你的口令k并準備好了,我們開始吧,”
可以看到,這樣的應答程序總共進行了三次,這就是TCP連接建立之所以被叫為“三次握手”的原因了,
UDP
我們可以給 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,執行一端到另一端的半關閉也是可以的,
TIME_WAIT
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 狀態,
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 的危害
過多的 TIME_WAIT 的主要危害有兩種,
第一是記憶體資源占用,這個目前看來不是太嚴重,基本可以忽略,
第二是對埠資源的占用,一個 TCP 連接至少消耗一個本地埠,要知道,埠資源也是有限的,一般可以開啟的埠為 32768~61000 ,也可以通過
net.ipv4.ip_local_port_range指定,如果 TIME_WAIT 狀態過多,會導致無法創建新連接,這個也是我們在一開始講到的那個例子,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/224527.html
標籤:其他
