一、getaddrinfo
函数原型: int getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res);
函数功能:根据hints指定的参数要求,获取服务器node的信息,并将获取的结果存储到res中
函数参数:node 服务器的域名或者IP地址
service 服务的十进制端口字符串或者协议类型,如“8099”、“http”
hints 输入参数,指定获取服务器信息的参数
res 存储返回的服务器信息
返回值:成功返回0,失败返回对应的错误信息
struct addrinfo结构体如下:
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。
ai_flags用来指定如何处理地址和名字,它的取值有3个:
- AI_PASSIVE 当此标志置位时,表示调用者将在bind()函数调用中使用返回的地址结构。当此标志不置位时,表示将在connect()函数调用中使用。
当节点名位NULL,且此标志置位,则返回的地址将是通配地址。 如果节点名NULL,且此标志不置位,则返回的地址将是回环地址。 - AI_CANNONAME 当此标志置位时,在函数所返回的第一个addrinfo结构中的ai_cannoname成员中,应该包含一个以空字符结尾的字符串,字符串的内容是节点名的正规名。
- AI_NUMERICHOST 当此标志置位时,此标志表示调用中的节点名必须是一个数字地址字符串。
在WiFi产品的开发中,我们比较常用的场景是根据主机的域名解析主机所使用的ip地址,ai_flags通常设置为0。
ai_family用来指定需要解析的域名对应的IP地址类型,可以是IPv4或者IPv6,如果不知道主机域名对应的IP类型,可以将这个参数设置成AF_UNSPEC,它的参数如下:
- AF_INET 协议类型为IPv4
- AF_INET6 协议类型为IPv6
- AF_UNSPEC 不指定协议类型
ai_protocol用来指定协议类型,ai_family只能指定IPv4或者IPv6,ai_protocal则更加细化,可以指定为TCP、UDP、IP等等,具体的取值如下:
- IPPROTO_IP IP协议
- IPPROTO_IPV4 IPv4协议
- IPPROTO_IPV6 IPv6协议
- IPPROTO_UDP UDP协议
- IPPROTO_TCP TCP协议
ai_socktype用来指定协议的数据类型,它的取值如下:
- SOCK_STREAM 数据流,对应TCP协议
- SOCK_DGRAM 数据报,对应UDP协议
剩余的几个参数在getaddrinfo函数的hints参数中没有用到,无需设置。
getaddrinfo函数返回的服务器信息存储在res变量中,一个域名可以对应多个IP地址,所以addrinfo结构体是一个链表,可以通过遍历链表得到所有的IP地址信息。
示例:
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int PhaseServerAddr(char *host)
{
int rc = -1;
char *ip;
struct addrinfo *result = NULL;
struct addrinfo hints = {0, AF_UNSPEC, SOCK_STREAM, IPPROTO_TCP, 0, NULL, NULL, NULL};
if ((rc = getaddrinfo(host, NULL, &hints, &result)) == 0)
{
struct addrinfo* res = result;
printf("AF_INET:%d\r\n",AF_INET);
while (res)
{
ip = inet_ntoa(((struct sockaddr_in*)(res->ai_addr))->sin_addr);
printf("ip type:%d addr:%s\r\n",res->ai_family,ip);
res = res->ai_next;
}
freeaddrinfo(result);
}
}
int main()
{
PhaseServerAddr("www.baidu.com");
}
执行结果:
把得到的IP地址放到浏览器中,也可以直接打开百度主页。
二、gethostbyname
函数原型:struct hostent *gethostbyname(const char *name);
函数功能:根据域名解析IP地址
函数参数:name 域名,如“www.baidu.com”
返回值:成功返回存储IP地址的结构体,失败返回NULL
struct 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 */
}
- h_name:官方域名。官方域名代表某一主页,但实际上一些著名公司的域名并未用官方域名注册。
- h_aliases:别名,可以通过多个域名访问同一主机。同一 IP 地址可以绑定多个域名,因此除了当前域名还可以指定其他域名。
- h_addrtype:指明服务器的地址类型,IPv4 对应 AF_INET,IPv6 对应 AF_INET6。
- h_length:保存IP地址长度。IPv4 的长度为 4 个字节,IPv6 的长度为 16 个字节。
- h_addr_list:存储域名对应的IP地址,一个域名可以对应多个IP地址。
示例:
void PhaseServer()
{
char **ptr;
struct hostent *hst;
char str[16];
hst = gethostbyname("www.baidu.com");
if(NULL != hst)
{
printf("offical name:%s\r\n",hst->h_name==NULL?"NULL":hst->h_name);
for(ptr = hst->h_aliases;*ptr != NULL;ptr++)
{
printf(" alias:%s\r\n",*ptr);
}
switch(hst->h_addrtype)
{
case AF_INET:
case AF_INET6:
ptr=hst->h_addr_list;
for(;*ptr!=NULL;ptr++)
{
char *p = inet_ntop(hst->h_addrtype, *ptr, str, sizeof(str));
if(NULL != p)
printf(" address:%s\r\n", p);
}
break;
default:
printf("unknown address type/n");
break;
}
}
}
运行结果:
|