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()阻塞中喚醒,進入該狀態。



注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com