前言
在写项目代码的过程中要将域名解析成IP地址,查阅了资料,对gethostbyname()函数和getaddrinfo()函数有所了解,所以进行一个总结,以便查阅。
一、gethostbyname()函数
1.gethostbyname()函数原型:
struct hostent *gethostbyname(const char *hostname);
该函数需要包含#include <netdb.h>头文件,参数hostname 为主机名,也就是域名;使用该函数时,传递域名字符串,返回一个hostent 的结构体指针;如果返回的结构体指针为空,则表示有错误发生;hostent的定义如下:
struct hostent
{
char *h_name; //官方域名
char **h_aliases; //域名别名
int h_addrtype; //IP地址的类型,IPv4或者IPv6,IPv4 对应 AF_INET,IPv6 对应 AF_INET6
int h_length; //IP地址长度
char **h_addr_list; //IP地址列表,同一个域名可能对应多个IP地址
}
2.简单例子
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <stdio.h>
int main(int argc, char **argv)
{
if (argc < 2)
{
printf("param < 2");
return -1;
}
char *domain_name = argv[1];
struct hostent *hptr;
hptr = gethostbyname(domain_name);
if (hptr == NULL)
{
printf("gethostbyname error for host: %s: %s\n", domain_name, hstrerror(h_errno));
return -1;
}
// 输出主机的官方域名
printf("\tofficial: %s\n", hptr->h_name);
// 输出主机的别名
char **pptr;
char str[INET_ADDRSTRLEN]; //INET_ADDRSTRLEN 表示16字节长度
for (pptr=hptr->h_aliases; *pptr!=NULL; pptr++)
{
printf("\ttalias: %s\n", *pptr);
}
// 输出ip地址
for (pptr = hptr->h_addr_list; *pptr!=NULL; pptr++)
{
// inet_ntop将网络传输的数值格式转化为点分十进制的ip地址格式
printf("\taddress: %s\n",inet_ntop(hptr->h_addrtype, *pptr, str, sizeof(str)));
}
return 0;
}
3.运行结果
二、getaddrinfo()函数
1.getaddrinfo()函数原型
int getaddrinfo( const char *hostname, const char *service, const struct addrinfo *hints, struct addrinfo **result );
参数说明:
hostname:主机名或域名
service:服务名可以是十进制的端口号,也可以是已定义的服务名称,如ftp、http等
hints:可以是一个空指针,也可以是一个指向某个addrinfo结构体的指针,调用者在这个结构中填入关于期望返回的信息类型的暗示。举例来说:指定的服务既可支持TCP也可支持UDP,所以调用者可以把hints结构中的ai_socktype成员设置成SOCK_DGRAM使得返回的仅仅是适用于数据报套接口的信息。
result:将结果存进一个指向addrinfo结构体链表的指针中。
返回值:0—成功,非0—出错
2.额外知识了解:struct addrinfo的定义如下:
struct addrinfo
{
int ai_flags;
int ai_family; // 地址族,可取的值有AF_INET(IPv4) AF_INET6(IPv6) AF_UPSPEC(可以代表任何域)
int ai_socktype; // socket的类型,主要有SOCK_STREA(流)M和SOCK_DGRAM(数据报)两种
int ai_protocol; // socket协议 IPPROTO_IPV4、IPPROTO_IPV6等
socklen_t ai_addrlen; // socket地址长度
struct sockaddr *ai_addr; // socket地址信息
char *ai_canonname;
struct addrinfo *ai_next; // 指向下一个socket地址信息的指针
};
在这里简单说明下struct sockaddr和struct sockaddr_in的区别,下面的例子中会用到;
struct sockaddr的 定义如下:
struct sockaddr
{
unsigned short sa_family; // 2 字节地址族, AF_xxx
char sa_data[14]; // 14 字节的协议地址
};
struct sockaddr_in的定义如下:
struct sockaddr_in
{
short sin_family; // 2 字节地址族, AF_xxx
unsigned short sin_port; // 2 字节端口号,htons(3490)
struct in_addr sin_addr; // 4 字节IP地址
char sin_zero[8]; // 8 字节填充位
};
可以发现这两个结构体一样大,都是16个字节,而且都有family属性,不同的是:sockaddr用其余14个字节来表示地址相关信息,而sockaddr_in把14个字节拆分成sin_port, sin_addr和sin_zero分别表示端口、ip地址,sin_zero用来填充字节使sockaddr_in和sockaddr保持一样大小。所以在使用中sockaddr是给操作系统用的,而sockaddr_in给编码者使用;
一般的用法为:
程序员把类型、ip地址、端口填充sockaddr_in结构体,然后强制转换成sockaddr,作为参数传递给系统调用函数。
2.简单例子
#include <sys/socket.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
int main(int argc, char *argv[])
{
struct addrinfo hints;
struct addrinfo *result;
struct addrinfo *curr;
int ret = -1;
char ipstr[INET_ADDRSTRLEN];
struct sockaddr_in *ipv4;
// 设置期望值
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
ret = getaddrinfo(argv[1], NULL, &hints, &result);
if (ret != 0)
{
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(ret));
exit(EXIT_FAILURE);
}
// 输出获得IP地址
for (curr = result; curr != NULL; curr = curr->ai_next)
{
ipv4 = (struct sockaddr_in *)curr->ai_addr;
// inet_ntop将网络传输的数值格式转化为点分十进制的ip地址格式
inet_ntop(curr->ai_family, &ipv4->sin_addr, ipstr, INET_ADDRSTRLEN);
printf("ipaddr:%s\n", ipstr);
}
/* No longer needed */
freeaddrinfo(result);
return 0;
}
3.运行结果
总结
以上就是今天总结的内容,据了解gethostbyname()函数属于半淘汰状态,目前主要使用getaddrinfo()函数。如以上内容有问题,欢迎大家在评论区指正。
参考链接:
http://c.biancheng.net/view/2357.html.
https://blog.csdn.net/u011003120/article/details/78277133.
https://blog.csdn.net/xdshengk/article/details/47313103.