首页 > 其他分享 >getaddrinfo(), freeaddrinfo(), gai_strerror()的man手册

getaddrinfo(), freeaddrinfo(), gai_strerror()的man手册

时间:2022-10-29 23:04:07浏览次数:44  
标签:getaddrinfo struct ai strerror int freeaddrinfo addrinfo hints

  • 英文小册原文地址:​​beej.us/guide/bgnet…​​
  • 作者:Beej
  • 中文翻译地址:​​www.chanmufeng.com/posts/netwo…​​

getaddrinfo(), freeaddrinfo(), gai_strerror()

获取有关host name(主机名)以及service(服务)信息,并将结果保存在​​struct sockaddr​​结构中。

函数原型

#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>

int getaddrinfo(const char *nodename, const char *servname,
const struct addrinfo *hints, struct addrinfo **res);

void freeaddrinfo(struct addrinfo *ai);

const char *gai_strerror(int ecode);

struct addrinfo {
int ai_flags; // AI_PASSIVE, AI_CANONNAME, ...
int ai_family; // AF_xxx
int ai_socktype; // SOCK_xxx
int ai_protocol; // 0 (auto) or IPPROTO_TCP, IPPROTO_UDP

socklen_t ai_addrlen; // length of ai_addr
char *ai_canonname; // canonical name for nodename
struct sockaddr *ai_addr; // binary address
struct addrinfo *ai_next; // next structure in linked list
};

说明

​getaddrinfo()​​​是一个非常有用的函数,它会返回特定主机名的相关信息(例如其IP地址),并把信息保存在一个​​struct sockaddr​​结构中,自动为你处理细节(比如地址是IPv4还是IPv6)。

有了这个函数,原本的 ​​gethostbyname()​​​ 和 ​​getservbyname()​​也就可就可以退出江湖了。

接下来的描述可能会让你望而生畏,但是别担心,这个函数用起来贼简单。你可以先跳过下面的描述直接看一下例子,增强一下你的信心。

​nodename​​​参数中保存的就是host name(主机名)。主机名可以是域名,比如“​​www.chanmufeng.com​​”,也可以是IPv4或者IPv6地址(以字符串形式传递)。如果你用了​​AI_PASSIVE​​​选项,这个参数你也可以设置为​​NULL​​(见下文)。

​servname​​​通常就是端口号,比如你可以以字符串格式传递“​​80​​​”;也可以是服务名,比如“​​http​​​”、“​​tftp​​​”、或者“​​smtp​​​”、“​​pop​​​”等。众所周知的服务名可以在 ​​IANA Port List​​​​48​​ 中找到,你本地的​​/etc/services​​文件中也有这些信息。

然后就是核心参数——​​hints​​​。在使用​​addrinfo​​​之前,你必须先用​​memset()​​​将整个结构数据清空。接下来我们讲讲​​addrinfo​​中的字段。

​ai_flags​​​有很多个候选项,但是重要也就几个。(如果要同时使用多个后选项,可以使用​​|​​运算符对他们进行按位或运算)。查看man手册获取完整的标识列表。

​AI_CANONNAME​​​ 会令​​res​​​的 ​​ai_canonname​​ 填充为主机的canonical(real) name(规范名,或者成为真名)。

​AI_PASSIVE​​​ 会令​​res​​​的IP地址被设置为 ​​INADDR_ANY​​​ (IPv4) 或 ​​in6addr_any​​​ (IPv6);这让之后在调用​​bind()​​​时,可以自动使用当前主机的IP地址来填充​​struct sockaddr​​的IP地址。这在你写Server代码且不想写死IP地址时非常好用。

如果你使用了 ​​AI_PASSIVE​​​标识,你就可以将​​nodename​​​字段设置为​​NULL​​​了(因为​​bind()​​在之后会自动给你填上)。

接着聊参数。

你应该会想将 ​​ai_family​​​ 设置为​​AF_UNSPEC​​​,这样 ​​getaddrinfo()​​就能同时应付IPv4和IPv6了。当然喽,你也可以用 AF_INET 或 AF_INET6 来自己指定使用IPv4还是IPv6.

