我是靠谱客的博主 清脆豌豆,最近开发中收集的这篇文章主要介绍Linux网络编程 - 主机域名和IP地址的转换(IPv4 & IPv6),觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

一  域名和网络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网络地址格式相关的结构体有:sockaddrsockaddr_in6in6_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)所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(43)

评论列表共有 0 条评论

立即
投稿
返回
顶部