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