1. gethostbyname
函数:struct hostent *gethostbyname(const char *name)
头文件 #include <netdb.h>
返回hostent结构体类型指针:
struct hostent {
char *h_name; /* official name of host */
char **h_aliases; /* alias list */
int h_addrtype; /* host address type */
int h_length; /* length of address */
char **h_addr_list; /* list of addresses */
}
这个函数的局限是只能返回IPv4地址,并且不允许调用者指定所需地址类型的任何信息,返回的结构只包含了用于存储 IPv4 地址的空间。POSIX规范预警可能会在将来某个版本中撤销gethostbyname函数。
从POSIX规范中撤销该函数意在声明新的程序不该在使用它,鼓励在新的程序中改用getaddrinfo函数。
函数原型:
int gethostbyname_r(const char *name,
struct hostent *ret,
char *buf,
size_t buflen,
struct hostent **result,
int *h_errnop);
gethostbyname_r()函数是gethostbyname()函数的可重入版本,两个函数的功能是一样的。
2. gethostbyname2
函数:struct hostent *gethostbyname2(const char *name, int af);
它的工作原理类似于gethostbyname(),但是允许指定地址必须属于的地址族,当af参数为AF_INET时,gethostbyname2和gethostbyname一样,即查找并返回IPv4地址。当af参数为AF_INET6时,gethostbyname2只查找AAAA记录并且返回IPv6地址。
头文件:同上。
返回hostent结构体类型指针:同上。
函数原型:
int gethostbyname2_r(const char *name,
int af,
struct hostent *ret,
char *buf,
size_t buflen,
struct hostent **result,
int *h_errnop);
gethostbyname2_r()函数是gethostbyname2()函数的可重入版本,两个函数的功能是一样的。
3. getaddrinfo
函数将主机名、主机地址、服务名和端口的字符串表示转换成套接字地址结构体。它是已弃用的getgostbyname和getservbyname函数的新的替代品。和以前的那些函数不同,这个函数是可以重入的,适合于任何协议,函数把协议相关性完全隐藏在这个库函数内部。应用程序只要处理由getaddrinfo函数填写的套接口地址结构。
函数原型:
int getaddrinfo( const char *node,
const char *service,
const struct addrinfo *hints,
struct addrinfo **res);
1)nodename:节点名可以是主机名,也可以是数字地址。(IPV4的10进点分,或是IPV6的16进制);
2)servname:包含十进制数的端口号或服务名如(ftp,http);
3)hints:是一个空指针或指向一个addrinfo结构的指针,由调用者填写关于它所想返回的信息类型的线索;
4)res:存放返回addrinfo结构链表的指针;
头文件:
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
void freeaddrinfo(struct addrinfo *res);
const char *gai_strerror(int errcode);
struct addrinfo {
int ai_flags;
int ai_family;
int ai_socktype;
int ai_protocol;
socklen_t ai_addrlen;
struct sockaddr *ai_addr;
char *ai_canonname;
struct addrinfo *ai_next;
}
参数说明:
在getaddrinfo函数之前通常需要对以下6个参数进行以下设置:nodename、servname、hints的ai_flags、ai_family、ai_socktype、ai_protocol。在6项参数中,对函数影响最大的是nodename,sername和hints.ai_flag。而ai_family只是有地址为v4地址或v6地址的区别。而ai_protocol一般是为0不作改动。
其中ai_flags、ai_family、ai_socktype说明如下:
参数 | 取值 | 值 | 说明 |
ai_family | AF_INET | 2 | IPv4 |
AF_INET6 | 23 | IPV6 | |
AF_UNSPEC | 0 | 协议无关 | |
—- | —- | — | —- |
ai_protocol | IPPROTO_IP | 0 | IP协议 |
IPPROTO_IPv4 | 4 | IPv4 | |
IPPROTO_IPv6 | 41 | IPv6 | |
IPPROTO_UDP | 17 | UDP | |
IPPROTO_TCP | 6 | TCP | |
—- | —- | — | — |
ai_socktype | SOCK_STREAM | 1 | 流 |
SOCK_DGRAM | 2 | 数据报 | |
—- | —- | — | — |
ai_flags | AI_PASSIVE | 1 | 被动的,用于bind, |
AI_CANONNAME | 2 | ||
AI_NUMERICHOST | 4 | 地址为数字串 |
对于ai_flags值的说明:
AI_PASSIVE | AI_CANONNAME | AI_NUMERICHOST |
0/1 | 0/1 | 0/1 |
如上表所示,ai_flagsde值范围为0~7,取决于程序如何设置3个标志位,比如设置ai_flags为 “AI_PASSIVE|AI_CANONNAME”,ai_flags值就为3。
三个参数的含义分别为:
(1)AI_PASSIVE 当此标志置位时,表示调用者将在bind()函数调用中使用返回的地址结构。当此标志不置位时,表示将在connect()函数调用中使用。当节点名为NULL,且此标志置位,则返回的地址将是通配地址。如果节点名为NULL,且此标志不置位,则返回的地址将是回环地址。
(2)AI_CANNONAME当此标志置位时,在函数所返回的第一个addrinfo结构中的ai_cannoname成员中,应该包含一个以空字符结尾的字符串,字符串的内容是节点名的正规名。
(3)AI_NUMERICHOST当此标志置位时,此标志表示调用中的节点名必须是一个数字地址字符串。
给定host和service(套接字地址的两个组成部分),getaddrinfo返回result, result是一个指向addrinfo结构的链表,其中每个结构体指向一个对应于host和service的套接字地址结构。
在客户端调用了getaddrinfo之后,会遍历这个列表,一次尝试每一个套接字地址,直到调用socket和connect成功,建立起连接。类似地,服务器会尝试遍历列表中的每一个套接字的地址,直到调用socket和bind成功,描述符会被绑定到一个合法的套接字地址。为了避免内存泄漏,应用程序必须调用freeaddrinfo,释放该链表。如果getaddrinfo返回非零的错误代码,应用程序可以调用gai_streeror,将代码转成消息串。
getaddrinfo的host参数可以是域名,也可以是数字地址(如点分十进制IP地址)。service参数可以是服务名(如http),也可以是十进制端口号。如果不想把主机名转换成地址,可以把host设置为NULL。对service来说也是一样。但是必须指定两者中至少一个。
getaddrinfo一个很好的方面是addrinfo结构中的字段是不透明的,即它们可以直接传递给套接字接口中的函数,应程序代码无需再做任何处理。例如,ai_family、ai_socktype和ai_protocol可以直接传递给socket。类似的,ai_addr和ai_addrlen可以直接传递给connect和bind。这个强大的属性使得我们编写客户端和服务器能够独立于某一个特殊版本的IP协议。
4.关于上面几个函数线程安全如下表所示:
gethostbyname处理数据时返回的结果是存储在一个静态结构体变量中的,作为本地的一个全局变量,只初始化一次,也就是说,下次对这个变量进行操作时,是基于这次的结果来操作的。
下面小例子说明这个问题:
ht1 = gethostbyname(“www.sohu.com”);
ht2 = gethostbyname(“www.baidu.com”);
printf("ip:%s\n",inet_ntop(ht1->h_addrtype,ht1->h_addr_list[i],str,30));
printf(":%s\n",ht1->h_aliases[i]);
printf("IP:%s\n",inet_ntop(ht2->h_addrtype,ht2->h_addr_list[i],str,30));
printf("alias:%s\n",ht2->h_aliases[i]);
ht1和ht2这个结构体指针在运行gethostbyname这个函数后指向的是同一个地址(即上面gethostbyname存储返回结果的变量地址)(大家可以在编译器里分部执行查看变量值的变化)
5.小结
应用程序用来把主机名转换成IP地址,gethostbyname是曾经常用的入口。随着向IPv6和线程化编程模型的转移,getaddrinfo显得更有用,因为它既解析IPv6地址,又符合线程安全。