- 位元組。
客戶端收到服務端傳送的 SYN 和 ACK 之後,傳送ACK 的 ACK,之後處於 ESTABLISHED 狀態,因為它一發一收成功了。服務端收到 對ACK 的 ACK 之後,處於 ESTABLISHED 狀態,因為它也一發一收了。
為什麼不是兩次、四次或者更多次?
比如AB要建立連線,A發起一個連線但連線請求沒有響應,可能包丟了、包繞彎路了、超時了等等,A於是繼續給B發包,終於B收到了請求包但A並不知道,所以A可能還會繼續發。B收到包之後,知道了A存在並且要和它建立連線。如果B不想建立連線,A重試一會後放棄,連線建立失敗沒有問題。如果B願意建立連線,那就會發應答包給A。同樣這個應答包會和請求包出現一樣的問題,那麼對於B不能認為連線建立好了。
B傳送的應答包可能會傳送多次,只要有一次到達A,那麼A就認為連線已經建立了,因為A的訊息有去有回,相當於兩次握手。假如這時候連線已經建立,並且做了簡單通訊後,結束了連線。上面講了A要建立連線的時候,可能會傳送多個請求包。有的請求包繞了一大圈又回來了,B 會認為這也是一個正常的的請求,再次建立連線,但這個連線不會正常接發資料、也不會終結,B就一直等待。A發的訊息有去有回,但B發出的訊息沒有回,所以兩次握手肯定不行。其實三次握手中,A發給B回覆的回覆也會丟、繞路或者B掛了,按照這個邏輯B還要再發個確認就變成了四次握手,這樣不停套娃多達幾百次也可以,但這也不能保證就可靠了。TCP連線的設計原則是雙發的訊息都有去有回就基本可以了。
大部分情況下,A和B建立連線後會A會馬上傳送資料,如果A發給B的應答丟了,A後續傳送的資料到達時,B可以認為連線已經建立;又或者B掛了,那麼直接報錯返回B不可達資訊都沒問題。如果A就是不傳送資料,客戶端可以開啟keeplive機制,傳送探活包。服務端設定規定時間,超時了就主動關閉,釋放資源。
TCP把資料看成一個無結構、有序的位元組流,一個報文段的序號是該報文段首位元組的位元組流編號。三次握手除了確保雙方訊息有去有回,最重要的是解決tcp包的序號問題。A、B雙方互相告知對方包的起始序號。又會有個疑問,為什麼一定要交換序號,不能從序號1開始發包嗎?假設A給B發3個包,序號為1、2、3,但3包可能繞路了,沒有正常到達B,A又重新傳送。後來A掉線了,重新連上B後又從1開始,傳送兩個包1、2,但上次出問題的包3又回來了,發給了B,B認為這就是A要發給它的下一個包,於是出現錯誤。因此每個連線都要有不同的序號,序號的起始序號每4ms加一,等遇到重複的序號要4個多小時。比如3號包繞路了,就要隔4個多小時連線的起始序號才是3,這麼長時間3號包已經無效了,因為IP包頭裡有TTL(生存時間)。
如果一切正常,進行第三次揮手A收到B的主動關閉請求併傳送確認後進入TIME-WAIT狀態,為什麼需要這個狀態呢?假設A傳送完確認訊息後直接關閉,如果B沒收到這個確認訊息,那B會重發結果就是B關閉失敗。還有一種情況是A的埠空出來,但B還保持原來狀態不知道A的情況。如果 A 的埠被一個新的應用佔用了,這個新的應用會收到上個連線中 B 發過來的包,資料接收錯誤。所以A要進入TIME-WAIT狀態,並且TCP設定了等待時間2MSL(報文段最大生存時間),任何報文在網路上存在超過這個時間將被丟棄。協議規定 MSL 為 2 分鐘,實際應用中常用的是 30秒,1 分鐘和 2 分鐘等。還有一種情況是超過了2MSL,B依然沒有收到對FIN的ACK。這時即便A收到了B重發的FIN,A會直接傳送RST,表示重新開始揮手。
參考資料:《趣談網路協議》劉超
《計算機網路:自頂向下方法》原書第六版 陳鳴譯