linux c 域名转ip函数 gethostbyname 返回结构体 hostent 简介

在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便是www.a.shifen.com

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

执行

./tes.bin  www.baidu.com

结果:

h_name: www.a.shifen.com
h_aliases: www.baidu.com
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: www.a.shifen.com
h_aliases: www.baidu.com
h_addrtype: 2
h_length: 4
IP Address: 7da9873d
7d a9 87 3d

十六进制数展开后,是小端排列。

猜你喜欢

转载自blog.csdn.net/whatday/article/details/121050330