您的位置:

使用getnameinfo实现网络编程中IP地址和域名转换的详解

一、getnameinfo函数概述

getnameinfo是网络编程中常用的函数之一,它的作用是将IP地址和端口转换成可读的主机名和服务名。

该函数定义于sys/socket.h和netdb.h头文件中,它的原型如下:

int getnameinfo(const struct sockaddr *restrict sa, socklen_t salen,
                char *restrict host, socklen_t hostlen,
                char *restrict serv, socklen_t servlen, int flags);

其中,参数sa是一个指向要转换的sockaddr结构体的指针,salen是这个结构体的长度;

参数host和serv分别是主机名和服务名的存放缓冲区,hostlen和servlen是它们的长度;

参数flags的使用可以决定返回结果的格式,常用的值有NI_NUMERICHOST和NI_NUMERICSERV。

二、将IP地址转换为主机名

在使用getnameinfo函数将IP地址转换为主机名时,我们需要先将IP地址存放到一个sockaddr结构体中。

下面是具体的转换过程:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>

int main(int argc, char *argv[]) {
    struct addrinfo hints, *res;
    char ip_str[INET6_ADDRSTRLEN];
    void *addr;
    if (argc != 2) {
        fprintf(stderr,"usage: %s ip\n", argv[0]);
        exit(1);
    }
    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_UNSPEC; // Allow IPv4 or IPv6 
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = AI_PASSIVE; // For wildcard IP address 
    if (getaddrinfo(argv[1], NULL, &hints, &res) != 0) {
        fprintf(stderr, "getaddrinfo error\n");
        exit(1);
    }
    // convert address to a string 
    if (res->ai_family == AF_INET) { // IPv4 
        struct sockaddr_in *ipv4 = (struct sockaddr_in *)res->ai_addr;
        addr = &(ipv4->sin_addr);
    } else { // IPv6 
        struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)res->ai_addr;
        addr = &(ipv6->sin6_addr);
    }
    // convert the IP to a string and print it 
    inet_ntop(res->ai_family, addr, ip_str, sizeof(ip_str));
    printf("IP address: %s\n", ip_str);
    // convert IP to host name 
    char host[NI_MAXHOST];
    if (getnameinfo(res->ai_addr, res->ai_addrlen, host, NI_MAXHOST, NULL, 0, 0) != 0) {
        fprintf(stderr, "getnameinfo error\n");
        exit(1);
    }
    printf("Host name : %s\n", host);
    freeaddrinfo(res);
    return 0;
}

在这个例子中,我们通过命令行参数传入了需要转换的IP地址,然后使用getaddrinfo函数将这个地址转换为一个sockaddr结构体。

接着,我们使用inet_ntop函数将IP地址转换为一个字符串,并打印出来。

最后,我们使用getnameinfo函数将该IP地址转换为主机名,并打印出来。

需要注意的是,在使用getnameinfo函数时,可以通过flags参数来控制输出的格式。

三、将主机名转换为IP地址

和将IP地址转换为主机名类似,我们先需要用getaddrinfo函数将主机名转换为一个sockaddr结构体,然后再调用getnameinfo函数来将它转换为IP地址。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>

int main(int argc, char *argv[]) {
    struct addrinfo hints, *res;
    char ip_str[INET6_ADDRSTRLEN];
    if (argc != 2) {
        fprintf(stderr,"usage: %s hostname\n", argv[0]);
        exit(1);
    }
    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    if (getaddrinfo(argv[1], NULL, &hints, &res) != 0) {
        fprintf(stderr, "getaddrinfo error\n");
        exit(1);
    }
    // convert the socket address to a string and print it 
    int i;
    for(i = 1, res = res->ai_next; res; res = res->ai_next, ++i) {
        void *addr;
        char* ipver;
        if (res->ai_family == AF_INET) { // IPv4 
            struct sockaddr_in *ipv4 = (struct sockaddr_in *)res->ai_addr;
            addr = &(ipv4->sin_addr);
            ipver = "IPv4";
        } else { // IPv6 
            struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)res->ai_addr;
            addr = &(ipv6->sin6_addr);
            ipver = "IPv6";
        }
        inet_ntop(res->ai_family, addr, ip_str, sizeof ip_str);
        printf("%s address %d: %s\n", ipver, i, ip_str);
    }
    freeaddrinfo(res);
    return 0;
}

在这个例子中,我们通过命令行参数传入了需要转换的主机名,然后使用getaddrinfo函数将该主机名转换为一个sockaddr结构体。

然后,我们遍历所有的结果,将其中的IP地址转换为字符串,并打印出它们的版本(IPv4或IPv6)和序号。

四、小结

getnameinfo是网络编程中一个非常重要的函数,它可以方便地将IP地址和端口转换为可读的主机名和服务名。

在使用该函数时需要注意一些细节,比如flags参数可以控制输出的格式。通过这篇文章的介绍,相信读者已经对getnameinfo函数有了更深入的了解。