1.3 绑定套接字
通过bind()函数绑定套接字,该函数的格式为
int bind(SOCKET s, const struct sockaddr* name, int namelen);
其中,参数s
表示要绑定的套接字;
name
是要绑定的套接字地址;
namelen
是
name
的大小。如果绑定套接字成功,则返回值为
0
,否则返回
SOCKET_ERROR
。
1.3.1 sockaddr_in与sockaddr
在前文中已经提到,bind()函数的第二个参数类型是sockaddr结构的指针,表示套接字的地址。但在实际使用中,使用sockaddr_in结构来代替sockaddr结构。
(1)sockaddr_in结构
sockaddr_in结构体的定义如下所示
struct sockaddr_in{
short sin_family;
unsigned short sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};
其中,sin_family
表示地址族;
sin_port
表示服务的端口号;
sin_addr
表示
IP
地址;
in_zero
是字符数组,包含
8
个元素。
在Create()函数中,使用如下代码对sockaddr_in结构的对象m_server_sockaddr进行配置:
m_server_sockaddr.sin_family = AF_INET;
m_server_sockaddr.sin_port = htons(port);
m_server_sockaddr.sin_addr.S_un.S_addr = INADDR_ANY;
其中,AF_INET
表示IPv4
地址族;变量
port
是Create()
函数的参数,表示服务器监听的端口号,在
sockaddr_in
结构中的端口号必须使用网络字节序的方式进行存储。
htons()
函数的作用是把以主机字节序排列的变量
port
变为以网络字节序排列。
sin_addr
是
in_addr
结构的变量,
in_addr
结构是一个联合(
union
),该联合的名称为
S_un
,
S_addr
是
S_un
的成员,类型为
unsigned long
,
INADDR_ANY
的值是
0x00000000
,表示
IP
地址是任意值。
端口号:一台主机可以提供许多服务,例如Web服务、FTP服务等。可以通过端口号来区分不同的服务。由于该变量的类型是unsigned short,所以端口号的范围是0-65535。对于自定义的服务,其端口号应在2014-49151之间进行选择。
网络字节序和主机字节序:指定了在内存中保存数据的顺序。按照网络字节序保存的数据,内存的低地址保存数据的高字节位,内存的高地址保存数据的低字节位,也叫做“大头”(big-endian)。按照主机字节序保存的数据,内存的低地址保存数据的低字节位,内存的高地址保存数据的高字节位,也叫做“小头”(little-endian)IP地址。对于4个字节的数据0x12345678,按照网络字节序保存,则内存中的值依次为0x12,0x34,0x56,0x78;按照主机字节序保存时,内存中的值依次为0x78,0x56,0x34,0x12。
使用htons()函数可以将主机字节序保存的数据转换为网络字节序,该函数的参数为主机字节序的数据,返回值为网络字节序的数据。
in_addr结构:
成员变量sin_addr表示IP地址,该变量的类型是in_addr结构。in_addr结构的定义如下:
struct in_addr{
union{
struct{
unsigned char s_b1, s_b2, s_b3, s_b4;
}S_un_b;
struct{
unsigned short s_w1, s_w2;
}S_un_w;
unsigned long S_addr;
}S_un;
};
(2)sockaddr_in
与
sockaddr
的关系
sockaddr_in与sockaddr都是用来表示套接字属性。sockaddr结构的定义为
struct sockaddr{
ushort sa_family;
char sa_data[14];
};
从sockaddr
结构的定义可以看出,该结构的大小是
2+14=16
个字节;
sockaddr_in
结构在“1.3.1 sockaddr_in
与
sockaddr
”中提到,如果不算
sin_zero
变量,其大小为
2+2+4=8
个字节,所以要再添加长度为
8
个字节的
sin_zero
作为填充项,使得这两个结构的大小相同。因此,这两个结构在使用时可以互相转换。
1.4 监听套接字
使用listen()函数实现套接字的监听。该函数的格式为
int listen(SOCKET s, int backlog);
其中,参数s
表示要监听的套接字,
backlog
表示等待连接的最大队列长度,如果有
4
个客户端同时向服务器发送请求,那么前
3
个连接会被放在等待处理的队列中,以便服务器依次接受它们的连接,而第
4
个连接将会报错。当服务器接受了某个客户端的连接后,这个连接请求就会从队列中删除。
listen(m_socket_listen, backlog);
其中,m_socket_listen
是在“1.3
绑定套接字”中绑定的套接字,
backlog
是
Create()
函数的参数,用于指定等待连接的最大数量。