一、前言
1、TCP通信
TCP是面向連接的協議。運輸連接是用來傳送TCP報文的,而運輸連接的建立和釋放是每一次面向連接的通信中必不可少的過程。可以類比傳統的電話網,撥通號碼,開始通話,掛斷電話。用戶進程和服務器進程需要完成一次通信都需要完成三個階段:
- 連接建立
- 數據傳送
- 連接釋放
TCP協議中,主動發起請求的一端稱為客戶端,被動連接的一端稱為服務端。由於全雙工,不管是客戶端還是服務端,TCP連接建立完后都能發送和接收數據。
2、序列號、確認號及標志位等
- 序列號seq:占4個字節,用來標記數據段的順序,TCP把連接中發送的所有數據字節都編上一個序號,第一個字節的編號由本地隨機產生,給字節編上序號后,就給每一個報文段指派一個序號,序列號seq就是這個報文段中的第一個字節的數據編號。
- 確認號ack:占4個字節,期待收到對方下一個報文段的第一個數據字節的序號,序列號表示報文段攜帶數據的第一個字節的編號,而確認號指的是期望接受到下一個字節的編號,因此擋牆報文段最后一個字節的編號+1即是確認號。
- 確認ACK:占1個比特位,僅當ACK=1,確認號字段才有效。ACK=0,確認號無效。
- 同步SYN:連接建立時用於同步序號。當SYN=1,ACK=0表示:這是一個連接請求報文段。若同意連接,則在響應報文段中使用SYN=1,ACK=1.因此,SYN=1表示這是一個連接請求,或連接接收報文,SYN這個標志位只有在TCP建立連接才會被置為1,握手完成后SYN標志位被置為0.
- 終止FIN:用來釋放一個連接。
3、知乎示例
為什么是三次握手,而不是兩次或者四次握手,知乎上有個形象的示例。
三次握手:
“喂,你聽得到嗎?”
“我聽得到呀,你聽得到我嗎?”
“我能聽到你,今天balabala……”
兩次握手:
“喂,你聽得到嗎?”
“我聽得到呀”
“喂喂,你聽得到嗎?”
“草,我聽得到呀!!!!”
“你TM能不能聽到我講話啊!!喂!”
“……”
四次握手:
“喂,你聽得到嗎?”
“我聽得到呀,你聽得到我嗎?”
“我能聽到你,你能聽到我嗎?”
“……不想跟傻逼說話”
二、TCP三次握手
1、三報文握手
TCP三次握手中的三次,連接建立,其實是指一次握手中交換了三個報文,進行了三次信息的單向傳遞,而不是真正進行了三次握手。用英文就是 three messages handshake,這里的handshake是單數形式。所以,三次握手其實是三報文握手。
而TCP四次揮手,也就是在連接釋放的過程中,需要交換四個報文。
2、圖解TCP三次握手
圖自謝希仁《計算機網絡(第五版)》
最初兩端的TCP進程都處於CLOSED關閉狀態,A主動打開連接,而B被動打開連接。(A、B關閉狀態CLOSED——B收聽狀態LISTEN——A同步已發送狀態SYN-SENT——B同步收到狀態SYN-RCVD——A、B連接已建立狀態ESTABLISHED)
TCP三次握手過程
B的TCP服務器進程先創建傳輸控制塊TCB,准備接受客戶進程的連接請求。然后服務器進程就處於LISTEN(收聽)狀態,等待客戶的連接請求。若有,則作出響應。
1)第一次握手:A的TCP客戶進程也是首先創建傳輸控制塊TCB,然后向B發出連接請求報文段,(首部的同步位SYN=1,初始序號seq=x),(SYN=1的報文段不能攜帶數據)但要消耗掉一個序號,此時TCP客戶進程進入SYN-SENT(同步已發送)狀態。
2)第二次握手:B收到連接請求報文段后,如同意建立連接,則向A發送確認,在確認報文段中(SYN=1,ACK=1,確認號ack=x+1,初始序號seq=y),測試TCP服務器進程進入SYN-RCVD(同步收到)狀態;
3)第三次握手:TCP客戶進程收到B的確認后,要向B給出確認報文段(ACK=1,確認號ack=y+1,序號seq=x+1)(初始為seq=x,第二個報文段所以要+1),ACK報文段可以攜帶數據,不攜帶數據則不消耗序號。TCP連接已經建立,A進入ESTABLISHED(已建立連接)。
當B收到A的確認后,也進入ESTABLISHED狀態。
3、為什么要進行三次握手?
也就是說為什么要在A最后還要再發送一次確認?
是為了防止已經失效的請求報文端,再次傳到B,因而產生錯誤。
<1>、正常情況
client發送了連接請求,但是請求報文因為種種原因丟失而未確認。於是A超時重傳,再一次發送請求連接報文。server收到了,然后再發送請求確認報文,並最終完成client與server的連接。數據傳輸結束后,釋放連接。這個過程中,client一共發送了兩個請求連接報文段,第一個丟失,第二個到達server。
<2>、 異常情況
client發出的第一個連接請求報文段並沒有丟失,而是在某個網絡結點長時間的滯留了,以致延誤到連接釋放以后的某個時間才到達server。本來這是一個早已失效的報文段。但server收到此失效的連接請求報文段后,就誤認為是client再次發出的一個新的連接請求。於是就向client發出確認報文段,同意建立連接。假設不采用三次握手,那么只要server發出確認,新的連接就建立了。由於現在client並沒有發出建立連接的請求,因此不會理睬server的確認,也不會向server發送數據。但server卻以為新的運輸連接已經建立,並一直等待client發來數據。這樣,server的很多資源就白白浪費掉了。
采用三次握手的辦法可以防止上述現象發生。
三、TCP四次揮手
1、四報文揮手
想要從客戶端到服務端釋放連接,需要四次報文傳輸。
2、圖解TCP四次揮手
圖自謝希仁《計算機網絡(第五版)》
數據傳輸結束后,通信的雙方都可釋放連接,A和B都處於ESTABLISHED狀態。(A、B連接建立狀態ESTABLISHED——A終止等待1狀態FIN-WAIT-1——B關閉等待狀態CLOSE-WAIT——A終止等待2狀態FIN-WAIT-2——B最后確認狀態LAST-ACK——A時間等待狀態TIME-WAIT——B、A關閉狀態CLOSED)
TCP四次揮手過程
1)A的應用進程先向其TCP發出連接釋放報文段(FIN=1,序號seq=u),並停止再發送數據,主動關閉TCP連接,進入FIN-WAIT-1(終止等待1)狀態,等待B的確認。
2)B收到連接釋放報文段后即發出確認報文段,(ACK=1,確認號ack=u+1,序號seq=v),B進入CLOSE-WAIT(關閉等待)狀態,此時的TCP處於半關閉狀態,A到B的連接釋放。
3)A收到B的確認后,進入FIN-WAIT-2(終止等待2)狀態,等待B發出的連接釋放報文段。
4)B沒有要向A發出的數據,B發出連接釋放報文段(FIN=1,ACK=1,序號seq=w,確認號ack=u+1),B進入LAST-ACK(最后確認)狀態,等待A的確認。
5)A收到B的連接釋放報文段后,對此發出確認報文段(ACK=1,seq=u+1,ack=w+1),A進入TIME-WAIT(時間等待)狀態。此時TCP未釋放掉,需要經過時間等待計時器設置的時間2MSL后,A才進入CLOSED狀態。
TCP四次揮手總結
客戶端發送FIN后,進入終止等待狀態,服務器收到客戶端連接釋放報文段后,就立即給客戶端發送確認,服務器就進入CLOSE_WAIT狀態,此時TCP服務器進程就通知高層應用進程,因而從客戶端到服務器的連接就釋放了。此時是“半關閉狀態”,即客戶端不可以發送給服務器,服務器可以發送給客戶端。
此時,如果服務器沒有數據報發送給客戶端,其應用程序就通知TCP釋放連接,然后發送給客戶端連接釋放數據報,並等待確認。客戶端發送確認后,進入TIME_WAIT狀態,但是此時TCP連接還沒有釋放,然后經過等待計時器設置的2MSL后,才進入到CLOSE狀態。
3、為什么時間是2MSL
MSL即Maximum Segment Lifetime,就是最大報文生存時間,是任何報文在網絡上的存在的最長時間,超過這個時間報文將被丟棄。這里的2MSL是時間等待計時器設置的。
<1>、理由一
在Client發送出最后的ACK回復,但該ACK可能丟失。Server如果沒有收到ACK,將不斷重復發送FIN片段。所以Client不能立即關閉,它必須確認Server接收到了該ACK。Client會在發送出ACK之后進入到TIME_WAIT狀態。Client會設置一個計時器,等待2MSL的時間。如果在該時間內再次收到FIN,那么Client會重發ACK並再次等待2MSL。所謂的2MSL是兩倍的MSL(Maximum Segment Lifetime)。MSL指一個片段在網絡中最大的存活時間,2MSL就是一個發送和一個回復所需的最大時間。如果直到2MSL,Client都沒有再次收到FIN,那么Client推斷ACK已經被成功接收,則結束TCP連接。
<2>、理由二
client再發送完最后一個報文端ACK之后,再經過2MSL的時間,就可以是本次連接持續時間內的所有產生的報文段都消失,這樣下一次連接中就不會出現舊的請求連接報文段。
4、為什么是四次握手
為什么連接的時候是三次握手,關閉的時候卻是四次握手?
答:因為當Server端收到Client端的SYN連接請求報文后,可以直接發送SYN+ACK報文。其中ACK報文是用來應答的,SYN報文是用來同步的。但是關閉連接時,當Server端收到FIN報文時,很可能並不會立即關閉SOCKET,所以只能先回復一個ACK報文,告訴Client端,"你發的FIN報文我收到了"。只有等到我Server端所有的報文都發送完了,我才能發送FIN報文,因此不能一起發送。故需要四步握手。
四、參考
參考 謝希仁《計算機網絡(第五版)》
另外強烈推薦 戶根勤《網絡是怎樣連接的》