結合socket詳解TCP三次握手四次揮手

2021-08-14 09:38:03 字數 4284 閱讀 3767

tcp協議中的三次握手和四次揮手大家應該都至少聽說過了,本人一直覺得理論學習要結合**才能學習的更深刻,當知道東西是這樣,然後再知道為什麼是這樣的時候,領悟往往更加深刻,今天本人就結合socket程式設計中的api來解析一下tcp協議的三次握手和四次揮手過程。

那麼tcp協議中的三次握手和四次揮手實際在網路程式設計中是怎麼對應的呢?先貼乙個簡單的echo伺服器端和客戶端的**:

server: 

#include 

#include

#include

#include

#include

#include

#include

#include

#include

#define default_port 8000

using

namespace

std;

const

int buffer_size = 2048;

int main()

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

servaddr.sin_family = af_inet;

servaddr.sin_addr.s_addr = htonl(inaddr_any);

servaddr.sin_port = htons(default_port);

if(bind(listenfd,(sockaddr*)&servaddr,sizeof(servaddr)) == -1)

if(listen(listenfd,5) == -1)

while(true)

if(send(connfd,"connect successful",19,0) == -1)

}

close(connfd);

}

close(listenfd);

return

0;}

clinet

#include

#include

#include

#include

#include

#include

#include

#include

#include

using

namespace

std;

int main(int argc,char** argv)

if((sockfd = socket(af_inet,sock_stream,0)) < 0)

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

servaddr.sin_family = af_inet;

servaddr.sin_port = htons(8000);

if(inet_pton(af_inet,argv[1],&servaddr.sin_addr) <= 0)

if(connect(sockfd,(sockaddr*)&servaddr,sizeof(servaddr)) < 0)

while(true)

if(send(sockfd,send_buffer,strlen(send_buffer),0) < 0)

if((recv_len = recv(sockfd,rec_buffer,2048,0)) < 0)

cout

<< rec_buffer

0;}

功能很簡單,就是客戶端傳送字串,然後伺服器端收到列印出來,結合這個簡單的模型來看看通訊過程中的三次握手和四次揮手

client端主要做了以下幾件事:

socket() ->connect() -> send() ->recv() ->close()

sever端主要做了以下幾件事:

首先,socket(),bind()這兩個函式並不算實際參與到了三次握手和四次揮手的過程中,它們算是通訊開始前的預備動作,socket()負責產生乙個套接字的描述符,bind()負責將乙個本地協議位址賦予乙個套接字,同時也將通訊埠進行繫結,如果bind繫結的是inaddr_any,即表示所有傳送到伺服器的這個埠,不管是哪個網絡卡/哪個ip位址接收到的資料,都由這個服務端程序進行處理。一般情況下,如果你要建立網路伺服器應用程式,則你要通知伺服器作業系統:請在某位址 ***.***.***.***上的某埠 yyyy上進行偵聽,並且把偵聽到的資料報傳送給我。這個過程,你是通過bind()系統呼叫完成的。——也就是說,你的程式要繫結伺服器的某位址,或者說:把伺服器的某位址上的某埠佔為已用。伺服器作業系統可以給你這個指定的位址,也可以不給你。如果你的伺服器有多個網絡卡(每個網絡卡上有不同的ip位址),而你的服務(不管是在udp埠上偵聽,還是在tcp埠上偵聽),出於某種原因:可能是你的伺服器作業系統可能隨時增減ip位址,也有可能是為了省去確定伺服器上有什麼網路埠(網絡卡)的麻煩 —— 可以要在呼叫bind()的時候,告訴作業系統:「我需要在 yyyy 埠上偵聽,所有傳送到伺服器的這個埠,不管是哪個網絡卡/哪個ip位址接收到的資料,都是我處理的。」這時候,伺服器程式則在0.0.0.0這個位址上進行偵聽。

