Linux网络编程 - 主机域名和IP地址的转换(IPv4 IPv6)

一  域名和网络IP地址结构体 - struct hostent

struct hostent  结构体定义如下:

#include struct hostent
{char  *h_name;        //主机名,即官方域名char  **h_aliases;    //主机所有别名构成的字符串数组,同一IP可绑定多个域名int   h_addrtype;     //主机IP地址的类型,例如IPV4(AF_INET)还是IPV6int   h_length;       //主机IP地址占用字节长度,IPV4地址为4,IPV6地址则为16char  **h_addr_list;  //主机的ip地址,以网络字节序存储。若要打印出这个IP,需要调用inet_ntoa()
};

 【参考链接】域名和网络地址结构体---struct hostent

二  IPv4 地址格式与主机域名的相互转换

使用 gethostbyname() 函数可以利用字符串格式的域名获得IP地址,并且将地址信息装入 hostent 域名结构体。相关函数定义如下:

2.1  gethostbyname、gethostbyaddr 函数

  • gethostbyname() — 通过传递字符串格式的域名获取IP地址。
  • gethostbyaddr() — 利用IP地址字符串获取域名相关信息。
  • gethostent() — 获取网络主机的实体(entry),即 struct hostent 结构体对象。
#include extern int h_errno;struct hostent * gethostbyname(cosnt char *hostname);
//参数说明
//hostname: 指向存放域名字符串的内存地址
//返回值: 成功时返回hostent结构体指针,失败时返回NULL#include 
struct hostent *gethostbyaddr(const void *addr, socklen_t len, int type);/*参数说明
addr: IP地址字符串。len: 向第一个参数传递的地址信息的字节数,IPv4时为4,IPv6时为16。 
type: 传递地址族信息,IPv4时为AF_INET,IPv6时为AF_INET6。
*/
//返回值: 成功时返回 hostent 结构体变量地址值,失败时返回 NULLvoid sethostent(int stayopen);void endhostent(void);void herror(const char *s);const char *hstrerror(int err);struct hostent *gethostent(void);

函数说明

  • gethostbyname() 函数用于IPv4地址,type为 AF_INET。该函数不是线程安全的。
  • gethostbyaddr() 函数即可用于IPv4地址,也可用于IPv6地址。该函数不是线程安全的。
  • gethostent() 函数用于从 host file 获取 hostent entry。通常都是用一个while循环来获取所有的entry。该函数不是线程安全的。

2.2  gethostbyname_r()、gethostbyaddr_r()、gethostent_r() — 线程安全版本函数

#include 
#include int gethostbyname_r(const char *name, struct hostent *ret, char *buf, size_t buflen,struct hostent **result, int *h_errnop);int gethostbyaddr_r(const void *addr, socklen_t len, int type,struct hostent *ret, char *buf, size_t buflen,struct hostent **result, int *h_errnop);int gethostent_r(struct hostent *ret, char *buf, size_t buflen,struct hostent **result, int *h_errnop);

范例:sethostent,endhostent,gethostent 函数的配合使用方法。

#include 
#include 
#include 
#include   //inet_ntop()void printHostname(struct hostent *host);int main(int argc, char *argv[])
{struct hostent *host = NULL;sethostent(1);  //使用TCP连接while((host = gethostent()) != NULL) //从/etc/hosts文件获取主机域名{printHostname(host);printf("\n");}endhostent();  //关闭TCP连接return 0;
}void printHostname(struct hostent *host)
{//char **aliases = NULL;//char **addr_list = NULL;int i;char addr[INET_ADDRSTRLEN] = {0};// print host name and aliasesprintf("hostname: %s\n", host->h_name);for(i=0; host->h_aliases[i]; i++){printf("alternate name: %s\n", host->h_aliases[i]);}// print address type and lengthif(host->h_addrtype == AF_INET){printf("address type: AF_INET\n");}else if(host->h_addrtype == AF_INET6){printf("address type: AF_INET6\n");}printf("address length: %d\n", host->h_length);//print address listfor(i=0; host->h_addr_list[i]; i++){printf("host->h_addr_list[%d]=%p\n", i, host->h_addr_list[i]);printf("address: %s\n", inet_ntop(host->h_addrtype, host->h_addr_list[i], addr, INET_ADDRSTRLEN));//printf("addr: %s\n", addr);}
}

运行结果

hostname: localhost
alternate name: localhost.localdomain
alternate name: localhost4
alternate name: localhost4.localdomain4
address type: AF_INET
address length: 4
host->h_addr_list[0]=0x249a420
address: 127.0.0.1hostname: localhost
alternate name: localhost.localdomain
alternate name: localhost6
alternate name: localhost6.localdomain6
address type: AF_INET
address length: 4
host->h_addr_list[0]=0x249a420
address: 127.0.0.1

cat /etc/hosts 文件内容如下:

127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6

从运行结果来看,它并没有打印出 IPv6 格式的地址,可能的原因是Linux系统没有开启 IPv6 功能,这个需要设置相关的系统配置文件。

三  IPv6 地址格式与主机域名的相互转换

与 IPv6网络地址格式相关的结构体有:sockaddrsockaddr_in6in6_addr。相关结构体的定义如下:

