1. 程式人生 > >TCP協議的連線管理機制------三次握手,四次揮手

TCP協議的連線管理機制------三次握手,四次揮手

有關TCP協議的相關知識見:這篇部落格

        TCP與UDP最大的區別就是TCP保證可靠性資料傳輸。從TCP與UDP的協議報頭就可以看出差別。TCP的協議報頭比UDP報頭多了很多東西,而多出來的這些都是用於保證資料的可靠性傳輸的。下面將具體介紹TCP保證可靠傳輸的機制以及報頭中的欄位是如何用於可靠性傳輸機制的。

        TCP協議保證可靠性的一個重要機制就是連線管理機制。

1. 連線管理機制

下圖為客戶端和伺服器端根據TCP協議:三次握手建立連線,資料通訊,四次揮手釋放連線的示意圖:




  上圖中:

        三次握手:

(1)伺服器程式先執行起來後,當呼叫listen設定監聽套接字之後,進入LISTEN狀態。然後呼叫accept阻塞等待來自客戶端的連線請求;

(2)客戶端再執行,分配檔案描述符之後,呼叫connect向伺服器發起連線請求,阻塞等待伺服器的應答。發出帶有SYN同步標誌位的同步報文段,此時進入SYN_SENT狀態;

(3)伺服器收到客戶端的SYN同步報文段後,將該連線放入核心等待佇列中,並進入SYN_RECV狀態。此時,伺服器向客戶端發出帶有ACK標誌位的確認報文段,確認收到連線請求,並等待客戶端連線建立之後的確認訊號;

(4)客戶端收到伺服器的ACK確認報文段之後,認為伺服器同意建立連線,此時客戶端進人ESTABLISH狀態,並建立連線(將連線描述組織起來),然後向客戶端發出連線已經建立的確認訊號;

(5)伺服器收到確認訊號之後,知道客戶端已經建立連線,此時伺服器也進入ESTABLISH狀態建立連線;並分配新的檔案描述符connfd與客戶端進行資料通訊;

此時,三次握手後,雙方的連線建立成功。開始進行資料通訊:

(6)然後伺服器呼叫read阻塞等待來自客戶端的資料請求;

(7)客戶端呼叫write向伺服器發出資料請求;

(8)伺服器收到資料請求之後,從read返回,向客戶端發出帶有ACK的確認資料報,確認收到請求,然後對客戶端的資料進行處理;

(9)客戶端收到伺服器的確認訊號後,呼叫read阻塞等待伺服器的資料響應訊號;

(10)伺服器處理完客戶端的資料之後,向客戶端發出響應資料報;

(11)客戶端收到伺服器的響應資料報之後,從read返回,並向伺服器發出收到響應的確認訊號;

(12)伺服器收到確認訊號之後,繼續呼叫read阻塞等待來自客戶端的資料請求;此時,迴圈(6)~(11);

四次揮手斷開連線:

(13)某一時刻,客戶端不再發出資料請求,而是呼叫close關閉連線。並向伺服器發出帶有FIN標誌位的結束報文段,此時,客戶端進入FIN_WAIT_1狀態,等待伺服器的確認訊號;

(14)伺服器收到客戶端的FIN結束報文段,知道客戶端要關閉連線了,此時伺服器進入CLOSE_WAIT狀態,進行關閉連線前的一些後序操作,並向客戶端發出帶有ACK的確認報文段,表明他知道客戶端要關閉連線了;

(15)客戶端收到伺服器的確認報文段之後進入FIN_TIME_2狀態,等待伺服器處理完後續操作和伺服器的結束報文段;

(16)伺服器處理完後續操作之後,呼叫close關閉連線,進入LASK_ACK狀態,並向客戶端發出關閉連線的FIN結束報文段,然後等待客戶端的最後一個ACK確認報文段;

(17)客戶端收到伺服器端的結束報文段之後,向伺服器發出ACK確認報文段,此時,進入TIMA_WAIT狀態。

(18)伺服器收到確認報文段之後,進入CLOSED狀態徹底斷開連線。

(19)客戶端在TIME_WAIT之後等待一個2MSL後,進入CLOSED狀態,徹底斷開連線。