另外,socktype应该被设置为 ​​SOCK_STREAM​​​ 或 ​​SOCK_DGRAM​​,具体取决于你想使用那种socket。

最后,你可以将 ​​ai_protocol​​​ 设置为​​0​​,让其自动选择你的protocol type。

做了这么多,终于可以调用​​getaddrinfo()​​了。

事情变得有趣了。​​res​​​会指向​​struct addrinfo​​​的一个链接列表,您可以通过这个链表来获得全部的地址信息(符合你通过​​hints​​指定的address类型)。

但是你可能会获取到一些因为某些原因而无效的address,因此Linux的man手册提供的方法是循环读取链表,然后进行调用​​socket()​​​、​​connect()​​​(如果Server端程序使用了​​AI_PASSIVE​​​,那就是​​bind()​​),直到成功为止。

最后,当你处理完链表之后,你需要调用​​freeaddrinfo()​​来释放内存,避免内存出现泄漏。

返回值

成功返回​​0​​​,异常返回​​非0​​​。如果返回的是​​非0​​​,你可以调用​​gai_strerror()​​获取一个文字版本的错误信息。

例子

// code for a client connecting to a server
// namely a stream socket to www.example.com on port 80 (http)
// either IPv4 or IPv6

int sockfd;
struct addrinfo hints, *servinfo, *p;
int rv;

memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC; // use AF_INET6 to force IPv6
hints.ai_socktype = SOCK_STREAM;

if ((rv = getaddrinfo("www.example.com", "http", &hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
exit(1);
}

// loop through all the results and connect to the first we can
for(p = servinfo; p != NULL; p = p->ai_next) {
if ((sockfd = socket(p->ai_family, p->ai_socktype,
p->ai_protocol)) == -1) {
perror("socket");
continue;
}

if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
perror("connect");
close(sockfd);
continue;
}

break; // if we get here, we must have connected successfully
}

if (p == NULL) {
// looped off the end of the list with no connection
fprintf(stderr, "failed to connect\n");
exit(2);
}

freeaddrinfo(servinfo); // all done with this structure
// code for a server waiting for connections
// namely a stream socket on port 3490, on this host's IP
// either IPv4 or IPv6.

int sockfd;
struct addrinfo hints, *servinfo, *p;
int rv;

memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC; // use AF_INET6 to force IPv6
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE; // use my IP address

if ((rv = getaddrinfo(NULL, "3490", &hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
exit(1);
}

// loop through all the results and bind to the first we can
for(p = servinfo; p != NULL; p = p->ai_next) {
if ((sockfd = socket(p->ai_family, p->ai_socktype,
p->ai_protocol)) == -1) {
perror("socket");
continue;
}

if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
close(sockfd);
perror("bind");
continue;
}

break; // if we get here, we must have connected successfully
}

if (p == NULL) {
// looped off the end of the list with no successful bind
fprintf(stderr, "failed to bind socket\n");
exit(2);
}

freeaddrinfo(servinfo); // all done with this structure

参阅

​​gethostbyname()​​, ​​getnameinfo()​​

标签:getaddrinfo,struct,ai,strerror,int,freeaddrinfo,addrinfo,hints
From: https://blog.51cto.com/u_13887950/5806583

相关文章

  • getaddrInfo
    /*HostandservicenamelookupsusingNameServiceSwitchmodules.Copyright(C)1996-2022FreeSoftwareFoundation,Inc.ThisfileispartoftheGNUCL......
  • Error: getaddrinfo ENOTFOUND
    Error:getaddrinfoENOTFOUND的相关解决办法 1https://www.cnblogs.com/eyunhua/p/7993433.html 1https://codeday.me/bug/2019050......
  • 简单测试C语言<string.h>中strerror(int errornum)能输出什么
    使用一个简单程序来验证一下:#include<stdio.h>#include<string.h>intmain(intargc,char*argv[]){for(inti=-5;i<50;i++)printf("errno[%2......
  • 【C标准库】详解strerror函数
    创作不易,感谢支持strerror头文件:string.h描述:strerror()函数接受一个参数:errnum,它是一个表示错误代码的整数值。此函数将错误代码转换为说明错误的合适字符串指针并返......