#include 
#include struct sockaddr
{sa_family_t    sin_family;    //地址族(Address Family)char    sa_data[14];   //地址信息
};struct sockaddr_in6
{sa_family_t     sin6_family;   //AF_INET6(地址族)in_port_t       sin6_port;     //port number(端口号)uint32_t        sin6_flowinfo; //IPv6 flow information(IPv6流信息)struct in6_addr sin6_addr;     //IPv6 address(IPv6地址结构体)uint32_t        sin6_scope_id; //Scope ID (new in 2.4)(尚处于实验阶段)
};struct in6_addr
{unsigned char   s6_addr[16];   //IPv6 address(IPv6地址,要用网络字节序表示)
};

结构体说明】sockaddr 结构体存储空间的大小和 sockaddr_in、sockaddr_in6 结构体的存储空间大小是一样的,这样做的好处是便于进行地址信息的转换。sockaddr 是通用网络地址信息结构体,sockaddr_in 是 IPv4 格式的网络地址信息结构体,sockaddr_in6 是IPv6 格式的网络地址信息的结构体。

3.1  gethostbyname2 函数 — 通过主机域名获取 IPv4 / IPv6 网络地址格式的主机实体对象 hostent

#include 
#include struct hostent *gethostbyname2(const char *name, int af);//参数说明
//name: 指向存放IP地址字符串的内存空间地址。
//af: 协议族名称。IPv4:AF_INET; IPv6:AF_INET6。
//返回值: 成功,返回指向hostent结构体对象的指针;失败,返回 NULL。 //线程安全版本函数
int gethostbyname2_r(const char *name, int af,struct hostent *ret, char *buf, size_t buflen,struct hostent **result, int *h_errnop);

函数说明】gethostbyname2 是 gethostbyname 函数的升级版,既支持 IPv4 也支持 IPv6,通过传入参数 af 的值加以区分。如果传入的实参是 AF_INET,则返回的 hostent 中的IP地址是以 IPv4 格式的字符串来表示;如果传入的是 AF_INET6,则返回的 hostent 中的IP地址是以 IPv6 格式的字符串来表示的。

范例:gethostbyname、gethostbyname2 函数的使用。

#include 
#include 
#include 
#include   //inet_ntop()void printHostname(struct hostent *host);int main(int argc, char *argv[])
{if(argc != 2){printf("Usage: %s \n", argv[0]);return -1;}struct hostent *hst4 = NULL;struct hostent *hst6 = NULL;hst4 = gethostbyname(argv[1]);if(hst4 != NULL){printHostname(hst4);}putchar('\n');hst6 = gethostbyname2(argv[1], AF_INET6);if(hst6 != NULL){printHostname(hst6);}return 0;
}void printHostname(struct hostent *host)
{//char **aliases = NULL;//char **addr_list = NULL;int i;char addr[INET_ADDRSTRLEN] = {0};// print host name and aliasesprintf("hostname: %s\n", host->h_name);for(i=0; host->h_aliases[i]; i++){printf("alternate name: %s\n", host->h_aliases[i]);}// print address type and lengthswitch(host->h_addrtype){case AF_INET:printf("address type: AF_INET\n");break;case AF_INET6:printf("address type: AF_INET6\n");break;default:printf("address type: Unknown\n");}printf("address length: %d\n", host->h_length);//print address listfor(i=0; host->h_addr_list[i]; i++){printf("host->h_addr_list[%d]=%p\n", i, host->h_addr_list[i]);printf("address: %s\n", inet_ntop(host->h_addrtype, host->h_addr_list[i], addr, INET_ADDRSTRLEN));//printf("addr: %s\n", addr);}
}

 运行结果

$ gcc demo.c -o demo
【运行结果1】
$ ./demo localhost
hostname: localhost
alternate name: localhost.localdomain
alternate name: localhost4
alternate name: localhost4.localdomain4
alternate name: localhost.localdomain
alternate name: localhost6
alternate name: localhost6.localdomain6
address type: AF_INET
address length: 4
host->h_addr_list[0]=0x1c1a150
address: 127.0.0.1
host->h_addr_list[1]=0x1c1a220
address: 127.0.0.1hostname: localhost
alternate name: localhost.localdomain
alternate name: localhost6
alternate name: localhost6.localdomain6
address type: AF_INET6
address length: 16
host->h_addr_list[0]=0x1c1b5e0
address: ::1【运行结果2】
$ ./demo 127.0.0.1
hostname: 127.0.0.1
address type: AF_INET
address length: 4
host->h_addr_list[0]=0x1e1d150
address: 127.0.0.1【运行结果3】
$ ./demo ::1
hostname: ::1
address type: AF_INET6
address length: 16
host->h_addr_list[0]=0xd00560
address: ::1

结果分析

从运行结果可以知道,使用 gethostbyname 函数返回的 hostent 结构体对象指针,它保存的是IPv4格式的网络地址信息;而使用 gethostbyname2 函数返回的 hostent 结构体对象指针,它保存的是IPv6格式的网络地址信息。

同时可以看到,这两个函数传入的字符串信息不仅可以是主机名或域名,还可以是主机的IP地址。这在 IPv4 和 IPv6 混合编程环境下是很有用处的,即通过传入的IP地址字符串,可以判断出这是 IPv4 格式地址还是 IPv6 格式地址。


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部