概述
一 域名和网络IP地址结构体 - struct hostent
struct hostent 结构体定义如下:
#include <netdb.h>
struct hostent
{
char *h_name; //主机名,即官方域名
char **h_aliases; //主机所有别名构成的字符串数组,同一IP可绑定多个域名
int h_addrtype; //主机IP地址的类型,例如IPV4(AF_INET)还是IPV6
int h_length; //主机IP地址占用字节长度,IPV4地址为4,IPV6地址则为16
char **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 <netdb.h>
extern int h_errno;
struct hostent * gethostbyname(cosnt char *hostname);
//参数说明
//hostname: 指向存放域名字符串的内存地址
//返回值: 成功时返回hostent结构体指针,失败时返回NULL
#include <sys/socket.h>
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 结构体变量地址值,失败时返回 NULL
void 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 <netdb.h>
#include <sys/socket.h>
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 <stdio.h>
#include <stdlib.h>
#include <netdb.h>
#include <arpa/inet.h> //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 aliases
printf("hostname: %sn", host->h_name);
for(i=0; host->h_aliases[i]; i++)
{
printf("alternate name: %sn", host->h_aliases[i]);
}
// print address type and length
if(host->h_addrtype == AF_INET)
{
printf("address type: AF_INETn");
}
else if(host->h_addrtype == AF_INET6)
{
printf("address type: AF_INET6n");
}
printf("address length: %dn", host->h_length);
//print address list
for(i=0; host->h_addr_list[i]; i++)
{
printf("host->h_addr_list[%d]=%pn", i, host->h_addr_list[i]);
printf("address: %sn", inet_ntop(host->h_addrtype, host->h_addr_list[i], addr, INET_ADDRSTRLEN));
//printf("addr: %sn", 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.1
hostname: 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网络地址格式相关的结构体有:sockaddr、sockaddr_in6、in6_addr。相关结构体的定义如下:
#include <sys/socket.h>
#include <netinet/in.h>
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 <netdb.h>
#include <sys/socket.h>
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 <stdio.h>
#include <stdlib.h>
#include <netdb.h>
#include <arpa/inet.h> //inet_ntop()
void printHostname(struct hostent *host);
int main(int argc, char *argv[])
{
if(argc != 2){
printf("Usage: %s <hostname>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 aliases
printf("hostname: %sn", host->h_name);
for(i=0; host->h_aliases[i]; i++)
{
printf("alternate name: %sn", host->h_aliases[i]);
}
// print address type and length
switch(host->h_addrtype)
{
case AF_INET:
printf("address type: AF_INETn");
break;
case AF_INET6:
printf("address type: AF_INET6n");
break;
default:
printf("address type: Unknownn");
}
printf("address length: %dn", host->h_length);
//print address list
for(i=0; host->h_addr_list[i]; i++)
{
printf("host->h_addr_list[%d]=%pn", i, host->h_addr_list[i]);
printf("address: %sn", inet_ntop(host->h_addrtype, host->h_addr_list[i], addr, INET_ADDRSTRLEN));
//printf("addr: %sn", 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.1
hostname: 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 格式地址。
最后
以上就是清脆豌豆为你收集整理的Linux网络编程 - 主机域名和IP地址的转换(IPv4 & IPv6)的全部内容,希望文章能够帮你解决Linux网络编程 - 主机域名和IP地址的转换(IPv4 & IPv6)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复