2. 為什麼要進行三次握手,而不是兩次握手或更多次握手?

如果是兩次握手,當伺服器收到SYN同步報文段之後認為連線建立,然後會維護該連線,將該連線組織管理起來。此時就會進入ESTABLISHED狀態,併發送ACK報文段給客戶端,當客戶端收到ACK後才會進入ESTABLISHED狀態建立連線。如果ACK在傳輸過程中丟包,此時,客戶端會認為連線沒有建立,而重新向伺服器傳送重複的SYN報文段,伺服器接收後會再次建立該連線。如果ACK一直丟失,客戶端會一直髮送SYN,伺服器就會建立很多相同的無效的連線,從而使伺服器資源浪費,使伺服器受到影響。所以不能進行兩次握手。

        如果進行三次握手,當客戶端收到伺服器的SYN+ACK報文後,會進入ESTABLISHED狀態,並將該連線維護並建立。然後向伺服器傳送ACK報文段,當伺服器接收到ACK之後,才會建立連線並進入ESTABLISHED狀態。如果ACK在傳送給伺服器時丟失,此時,伺服器認為連線沒有建立,所以會重新發送SYN+ACK報文段。此時就變成了客戶端會建立無效連線。但是客戶端認為連線建立成功之後會向伺服器傳送資料請求。當送至伺服器時,伺服器認為連線並沒有建立,所以會向客戶端傳送RST復位報文段要求重新建立連線。此時,客戶端會重新發起連線請求,進而建立連線。在此過程中,雖然客戶端會建立一些無效連線,會受到影響,但是伺服器並沒有受到影響,從而保證了伺服器的安全。同時客戶端雖然受到了影響,但隨後會重新建立連線,所以會減小影響。

        所以,之所以進行三次握手,是因為:

(1)保證伺服器的安全;

(2)雖然客戶端會受影響,但隨後會重建;

(3)三次握手的成功率很高,不需要更多次握手。多次握手還會浪費資源。

3. TIME_WAIT狀態

  當我們啟動伺服器和客戶端之後,用Ctrl+C終止伺服器之後。在立即執行伺服器,發現會出現繫結錯誤的訊息:

TCP協議規定,主動斷開連線的一方會進入TIME_WAIT狀態,等待一個2MSL時間之後才會進入CLOSED狀態。所以,在伺服器主動斷開連線後會進入TIME_WAIT狀態,此時程序雖然終止了,但TCP層的連線還沒有完全斷開,因此不能再次監聽同樣的埠號,所以會出現繫結錯誤的訊息,待2MSL時間之後,才能繫結成功。

        MSL在Centos7上預設的時間是60s。

        TIME_WAIT之後為什麼要等待2MSL才進入CLOSED狀態?

        首先MSL是報文在網路中生存的最大時間。所以TIME_WAIT之後等待2MSL是因為:

(1)在兩個傳輸方向上使尚未被接受或遲到的報文都消失。防止上一個連線的無效報文發給下一個連線;

(2)保證最後一個ACK報文可靠送達。如果最後一個ACK報文花費了MSL時間後丟失,此時,被關閉連線的而一端就會重新發送FIN報文段,可能也會花費MSL時間後才會送到。如果主動斷開連線的一方在TIME_WAIT之後的2MSL時間內又收到了FIN報文,說明最後一個ACK報文丟失,此時可以在傳送一個ACK報文給對端,再次等待2SL時間。如果在2MSL時間內沒有收到FIN報文說明ACK安全送達,此時,直接關閉連線進入CLOSED狀態即可。

4. 當伺服器需要處理大量的客戶端的連線時,而又可能很短客戶端連線不活躍,伺服器就會主動與這些客戶端斷開連線,從而形成大量的TIME_WAIT連線,導致伺服器的埠號不夠用,無法處理新的連線。

        所以使用setsockopt()設定socket描述符的選項SO_REUSEADDR為1。表示允許穿件埠號相同但IP地址不同的socket描述符,使用時在server程式碼中的socket()和bind()之間插入如下程式碼:

int opt = 1;
setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));