IPv4中使用gethostbyname()函数完成主机名到地址解析,这个函数仅仅支持IPv4,且不允许调用者指定所需地址类型的任何信息,返回的结构只包含了用于存储IPv4地址的空间。
IPv6中引入了getaddrinfo()的新API,它是协议无关的,既可用于IPv4也可用于IPv6。getaddrinfo函数能够处理名字到地址以及服务到端口这两种转换,返回的是一个addrinfo的结构(列表)指针而不是一个地址清单。
1. getaddrinfo函数原型:
参数解析:
struct addrinfo{
int ai_flags; /*AI_PASSIVE,AI_CANONNAME*/
int ai_family; /*PF_xxx*/
int ai_socktype; /*SOCK_xxx*/
int ai_protocol; /*0 ir IPPROTO_xxx for IPV4 and IPV6*/
size_t ai_addrlen; /*length of ai_addr*/
struct sockaddr *ai_addr; /*binary address*/
char *ai_canonname; /*canonical name for hostname*/
struct addrinfo *ai_next; /*next structure in linked list*/
};
hints结构中调用者可以设置的成员有:
ai_flags //0个或多个或在一起的AI_xxx值
ai_family //AF_xxx
ai_socktype//SOCK_xxx
ai_protocol
详细如下图:
函数使用:
#include<stdio.h>
#include<netdb.h>
#include<stdlib.h>
#include<arpa/inet.h>
#include<string.h>
int main(int argc, char * argv[])
{
struct addrinfo *ai;
struct addrinfo *curr;
struct addrinfo hint;
char ipstr[16] = {0};
bzero(&hint, sizeof(hint)); //填充暗示信息
hint.ai_family = AF_INET;
hint.ai_socktype = SOCK_STREAM;
hint.ai_flags = AI_CANONNAME;//告知返回主机规范名,若不告知,则不返回。
if (getaddrinfo("sw2", NULL, &hint, &ai) == 0) //本系统主机名为 sw2
{
printf("ai->ai_family:%s \n",ai->ai_family == 2 ? "AF_INET" : "AF_INET6");
printf("ai->ai_socktype:%s \n",ai->ai_socktype == 1 ? "SOCK_STREAM" : (ai->ai_socktype == 2 ? "SOCK_DGRAM" : "SOCK_RAW"));
printf("ai->ai_protocol:%d \n",ai->ai_protocol);
printf("ai->ai_addrlen:%d \n",ai->ai_addrlen);
printf("ai->ai_canonname:%s \n",ai->ai_canonname);
printf("ai->ai_addr: \n");
for (curr = ai; curr != NULL; curr = curr->ai_next)
{
inet_ntop(AF_INET, &(((struct sockaddr_in *)(curr->ai_addr))->sin_addr), ipstr, 16);
printf("%s\n", ipstr);
}
freeaddrinfo(ai);//由getaddrinfo返回的所有存储空间都是动态获取的,这些存储空间必须通过调用freeaddrinfo返回给系统。
}
return;
}
函数结果:
toney@sw2:~/study/network$ ./a.out
ai->ai_family:AF_INET
ai->ai_socktype:SOCK_STREAM
ai->ai_protocol:6
ai->ai_addrlen:16
ai->ai_canonname:sw2
ai->ai_addr:
127.0.1.1
2. getnameinfo函数原型:
getnameinfo是getaddrinfo的互补函数,它是一个套接字地址为参数,返回其中的主机的一个字符串和描述其中服务的另一个字符串。
int getnameinfo(const struct sockaddr *sa, size_t salen, char *host, size_t hostlen, char *serv, size_t servlen, int flags);
输入参数:
sockaddr指向一个套接字结构地址。
返回参数:
host:指定主机字符串。
serv:指定服务字符串。
如果调用者不想返回主机字符串或服务字符串,则指定hostlen或servlen为0.
flags该标志:
改变功能的默认操作的标志。默认情况下,应返回主机的完全限定域名(FQDN),但是:
如果设置了标志位NI_NOFQDN,则只应为本地主机返回FQDN的节点名称部分。
如果设置了标志位NI_NUMERICHOST ,则在所有情况下都应返回sa参数指向的套接字地址结构中包含的地址的数字形式而不是其名称。
如果设置了标志位NI_NAMEREQD,则如果找不到主机名,则应返回错误。
如果设置了标志位NI_NUMERICSERV,则在所有情况下都应返回服务地址的数字形式(例如,其端口号)而不是其名称。
如果设置了标志位NI_NUMERICSCOPE,则应返回范围标识符的数字形式(例如,接口索引)而不是其名称。如果sa参数不是IPv6地址,则应忽略此标志。
如果设置了标志位NI_DGRAM,则表明该服务是数据报服务(SOCK_DGRAM)。默认行为应假定该服务是流服务(SOCK_STREAM)。
函数使用:
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
int ret;
char *ptr;
char hostname[128] = {0};
char servername[128] = {0};
struct sockaddr_in addr_dst;
ptr = "127.0.0.1";
memset(&addr_dst,0,sizeof(addr_dst));
addr_dst.sin_family = AF_INET;
addr_dst.sin_addr.s_addr = inet_addr(ptr);
ret = getnameinfo((struct sockaddr *)&addr_dst, sizeof(addr_dst), hostname, sizeof(hostname), servername, sizeof(servername), 0);
if (ret != 0)
{
printf("error in getnameinfo: %s \n", gai_strerror(ret));
}
else
{
printf("hostname IP: %s \n", hostname);
printf("servername IP: %s \n", servername);
}
return;
}
函数结果:
toney@sw2:~/study/network$ ./a.out
hostname IP: localhost
servername IP: 0