拜託了,看完這篇別再問我什麼是TCP三次握手和四次揮手

TCP三次握手和四次揮手的問題在面試中是最為常見的考點之一。很多讀者都知道三次和四次,但是如果問深入一點,他們往往都無法作出準確回答。

三次握手如何建立連接?


拜託了,看完這篇別再問我什麼是TCP三次握手和四次揮手

三次握手建立鏈接

從圖中可以清楚的看到,三次握手的過程,我 在在把過程清楚的解釋一遍,順便說下每個過程容易被問到的知識點。

採用C/S模式解釋,假設C端發起傳輸請求。

在發送建立鏈接請求之前,C端是保持CLOSED狀態,S端最開始也是處於CLOSED狀態,當執行listen函數套接字進入被動監聽狀態

所謂被動監聽,是指當沒有客戶端請求時,套接字處於“睡眠”狀態,只有當接收到客戶端請求時,套接字才會被“喚醒”來響應請求。

第一次:C端發送SYN=1的請求報文,此時C端進入SYN SENT狀態,等待服務器確認。

此時如果報文丟失發送不到對端會如何?

C端發送報文之後會啟動一個定時器,在超時之後未收到S端的確認,會再次發送SYN請求,每次嘗試的時間會是第一次的二倍,如果總的總嘗試時間為75秒,此次建立鏈接失敗。

第二次:S端收到C端發送的SYN報文(建立鏈接請求)後,S端必須返回確認號並且同時發送一條SYN報文,此時進入SYN RCVD狀態。

為啥要連帶發送SYN報文?

TCP是全雙工通信,協議規定當收到建立鏈接請求後必須返回序列號,同時建立本端到對端的通信鏈接。這也叫做捎帶應答機制。

如果第二次報文丟失怎麼辦?

在發送完ACK+SYN報文後會啟動一個定時器,超時沒有收到ACK確認,會再次發送,會進行多次重試。超時時間依舊每次翻倍,重試次數可設置。

修改 /proc/sys/net/ipv4/tcp_synack_retries 的值

第三次:C端收到S端發的ACK+SYN報文,需要返回一個應答ACK的報文,此時該連接會進入半連接狀態的隊列,當S端收到ACK後,一條完整的全雙工TCP鏈接建立完成,雙方進入ESTABLISHED狀態。

這裡有個常用攻擊手段,攻擊者偽造一個SYN請求發送給服務端,服務端響應之後,會收不到C端的ACK確認,服務端會不斷的重試,默認會重試五次。

此時服務端會維持這個鏈接的所有資源,如果有大量這樣的請求,服務端的資源會被耗完。

這就是DOS攻擊。

如果第三次報文丟失怎麼辦?

S端在發出ACK+SYN報文後會啟動一個定時器,在超時觸發還沒收到ACK就確認是丟失了,會重試一次發送。

這裡面的每個狀態都必須搞明白,面試官也超級愛問上面的狀態轉移。

龍叔還遇到過一個面試官問我用過socket編程麼?問我用過哪些socket函數?

C端socket編程代碼

//C端

#define PORT 8080

#define BUFFER_SIZE 1024

int main(int argc, char **argv){

//定義IPV4的TCP連接的套接字描述符

int sock_cli = socket(AF_INET,SOCK_STREAM, 0);

//定義sockaddr_in

struct sockaddr_in servaddr;

memset(&servaddr, 0, sizeof(servaddr));

servaddr.sin_family = AF_INET;

servaddr.sin_addr.s_addr = inet_addr(argv[1]);

servaddr.sin_port = htons(PORT);

//連接服務器,成功返回0,錯誤返回-1

int ret = connect(sock_cli, (struct sockaddr *)&servaddr, sizeof(servaddr));

//客戶端將控制檯輸入的信息發送給服務器端,服務器原樣返回信息,阻塞

while (fgets(sendbuf, sizeof(sendbuf), stdin) != NULL)

{

ret=send(sock_cli, sendbuf, strlen(sendbuf),0); ///發送

recv(sock_cli, recvbuf, sizeof(recvbuf),0); ///接收

fputs(recvbuf, stdout);

}

close(sock_cli); // 關閉連接

return 0;

}

複製代碼

S端socket編程代碼

int main(int argc, char **argv){

//定義IPV4的TCP連接的套接字描述符

int server_sockfd = socket(AF_INET,SOCK_STREAM, 0);

//定義sockaddr_in

struct sockaddr_in server_sockaddr;

server_sockaddr.sin_family = AF_INET;

server_sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);

server_sockaddr.sin_port = htons(PORT);

//bind成功返回0,出錯返回-1

if(bind(server_sockfd,(struct sockaddr *)&server_sockaddr,sizeof(server_sockaddr))==-1)

//listen成功返回0,出錯返回-1,允許同時監聽的連接數為QUEUE_SIZE

if(listen(server_sockfd,QUEUE_SIZE) == -1)

for(;;)

{

struct sockaddr_in client_addr;

socklen_t length = sizeof(client_addr);

//進程阻塞在accept上,成功返回非負描述字,出錯返回-1

int conn = accept(server_sockfd, (struct sockaddr*)&client_addr,&length);

//處理數據部分

...

}

