TCP的網絡連接建立過程

套接字描述符
套接字描述符是int類型的。套接字描述符是文件描述符的一種,是UNIX系統中內核對各種類型文件的標識。

套接字地址結構體
sockaddr_in結構體
struct sockaddr_in {
     short sin_family; 
/* Address family 一般來說 AF_INET(地址族)PF_INET(協議族 )*/
     unsigned short sin_port; 
/* Port number (必須要採用網絡數據格式,普通數字可以用htons()函數轉換成網絡數據格式的數字) */
     struct in_addr sin_addr; 
/* Internet address 網絡地址 */
     unsigned char sin_zero[8];
/* Same size as struct sockaddr 沒有實際意義,只是爲了跟SOCKADDR結構在內存中對齊 */
};

sockaddr結構體
這個網絡地址結構體在大小上和sockaddr_in一致,但是字段上劃分的比較省略。

struct sockaddr { 
unsigned short sa_family; // 2 bytes address family, AF_xxx 
char sa_data[14]; // 14 bytes of protocol address 
};

該結構體是在操作系統內部處理時使用的, 我們編寫程序時不使用它,而應該使用sockaddr_in結構體。


服務器端的套接字:
在服務器端一般會有兩種套接字。一種是socket函數所創建的服務器連接監聽套接字,另一個是accept函數所創建的客戶端連接通訊套接字。
前者,一直在爲服務器監聽端口的連接請求,即處於LISTEN狀態,當新連接請求到達時(收到客戶端發來的SYN報文)該套接字會複製出一個新套接字,並讓新套接字進入SYN_RECEIVED狀態,新套接字發回SYN-ACK報文給客戶端,當新套接字收到客戶端發來的ACK連接確認時,則進入ESTABLISHED狀態(三次握手完畢),並會喚醒accept的阻塞或者事件到達信號。
後者,就是這個複製來的新套接字,其只爲這一個客戶端的通訊服務。

1、創建服務器端的套接字

server_sockfd = socket(PF_INET, SOCK_STREAM, 0))

參數一,是協議族,PF_NET或AF_NET,但這倆在linux裏其實是同一個宏。

參數二,是套接字的類型,流套接字類型爲SOCK_STREAM、數據報套接字類型爲SOCK_DGRAM、原始套接字SOCK_RAW。

參數三,是通信類型,在Internet通訊域中,此參數一般取值爲0,系統會根據套接字的類型決定應使用的傳輸層協議。

該函數會在操作系統內核中創建一個套接字,並把描述符返回出來,該套接字用於監聽服務器端口的連接請求。在Linux下如果失敗則返回-1。


2、將服務器端套接字綁定到特定端口

bind(server_sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr))

參數一,是要綁定的服務器端套接字的描述符。

參數二,是指向服務器端的網絡地址結構體的指針。

參數三,是地址結構體的大小。

該函數將服務器套接字與服務器端口綁定起來,下一步的監聽就會只監聽該端口。函數成功返回0,失敗返回-1。


3、監聽服務器端的套接字描述符

listen(server_sockfd, 5);  

參數一,是要監聽的套接字描述符。

參數二,是連接請求隊列(未完成連接隊列?)的最大長度。但這個參數並不被用來控制客戶端連接數量。

該函數將剛纔創建好的套接字設定爲被動連接套接字(服務器一般是被動的)。沒有錯誤時會返回0。

對於一個監聽着的套接字,內核會爲其維持着兩個隊列。

1、未完成的連接隊列。

listen_sock結構用於保存SYN_RECV狀態的連接請求塊,所以也叫半連接隊列。

An incomplete connection queue, which contains an entry for each SYN that has arrived from a client for which the server is awaiting completion of the TCP three-way handshake. These sockets are in the SYN_RCVD state (Figure 2.4).

2、已完成的連接隊列。

A completed connection queue, which contains an entry for each client with whom the TCP three-way handshake has completed. These sockets are in theESTABLISHED state (Figure 2.4).

啓動監聽時,做的工作主要包括:
1. 創建半連接隊列,全連接隊列。
2. 初始化sock的一些變量,把它的狀態設爲TCP_LISTEN。
3. 檢查端口是否可用,防止bind()後其它進程修改了端口信息。
4. 把sock連接進入監聽哈希表listening_hash中。


4、等待客戶端的連接請求

client_sockfd = accept(server_sockfd, (struct sockaddr *)&remote_addr, &sin_size)

參數一,正在監聽的服務器端套接字描述符。

參數二,這是函數返回方式的參數,用於存儲接收到的連接的客戶端網絡地址結構體。

參數三。這也是函數返回的數據,用於存儲參數二所指向區域的長度。

從未完成的連接隊列中提取一個連接並處理,如果隊列裏沒有連接請求,函數會阻塞在這個地方。如果失敗則返回-1,如果成功則返回一個新的套接字描述符,表示與該客戶端連接的套接字,之後服務器對客戶端的數據通信(send/recv)都是使用該套接字。


客戶端的套接字:

在客戶端只有一個套接字,先是用於與服務器端建立連接,之後用於與服務器端通信。建立連接時該套接字發送SYN報文給服務器,進入SYN_SENT狀態。當收到服務器的SYN_ACK報文後進入ESTABLISHED狀態,開始正式通信。

1、創建客戶端的套接字(和服務器端的創建方式一致)

2、將客戶端套接字連接到服務器端

connect(client_sockfd, (struct sockaddr *)&remote_addr, sizeof(struct sockaddr))

參數一,客戶端的套接字描述符。

參數二,指向服務器端的網絡地址結構體。

參數三,服務器端網絡地址結構體的大小。

這時客戶端會向服務器端發出請求連接(SYN報文),等待服務器端的accept函數來響應。成功則返回0,失敗返回-1。


TCP連接建立階段的狀態
LISTEN     
服務器端狀態。
等待連接請求。服務器端調用listen()函數即進入該LISTEN狀態。

SYN-SENT     
客戶端狀態。
等待服務器的連接響應。客戶端調用connect()函數即進入該狀態,同時向服務器端發送SYN報文。

SYN-RECEIVED     
服務器端狀態。
等待客戶端的連接確認。服務器端收到了SYN報文,則進入該狀態,併發回SYN-ACK報文。

ESTABLISHED
服務器/客戶端狀態。
客戶端在收到服務器的SYN-ACK報文後從connect()阻塞中喚醒,進入該狀態;服務器端在收到客戶端的連接確認報文後從accept()阻塞中喚醒,進入該狀態。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章