listen()函式則完成主要的三次握手的過程,listen負責將主動連線套接字變為被動連線套接字(當乙個socket()建立套接字後,它被假設為乙個主動套接字),然後指定核心為相應套接字排隊的最大連線個數。然後維護乙個半連線佇列和乙個全連線佇列,什麼是半連線佇列呢?就是只進行了兩次握手,還未收到client確認ack的連線,全連線佇列就是3次握手成功的連線的佇列,這個連線的總長度是由listen(int sockfd,int backlog)中的backlog來決定,但是這個backlog不好確定,berkeley的實現為這個backlog增設了乙個模糊因子(將其乘以1.5得到佇列最大長度),現在的http的伺服器一般都將backlog設定乙個大值來保證連線都可以得到處理,在《unix網路程式設計》中給出了乙個利用環境變數listenq來設定backlog值得做法,方式如下

然後就是accept(),accept()也不算參與3次握手的過程中了,accept()的任務就是從listen()的全連線佇列裡取出第乙個就緒的連線,然後返回乙個新的connection fd。

如果accept成功,那麼其返回值是由核心自動生成的乙個全新描述符,代表與所返回client的tcp連線,要注意區分監聽套接字(listenfd)和已連線套接字(connfd),乙個伺服器通常僅建立乙個listenfd,它在伺服器的生命期內一直存在,核心為每個連線成功的client建立乙個connfd,當server完成對某個client的服務時,就會關閉這個connfd。

為什麼要3次握手而不是兩次呢?在謝希仁的《計算機網路》中是這樣說的:為了防止已失效的連線請求報文段突然又傳送到了服務端,因而產生錯誤。比如:

「已失效的連線請求報文段」的產生在這樣一種情況下:client發出的第乙個連線請求報文段並沒有丟失,而是在某個網路結點長時間的滯留了,以致延誤到連線釋放以後的某個時間才到達server。本來這是乙個早已失效的報文段。但server收到此失效的連線請求報文段後,就誤認為是client再次發出的乙個新的連線請求。於是就向client發出確認報文段,同意建立連線。假設不採用「三次握手」,那麼只要server發出確認,新的連線就建立了。由於現在client並沒有發出建立連線的請求,因此不會理睬server的確認,也不會向server傳送資料。但server卻以為新的運輸連線已經建立,並一直等待client發來資料。這樣,server的很多資源就白白浪費掉了。採用「三次握手」的辦法可以防止上述現象發生。例如剛才那種情況,client不會向server的確認發出確認。server由於收不到確認,就知道client並沒有要求建立連線。」

這就很明白了,防止了伺服器端的一直等待而浪費資源。

參與到四次揮手過程主要就是close()函式了:

那為什麼要4次揮手呢?tcp協議是一種面向連線的、可靠的、基於位元組流的運輸層通訊協議。tcp是全雙工模式,這就意味著,當主機1發出fin報文段時,只是表示主機1已經沒有資料要傳送了,主機1告訴主機2,它的資料已經全部傳送完畢了;但是,這個時候主機1還是可以接受來自主機2的資料;當主機2返回ack報文段時,表示它已經知道主機1沒有資料傳送了,但是主機2還是可以傳送資料到主機1的;當主機2也傳送了fin報文段時,這個時候就表示主機2也沒有資料要傳送了,就會告訴主機1,我也沒有資料要傳送了,之後彼此就會愉快的中斷這次tcp連線。

TCP三次握手之Socket

原文 include int listen int sockfd,int backlog 本函式的第二個引數規定了核心應該為相應套介面排隊的最大連線個數,一般為以下兩個佇列的大小之和,即未完成三次握手佇列 已經完成三次握手佇列 為了更好的理解backlog引數,我們必須認識到核心為任何乙個給定的監聽...

tcp三次握手 TCP 三次握手總結

tcp特點概述 tcp segment structure 段結構 step2 server host receives syn,replie with syn ack segment 答覆syn ack報文段 step3 client receives synack,replies with ac...

TCP 三次握手

tcp 三次握手 tcp 連線是通過三次握手進行初始化的。三次握手的目的是同步連線雙方的序列號和確認號並交換 tcp 視窗大小資訊。以下步驟概述了通常情況下客戶端計算機聯絡伺服器計算機的過程 1.客戶端向伺服器傳送乙個syn置位的tcp報文,其中包含連線的初始序列號x和乙個視窗大小 表示客戶端上用來...