目录
getaddrinfo
-
包含头文件:
-
<netdb.h>
函数原型:
-
int getaddrinfo(const char *hostname, const char *service, const struct addrinfo *hints, struct addrinfo **result);
参数说明:
-
hostname
:主机名或者地址串(IPv4的点分十进制串或者IPv6的16进制串) -
service
:服务名可以是十进制的端口号,也可以是已定义的服务名称,如 FTP、HTTP 等 -
hints
:可以是空指针,也可以是一个指向某个addrinfo
结构体的指针,在这个结构中填入关于期望返回的信息的暗示。 -
result
:通过result
指针参数返回一个指向addrinfo
结构体链表的指针。 -
返回值
:0 代表成功,非 0 代表出错。
Details:
-
hostname
:实际上这个参数是用来指定地址的(IPv4 或 IPv6)。如果传入的值是主机名称,比如 localhost,那么会在/etc/hosts
文件中查找 “localhost” 所对应的端口号。因此,可以手动编辑/etc/hosts
文件,添加其它的主机名称并指定地址,然后在这里的hostname
参数中就可以指定主机名称来映射到地址了。 -
service
:类似于hostname
参数,当传入服务名称时会查找/etc/services
文件来确定需要的端口号。 -
hints
通常需要设置的是ai_family
、ai_socktype
、ai_flags
和ai_protocol
四项。ai_family
代表地址结构类型,AF_INET4
和AF_INET6
分别指代 IPv4 和 IPv6,而AF_UNSPEC
则代表不指定。ai_socktype
有SOCK_STREAM
和SOCK_DGRAM
两种,分别对应了 TCP 协议和 UDP 协议。ai_flags
设置为AI_PASSIVE
时,表示返回的结果将用于监听绑定。服务端通常设置为AI_PASSIVE
,客户端通常设置为AI_CANONNAME
。ai_protocol
通常设置为 0,表示允许返回任意类型的协议。
-
result
:一个addrinfo
链表(结构体addrinfo
含有一个名为next
的addrinfo
指针)。该链表包含所有符合参数hostname
和service
要求的地址。bash 命令host
可以检查一个域名所对应的地址,如host tencent.com
将返回域名 “tencent.com” 所对应的地址。由于实际使用getaddrinfo()
函数时,常用 for 循环for (auto p = result; p != nullptr; p = p->ai.next;)
来遍历符合要求的addrinfo
链表,但如果没有任何符合要求的地址时getaddrinfo()
不会对result
做任何更改,所以如果声明result
指针时没有对其初始化为 nullptr,访问p->ai.next
时就会出现segmentation fault
错误。 -
freeaddrinfo(addrinfo **result)
:getaddrinfo
函数返回的 result 所指向的内存地址是由系统开辟的,因此在使用完addrinfo
后要调用freeaddrinfo
释放内存空间,参数为指向addrinfo
链表的首节点的指针。
socket
-
包含头文件
-
<netdb.h>
函数原型
-
int socket(int domain, int sock_type, int protocol);
参数说明
-
三个参数分别用来指定地址类型、连接类型和协议类型。分别对应
addrinfo
结构体中的ai_family
、ai_socktype
和ai_protocol
,因此可用getaddrinfo
函数所返回的result
来指定。 -
返回值
返回一个socket
句柄(或socket
描述符),一个小整数。
Details
-
在使用完
socket
句柄后需要对其进行释放。如bind
、listen
失败后,记得close(sock)
。close
函数定义在<zconf.h>
中。
setsockfd
::setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes))
设置 sock
的选项。上述代码是为了复用地址。对服务端来说,该选项设置后,可以立即重启服务器。(对于普通 TCP 连接,在服务端关闭了 sock
后,该 sock
进入 TIME_WAIT
状态,其生命周期长达十几秒。关闭服务器后,如果立即重启,那么服务端所要绑定的地址仍然存在数个 TIME_WAIT
状态的连接,因而出现“地址已被使用”的错误,需要等待 TIME_WAIT
状态的连接全部被回收后才能重启服务器。设置了地址复用后,如果 bind
时存在的连接只有 TIME_OUT
状态的,那么可以进行重绑定。如果存在其它状态的则仍然会抛出“地址已被使用”的错误。)
bind
-
包含头文件
-
<netdb.h>
函数原型
-
int bind(int sock, const sockaddr *addr, socklen_t len);
参数说明
-
sock
:socket
句柄。 -
addr
:一个指向sockaddr
结构体的指针。一般使用getaddrinfo
所设置的result
的ai_addr
成员。 -
len
:一般使用getaddrinfo
所设置的result
的ai_addrlen
成员。 -
返回值
:0 代表成功,非 0 代表失败。
listen
-
包含头文件
-
<netdb.h>
函数原型
-
int listen(int sock_fd, int n);
参数说明
-
sock_fd
:socket
句柄。 -
n
:消息队列的最大长度。实际值为该参数与系统最大值中较小的那个。在 Linux 下,位于/proc/sys/net/core/somaxcon
中。具体工作模式详见accept
函数。 -
返回值
:0 代表成功,非 0 代表失败。
accept
-
包含头文件
-
int accept(int sock_fd, sockaddr *client, socklen_t *len);
函数原型
-
sock_fd
:socket
句柄。 -
client
:客户端的地址。使用时应先初始化一个sockaddr
结构体,然后传入它的地址。 -
len
:调用时传入初始化的sockaddr
结构体的长度,调用后被设置为能表示客户端地址的最小字节数。如果该值大于传入时设置的大小,那么将缩短为传入时指定的大小。比如初始化一个正常的sockaddr
并传入,但len
参数被初始化为指向整数 0 的指针,那么该函数将写入 0 字节到sockaddr
结构体中,也就是不写。 -
返回值
:返回一个整数,该整数为一个socket
句柄,代表这个连接所对应的socket
。
Details
-
listen
一个socket
套接字后,会在指定的端口开始监听。每有一个连接的请求被收到,都会将其放入消息队列中。调用accept
函数会从消息队列中取出一个进行处理。如果队列为空,则阻塞代码运行。