在linux网络编程中,gethostbyname函数可以通过域名url直接获得ip地址相关信息,返回的是一个名为hostent的结构体,通过man gethostbyname手册查询后,发现该结构体如下
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 */
}
#define h_addr h_addr_list[0] /* for backward compatibility */
该结构体的成员前四个都比较容易理解,h_name是服务器端的名称,比如打开www.baiducom,那么相应的h_name便是,h_aliases是服务器为了负载均衡所使用的域名,h_addrtype指出ip地址的版本号,ipv4以及ipv6, 对理解造成障碍的是h_addr_list,这是一个字符指针的指针,但惟独只有首个成员有意义,就是h_addr_list[0], 通过宏定义将h_addr命名为h_addr_list[0], 其实这个指针指向的,是一个名为in_addr的结构体的地址,可以通过强制类型转换(struct in_addr *) h->h_addr 得到该结构体。而struct in_addr结构体定义 如下
struct in_addr {
in_addr_t s_addr;
};
只有 一个成员变量,ni_addr_t其实是int类型,通过宏定义命名为ni_addr_t,在不同硬件平台上估计有 不同的长度,而s_addr,其实是将点分十进制构成的ip地址转换成十进制数字后的值。
下面通过一个小程序验证一下
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
int main (int argc, char *argv[])
{
struct hostent *h;
/* 检测命令行中的参数是否存在 */
if (argc != 2) {
/* 如果没有参数,给出使用方法 */
printf ("usage: getip address\n");
/* 然后退出 */
exit(1);
}
/* 取得主机信息 */
if ((h=gethostbyname(argv[1])) == NULL){
/* 如果gethostbyname失败,则给出错误信息 */
herror("gethostbyname");
/* 然后退出 */
exit(1);
}
/* 列印程序取得的信息 */
printf("h_name: %s\n", h->h_name);
printf("h_aliases: %s\n", h->h_aliases[0]);
printf("h_addrtype: %d\n", h->h_addrtype);
printf("h_length: %d\n", h->h_length);
struct in_addr *inaddr;
inaddr = (struct in_addr *)h->h_addr;
printf("IP Address: %x\n",inaddr->s_addr);
/* 返回 */
return 0;
编译#gcc tes.c -o tes.bin
结果:
h_name: h_aliases: h_addrtype: 2 h_length: 4 IP Address: 69a9873d |
上边的ip地址并非点分十进制的字符串的形式,而是已经按照字节排列的16进制形式。
这里会有一个非常令人困惑的地方,牵扯到字符以及整数在linux中的存储问题,linux系统中存储的规则是小段存储,就是说,一段数字或字符,权值小的,存储在地址的低端部分,权值高的,存储在地址高的部分,所有的数据和字符按照地址增长的方向存储。
比如, 69 a9 87 3d在地址中,如果由低向高排列的话,会是
3d, 87, a9, 69
明白这点了吗?通过一个小程序 来验证以下,将上边的代码添加如下函数
void show(int x)
{
int a, b, c, d;
a = x&0xff000000;
a>>=24;
b = x&0x00ff0000;
b>>=16;
c = x&0x0000ff00;
c>>=8;
d = x&0x000000ff;
printf("%x %x %x %x\n", a, b, c, d);
}
这个函数的功能是将整形变量x的按照字节长度展开
添加后是 这样:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>\
void show(int x);
int main (int argc, char *argv[])
{
struct hostent *h;
/* 检测命令行中的参数是否存在 */
if (argc != 2) {
/* 如果没有参数,给出使用方法 */
printf ("usage: getip address\n");
/* 然后退出 */
exit(1);
}
/* 取得主机信息 */
if ((h=gethostbyname(argv[1])) == NULL){
/* 如果gethostbyname失败,则给出错误信息 */
herror("gethostbyname");
/* 然后退出 */
exit(1);
}
/* 列印程序取得的信息 */
printf("h_name: %s\n", h->h_name);
printf("h_aliases: %s\n", h->h_aliases[0]);
printf("h_addrtype: %d\n", h->h_addrtype);
printf("h_length: %d\n", h->h_length);
struct in_addr *inaddr;
inaddr = (struct in_addr *)h->h_addr;
printf("IP Address: %x\n",inaddr->s_addr);
show(inaddr->s_addr);
/* 返回 */
return 0;
}
void show(int x)
{
int a, b, c, d;
a = x&0xff000000;
a>>=24;
b = x&0x00ff0000;
b>>=16;
c = x&0x0000ff00;
c>>=8;
d = x&0x000000ff;
printf("%x %x %x %x\n", a, b, c, d);
}
执行结果如下:
h_name: h_aliases: h_addrtype: 2 h_length: 4 IP Address: 7da9873d 7d a9 87 3d |
十六进制数展开后,是小端排列。