现在的位置: 首页 > 综合 > 正文

Linux下获取本地IP地址——–getaddrinfo

2013年11月30日 ⁄ 综合 ⁄ 共 3161字 ⁄ 字号 评论关闭

  获取本地IP地址的方式很多,这里使用的接口是getaddrinfo(最开始发表时,这个地方表述有错误,这个接口也不能解决主机名为localhost,获取的IP地址是127.0.0.1,但是可以通过修改/etc/hosts文件来绑定本地IP地址)。这个接口在《Unix网络编程》第一卷的11.2节有讲解,但是当时看书的时候没有深入的研究这个接口(当时可能感觉获取本地ip地址很容易),这个接口及其相关的两个接口定义如下:

       #include <sys/types.h>
       #include <sys/socket.h>
       #include <sys/socket.h>
       #include <netdb.h>

       int getaddrinfo(const char *node, const char *service,
                       const struct addrinfo *hints,
                       struct addrinfo **res);

       void freeaddrinfo(struct addrinfo *res);

       const char *gai_strerror(int errcode);

一、 getaddrinfo接口
getaddrinfo通过res来返回一个指向struct addrinfo结构链表的指针(注意这里返回的是一个链表),struct addrinfo结构的定义如下所示:

struct addrinfo
{
  int ai_flags;         /* Input flags.  */
  int ai_family;        /* Protocol family for socket.  */
  int ai_socktype;      /* Socket type.  */
  int ai_protocol;      /* Protocol for socket.  */
  socklen_t ai_addrlen;     /* Length of socket address.  */
  struct sockaddr *ai_addr; /* Socket address for socket.  */
  char *ai_canonname;       /* Canonical name for service location.  */
  struct addrinfo *ai_next; /* Pointer to next in list.  */
};

其中ai_flags中可以设置的值为(这里之所以要列出来是因为感觉书中说的太少,而且翻译的好像跟实际的注释差别比较大,读者可以自己看注释理解,更多的信息可以通过man手册和netdb.h中看到)

/* Possible values for `ai_flags' field in `addrinfo' structure.  */
# define AI_PASSIVE 0x0001  /* Socket address is intended for `bind'.  */
# define AI_CANONNAME   0x0002  /* Request for canonical name.  */
# define AI_NUMERICHOST 0x0004  /* Don't use name resolution.  */
# define AI_V4MAPPED    0x0008  /* IPv4 mapped addresses are acceptable.  */
# define AI_ALL     0x0010  /* Return IPv4 mapped and IPv6 addresses.  */
# define AI_ADDRCONFIG  0x0020  /* Use configuration of this host to choose
                   returned address type..  */
# ifdef __USE_GNU
#  define AI_IDN    0x0040  /* IDN encode input (assuming it is encoded
                   in the current locale's character set)
                   before looking it up. */
#  define AI_CANONIDN   0x0080  /* Translate canonical name from IDN format. */
#  define AI_IDN_ALLOW_UNASSIGNED 0x0100 /* Don't reject unassigned Unicode
                        code points.  */
#  define AI_IDN_USE_STD3_ASCII_RULES 0x0200 /* Validate strings according to
                        STD3 rules.  */
# endif
# define AI_NUMERICSERV 0x0400  /* Don't use name resolution.  */

hoststname是主机名或地址串,service是服务名或十进制数的端口号字符串。调用者可以设置的hints结构的成员有ai_flags、ai_family,ai_socktype和ai_protocol。

 如果hints可以是一个空指针或指向一个addrinfo结构的指针,由调用者来指定要返回的信息类型。hostname和service不能同时为空,如果同时为空,会返回EAI_NONAME错误。还有一个细节需要注意的是getaddrinfo接口发生错误时不是通过设置errno来标示错误类型,而是返回自定义的错误码。

二、 freeaddrinfo接口

  freeaddrinfo接口用来释放getaddrinfo接口返回的res指向的链表,如果调用getaddrinfo接口后没有释放内存,会造成内存泄露,所以一定要记得调用这个接口来释放内存

三、 gai_strerror接口

 gai_strerror用来获取getaddrinfo接口返回的错误码对应的错误信息,注意getaddrinfo不是设置errno,切记!

四、示例

  说的再多还是不如来个例子实在,下面是我写的一个实际使用的例子,有不合适的地方希望大家批评指正,共同进步。

#include <sys/types.h>
#include <arpa/inet.h>

int main(void)
{
    char               buf[100];
    int                ret = 0;
    struct addrinfo    hints;
    struct addrinfo    *res, *curr;
    struct sockaddr_in *sa;

    bzero(&hints, sizeof (hints));
    hints.ai_flags = AI_CANONNAME;
    hints.ai_family = AF_INET;
    if (gethostname(buf, sizeof (buf)) < 0) {
        perror("gethostname");
        return -1; 
    }   
    if((ret = getaddrinfo(buf, NULL, &hints, &res)) != 0) {
        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(ret));
        return -1; 
    }   
    curr = res;
    while (curr && curr->ai_canonname) {
        sa = (struct sockaddr_in *)curr->ai_addr;
        printf("name: %s\nip:%s\n\n", curr->ai_canonname, 
                inet_ntop(AF_INET, &sa->sin_addr.s_addr, buf, sizeof (buf)));
        curr = curr->ai_next;
    }   

 在循环链表的时候我除了判断curr不为NULL之外,还判断了ai_canonname成员是否未空,为什么要这么做呢?因为我在我的测试机上测试时,本身只有一个IP地址的,但是输出的时候却输出了三次同样的ip地址,不同的是后面两个的输出时名字为空,所以我才会加上这个判断。由于测试资源有限,所以没有做过多的测试,在我测试的过程中确实如此,或许是一个BUG。如果不是BUG,希望知道的可以给留个言,也让我学习一下。

抱歉!评论已关闭.