close(server_sockfd);

return 0;

}

複製代碼

為什麼需要三次握手建立鏈接,2次可以麼,4次行不行?

這問題問的,面試官是咋了?在這明知故問的,整些有的沒的。肯定是不行啊,RFC 標準就是這樣寫的啊。

可不敢這樣回答啊,標準是說的三次握手建立鏈接,可沒說四次不行啊。要是這樣答,妥妥的會收到,同學我們今天的面試到此基本結束了,你回家等消息...

龍叔來說說這個問題,為什麼不能兩次?

如果第二次不發送SYN+ACK,只是發送確認應答消息ACK,會造成只能建立單向通信,而且不能應答。而TCP是全雙工通信的,而且必須保證可靠性。

如果第二發送SYN+ACK,不用應答。此時會出現三種情況

一、二次握手失敗,C端會重複發送SYN報文,等待對端發送確認報文,S端會保存tcp連接的所有資源,大量的這種情況會導致S資源耗盡。

二、二次握手成功,S收不到ACK會重複發送SYN+ACK報文。

三、二次握手完以後,雙方以為連接建立成功,即可開始通信。假如此時連接並沒有真的建立成功,S端開始發送消息,會造成網絡擁堵發生。

為什麼不能是四次?

四次其實原則上來說是可以的,就是把第二次的ACK和SYN分兩次發送。在理論上是完全可以行得通的,但是TCP本著節約網絡網絡資源的前提。

還有一種是不拆開二次握手的捎帶應答,三次握手之後C端繼續發送SYN報文,其時這是徒勞的。第三次完成以後鏈接已經建立,後面無論多少次都是徒勞。

如果雙方同時建立連接,會發生什麼情況?

拜託了,看完這篇別再問我什麼是TCP三次握手和四次揮手

TCP同時建立鏈接

這就是雙方同時建立鏈接的情況,情況還不錯,反正能建立成功,這點是肯定的。但是要注意兩點

第一、此時只會建立一條全雙工的TCP鏈接,不是兩條。

第二、雙方沒有CS之分,兩端都是同時承擔兩個角色,客戶端和服務器。

四次揮手斷開鏈接

先整個圖看下四次揮手的整個過程和狀態轉移。狀態轉移會考看仔細點。

拜託了,看完這篇別再問我什麼是TCP三次握手和四次揮手

依舊採用C/S模式解釋此過程。

第一次:當C端的應用程序結束數據傳輸是,會向S端發送一個帶有FIN附加標記的報文段(FIN表示英文finish),此時C端進入FIN_WAIT1狀態,C端不能在發送數據到S端。

第二次:S端收到FIN報文會響應一個ACK報文,S端進入CLOSE_WAIT狀態。進入此狀態後S端把剩餘未發送的數據發送到C端,C端收到S端的ACK之後,進入FIN_WAIT2狀態。

同時繼續接受S端傳輸的其他數據包。

第三次:S端處理完自己待發送的數據之後,也會發送FIN斷開鏈接的請求,S端進入LAST_ACK狀態。

第四次:C端收到S端的斷開鏈接請求後會啟動一個定時器,該定時器時長是2MSL(最大段報文生存時間),同時發送最後一次ACK報文。

為什麼要四次揮手?

TCP是全雙工的通信機制,每個方向必須單獨進行關閉。

TCP傳輸連接關閉的原則如下:

當一端完成它的數據發送任務後就可以發送一個FIN字段置1的數據段來終止這個方向的數據發送;當另一端收到這個FIN數據段後,必須通知它的應用層 對端已經終止了那個方向的數據傳送。

為什麼不能用三次握手中捎帶應答機制減少一次握手?

這點到是很迷惑人,但是掌握了TCP傳輸的一些細節就會發現並不難。

TCP是全雙工通信的,S收到斷開鏈接請求後只是表示C端不會傳輸數據到S端了,但是並不表示S端不傳輸數據到C端。

如果採用捎帶應答,S端將無法把剩餘的數據傳輸到C端。

為何最後一次ACK之後需要等待2MSL的時間?

網絡是不可靠的,TCP是可靠協議,必須保證最後一次報文送達之後才能斷開鏈接,否則會再次收到S端的FIN報文信息。

而等待2MSL時間就是為了保證最後最後一次報文丟失時還能重新發送。

為何是2MSL的時間?

2MSL是報文一個往返的最長時間,假設小於這個時間會發生,ACK丟了,但是還沒接收到對方重傳的FIN我方就重新發送了ACK。

如果已經建立了連接,但是客戶端突然出現故障了怎麼辦?

這個不難TCP自己做了保證,TCP默認有個定時器,每次收到客戶端的請求後會把定時器設置好,通常設置兩小時,超過兩小時還沒收到數據。

服務端會發送一個探測報文,以後每隔75秒鐘發送一次。若一連發送10個探測報文仍然沒反應,服務器就認為客戶端出了故障,接著就關閉連接。


分享到:


相關文章: