getaddrinfo()和getnameinfo()函数用法

        Linux提供了一些强大的函数(称为getaddrinfo和getnameinfo)实现二进制套接字地址结构和主机名、主机地址、服务名和端口号的字符串表示之间的相互转化。当和套接字接口一起使用时,这些函数能使我们编写独立于任何特定版本的协议的网络程序。

#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
int getaddrinfo(const char *host, const char *service, 
                const struct addrinfo *hints, 
                struct addrinfo **result);
//返回:如果成功返回0;如果错误返回非0的错误代码。

void freeaddrinfo(struct addrinfo *result);
//释放链表,无返回。

const char *gai_strerror(int errcode);
//返回:错误消息。

下面就来分析getaddrinfo()函数的用法。

参数说明:

  1. const char *host:一个域名或一个IP地址(IPv4或IPv6都行)。

  2. const char *service:服务名(http,ftp等)或端口号。

  3. const struct addrinfo *hints:参数hints可以为空(NULL),也可以是一个指向addrinfo结构体的指针。addrinfo结构体成员如下所示:

struct addrinfo {
    int              ai_flags;
    int              ai_family;
    int              ai_socktype;
    int              ai_protocol;
    char            *ai_canonname;
    size_t           ai_addrlen;
    struct sockaddr *ai_addr;
    struct addrinfo *ai_next;
};

学过数据结构都知道addrinfo就是一个链表,ai_next指向下一个节点。 如果要传递hints参数,只能设置下列字段:ai_family、ai_socktype、ai_protocol和ai_flags字段。其他字段必须设置为NULL。实际中,我们用memset将整个结构清零,然后有选择地设置一些字段。

下面对addrinfo的每个字段详细分析:

  • ai_family:指定返回地址的地址族。

常量名  取值含义
AF_INET 2IPv4
AF_INET623IPv6
AF_UNSPEC0协议无关
  • ai_socktype:指定套接字的类型 

常量名取值含义
SOCK_STREAM1
SOCK_DGRAM2数据报
  • ai_protocol:指定socket的协议

常量名取值含义
IPPROTO_IP0协议无关
IPPROTO_IPV44IPv4
IPPROTO_IPV641IPv6
IPPROTO_UDP17UDP
  • ai_flags:一个位掩码

    A1_ADDRCONFIG:它要求只有当本地主机被配置为IPv4时,getaddrinfo返回IPv4地址。对IPv6也是类似。
    AI_CANONNAME:ai_canonname字段默认为NUlL。如果设置了该标志,就是告诉getaddrinfo将列表中第一个addrinfo结构的ai_canonname字段指向host的权威(官方)名字。

    AI_NUMERICSERV:参数service默认可以是服务名或端口号。这个标志强制参数service为端口号。
    AL_PASSIVE:getaddrinfo默认返回套接字地址,客户端可以在调用connect时用作主动套接字。这个标志告诉该函数,返回的套接字地址可能被服务器用作监听套接字。在这种情况中,参数host应该为NULL。得到的套接字地址结构中的地址字段会是通配符地址,告诉内核这个服务器会接受发送到该主机所有IP地址的请求。这是所有示例服务器所期望的行为。

        如果将hints指定为 NULL相当于将 ai_socktype 和 ai_protocol 设置为 0;将ai_family设置为AF_UNSPEC;将ai_flags 设置为(AI_V4MAPPED | AI_ADDRCONFIG)。

4.struct addrinfo **result:result一个指向addrinfo结构的链表,其中每个结构指向一个对应于host和service的套接字地址结构。

第二个重点函数就是getnameinfo()。getnameinfo函数和getaddrinfo是相反的,将一个套接字地址结构转换成相应的主机和服务名字符串。它是已弃用的gethostbyaddr和getservbyport函数的新的替代品,和以前的那些函数不同,它是可重入和与协议无关的。

#include <sys/socket.h>
#include <netdb.h>
int getnameinfo(const struct sockaddr *sa,socklen_t salen,
                char *host,size_t hostlen,
                char *service,size_t servlen,int flags);
//返回:如果成功则为0,如果错误则为非零的错误代码。

        参数sa指向大小为salen字节的套接字地址结构,host指向大小为hostlen字节的缓冲区,service指向大小为servlen字节的缓冲区。getnameinfo函数将套接字地址结构sa转换成对应的主机和服务名字符串,并将它们复制到host和servcice缓冲区。如果getnameinfo返回非零的错误代码,应用程序可以调用gai_strerror把它转化成字符串。
       如果不想要主机名,可以把host设置为NULL,hostlen设置为0。对服务字段来说也是一样。不过,两者必须设置其中之一。
       参数flags是一个位掩码,能够修改默认的行为。可以把各种值用OR组合起来得到该掩码。下面是两个有用的值:

  • NI_NUMERICHOST:getnameinfo默认试图返回host中的域名。设置该标志会使该函数返回一个数字地址字符串。

  • NI_NUMERICSERV:getnameinfo默认会检查/etc/services,如果可能,会返回服务名而不是端口号。设置该标志会使该函数跳过查找,简单地返回端口号。


下面是getaddrinfo()和getnameinfo()函数的应用,一个域名解析程序能更好的理解这两个函数。


#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netdb.h>

#define MAXLINE 8192

int main(int argc ,char **argv)
{
  struct addrinfo *p,*listp,hints;
  char buf[MAXLINE];
  int rc,flags;
  if(argc!=2)
  {
    fprintf(stderr,"usag:%s<domain name>\n",argv[0]);
    exit(0);
  }
  memset(&hints,0,sizeof(struct addrinfo));
  hints.ai_family=AF_INET;
  hints.ai_socktype=SOCK_STREAM;
  if((rc=getaddrinfo(argv[1],NULL,&hints,&listp))!=0)
  {
    fprintf(stderr,"getaddrinfo error :%s",gai_strerror(rc));
    exit(1);
  }
  flags=NI_NUMERICHOST;
  for(p=listp;p;p=p->ai_next)
  {
    getnameinfo(p->ai_addr,p->ai_addrlen,buf,MAXLINE,NULL,0,flags);
    printf("%s\n",buf); 
  }
  freeaddrinfo(listp);
  exit(0);
 }

        这段代码比较简单,没有难懂偏僻的语法,逻辑也很清晰,所以就不解释这段代码了。

        程序名为hostinfo.c,通过输入gcc -o hostinfo hostinfo.c 指令就能得到hostinfo可执行目标文件。在Linux操作系统环境的运行结果如下:

通过ip地址查询网站得到的结果如下:

 

        运行结果和ip地址查询结果也基本一致。 

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值