简介
域名解析也叫域名指向、服务器设置、域名配置以及反向IP登记等等。说得简单点就是将好记的域名解析成IP,服务由DNS服务器完成,是把域名解析到一个IP地址,然后在此IP地址的主机上将一个子目录与域名绑定。
互联网中的地址是数字的IP地址,域名解析的作用主要就是为了便于记忆。
常用的域名解析函数有getaddrinfo()和gethostbyname(),一些资料书认为gethostbyname()已经过时且不支持IPv6,更推荐使用getaddrinfo()。
gethostbyname()的衍生版本gethostbyname2() 支持IPV6。
gethostbyname()
函数原型
#include <netdb.h>
struct hostent *gethostbyname(const char *name);
struct hostent{
char *h_name; //official name
char **h_aliases; //alias list
int h_addrtype; //host address type
int h_length; //address lenght
char **h_addr_list; //address list
}
// 从该结构体可以看出,不只返回 IP 地址,还会附带其他信息,各位读者只需关注最后一个成员 h_addr_list。下面是对各成员的说明:
// h_name:官方主机名,返回字符串,结尾带NULL。
// h_aliases:别名,可以通过多个域名访问同一主机。
// h_addrtype:IPv4或者IPv6。
// h_length:保存IP地址长度,IPv4 的长度为 4 个字节,IPv6 的长度为 16 个字节。
// h_addr_list:这是最重要的成员,通过该成员以整数形式保存域名对应的 IP 地址,有负载均衡的服务器可解析出多个IP。
demo
#include <netdb.h>
#include <arpa/inet.h>
#include <stdio.h>
int main()
{
struct hostent *host;
int i = 0;
host = gethostbyname("www.baidu.com");
if (host != NULL)
{
// 官方域名
printf("h_name = %s\r\n", host->h_name);
// 别名
for(i = 0; host->h_aliases[i]; i++)
{
printf("Aliases %d: %s\n", i+1, host->h_aliases[i]);
}
// 地址类型
printf("Address type: %s\n", (host->h_addrtype==AF_INET) ? "AF_INET": "AF_INET6");
// IP地址
for(i = 0; host->h_addr_list[i]; i++)
{
printf("IP addr %d: %s\n", i+1, inet_ntoa(*(struct in_addr *)host->h_addr_list[i]));
}
}
return 0;
}
// 输出
// h_name = www.a.shifen.com
// Aliases 1: www.baidu.com
// Address type: AF_INET
// IP addr 1: 14.215.177.38
// IP addr 2: 14.215.177.39
gethostbyname2()
函数原型
#include <netdb.h>
struct hostent *gethostbyname2(const char *name, int family);
// family可选 AF_INET(IPv4) 或 AF_INET6(IPv6)
struct hostent{
char *h_name; //official name
char **h_aliases; //alias list
int h_addrtype; //host address type
int h_length; //address lenght
char **h_addr_list; //address list
}
// 从该结构体可以看出,不只返回 IP 地址,还会附带其他信息,各位读者只需关注最后一个成员 h_addr_list。下面是对各成员的说明:
// h_name:官方主机名,返回字符串,结尾带NULL。
// h_aliases:别名,可以通过多个域名访问同一主机。
// h_addrtype:IPv4或者IPv6。
// h_length:保存IP地址长度,IPv4 的长度为 4 个字节,IPv6 的长度为 16 个字节。
// h_addr_list:这是最重要的成员,通过该成员以整数形式保存域名对应的 IP 地址,有负载均衡的服务器可解析出多个IP。
demo
如果把demo中的AF_INET6改成AF_INET,则效果和gethostbyname()一致。
#include <netdb.h>
#include <arpa/inet.h>
#include <stdio.h>
int main()
{
struct hostent *host;
int i = 0;
char buf[100];
host = gethostbyname2("iservice.10010.com", AF_INET6);
if (host != NULL)
{
// 官方域名
printf("h_name = %s\r\n", host->h_name);
// 别名
for(i = 0; host->h_aliases[i]; i++)
{
printf("Aliases %d: %s\n", i+1, host->h_aliases[i]);
}
// 地址类型
printf("Address type: %s\n", (host->h_addrtype==AF_INET) ? "AF_INET": "AF_INET6");
// IP地址
for(i = 0; host->h_addr_list[i]; i++)
{
printf("IP addr %d: %s\n", i+1, inet_ntop(AF_INET6, host->h_addr_list[i], buf, 100));
}
}
return 0;
}
// 输出
// h_name = iservice.10010.com.w.cdngslb.com
// Aliases 1: iservice.10010.com
// Address type: AF_INET6
// IP addr 1: 240e:95c:2002:1:3::3fe
// IP addr 2: 240e:95c:2002:1:3::3fd
getaddrinfo()
函数原型
#include <sys/socket>
#include <netdb.h>
int getaddrinfo(const char *host, const char *service, const struct addrinfo *hints, struct addrinfo **result);
// @param host:域名,如www.baidu.com。
// @param service:服务名,如http、ftp。
// @param hints:该结构规定了选择通过result返回的socket地址结构的标准。一般可以设置为
// {0, AF_UNSPEC, SOCK_STREAM, IPPROTO_TCP, 0, NULL, NULL, NULL}
// @param result:返回一个结构列表而不是单个结构。
// @return:成功返回0,成功返回后要使用freeaddrinfo(struct addrinfo *result)来释放动态申请的内存。
struct addrinfo{
int ai_flags;
int ai_family;
int ai_socktype;
int ai_protocol;
size_t ai_addrlen;
char *ai_canonname;
struct sockaddr *ai_addr;
struct addrinfo *ai_next;
}
hints参数
hints参数为如何选择getaddrinfo()返回的socket地址结构指定了更多的标准。当用作hints 参数时只能设置addrinfo结构的ai_flags、ai_family、ai_socktype以及ai_protocol字段,其他字段未用到,并将应该根据具体情况将其初始化为0或NULL。
hints .ai_family字段选择了返回的socket的地址结构的域,可以为AF_INET(IPv4)、AF_INET6(IPv6)(或其他的一些AF_*常量,只要实现支持)。如果需要获取所有种类socket地址结构,那么可以将这个字段的值指定为AF_UNSPEC。
hints .ai_socktype字段指定了socket类型,可以是SOCK_STREAM或SOCK_DGRAM,也可以为0,那么任意类型的socket都是可以接受的。
hints .ai_protocol字段表示选择的socket协议,可以是IPPROTO_TCP或IPPROTO_UDP,也可以为0,表示调用者接受任何协议。
hints .ai_flags字段是一个位掩码,它会改变getaddrinfo()的行为,一般为0,详情请参考《Linux/UNIX系统编程手册》P997。
demo
#include <netdb.h>
#include <arpa/inet.h>
#include <stdio.h>
int main()
{
int rc;
char buf[100];
struct addrinfo *result = NULL;
struct addrinfo hints = {0, AF_UNSPEC, SOCK_STREAM, IPPROTO_TCP, 0, NULL, NULL, NULL};
if ((rc = getaddrinfo("iservice.10010.com", NULL, &hints, &result)) == 0)
{
struct addrinfo* res = result;
while (res)
{
if (res->ai_family == AF_INET)
{
printf("%s\r\n", inet_ntoa(((struct sockaddr_in *)res->ai_addr)->sin_addr));
}
else if (res->ai_family == AF_INET6)
{
printf("%s\r\n", inet_ntop(AF_INET6, &(((struct sockaddr_in6 *)res->ai_addr)->sin6_addr), buf, 100));
}
res = res->ai_next;
}
freeaddrinfo(result);
}
}
// 输出
// 58.49.225.143
// 58.49.225.144
// 58.49.225.145
// 58.49.225.142
// 58.49.225.148
// 58.49.225.147
// 58.49.225.141
// 58.49.225.146
// 240e:95c:2002:1:3::3fe
// 240e:95c:2002:1:3::3fd