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。