获取本地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,希望知道的可以给留个言,也让我学习一下。