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結構在內存中對齊 */
};
struct sockaddr {
unsigned short sa_family; // 2 bytes address family, AF_xxx
char sa_data[14]; // 14 bytes of protocol address
};
該結構體是在操作系統內部處理時使用的, 我們編寫程序時不使用它,而應該使用sockaddr_in結構體。
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。