C语言中获取主机或者域名的IP地址

C语言中获取主机或者域名的IP地址

问题

最近有个任务,需要把公司的各个检测引擎做成docker虚拟机,使用docker-compose管理,在各个虚拟机之间使用桥模式搭建虚拟网络,各个检测引擎通过虚拟网络通讯。具体搭建过程以后给大家分享,不是这篇文章的重点。

后来发现一个问题:

之前各服务的端口都是监听在本地(127.0.0.1)的,而在服务配置之间通讯有些直接使用的TCP套接字(socket通讯),所以在客户端直接使用 connect 127.0.0.1 这个地址就可以了。但是现在需要将各个检测引擎 通过虚拟网络通讯,而各个检测引擎虚拟机的 IP地址是未知的,而各个检测引擎虚拟机的主机名是已知的,因为主机名可以通过 docker-compose 配置来设置固定主机名。

所以:

我就去修改C代码,增加主机名或者域名解析成IP地址的方法,顺便整理了给大家分享一下。以后用着了,直接copy就能用了。

talk is cheap, show me the code!!

// getaddrinfo.c
// gcc -Wall getaddrinfo.c -o test 

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

/**
 * @brief 给定一个主机名或者域名,把主机名能解析到的IPv4和IPv6给出来
 *
 * @param hostname 主机名/域名
 * @param (* ip_list 解析结果,是一个数组,内部有 申请空间,如果不为空,需要free
 *
 * @return 返回解析到的IP地址的数量
 */
int get_ip_by_hostname( char *hostname, char (* (* ip_list) )[INET6_ADDRSTRLEN]) {
    
    struct addrinfo hints; // 设置查询条件
    struct addrinfo *res;  // 指向结果链表的指针
    int status;
    int count = 0 ;        // 解析到的 ip 数量
    char ipstr[INET6_ADDRSTRLEN] ;
    char (*ip_rslt)[INET6_ADDRSTRLEN] = NULL ; 

    // 设置查询条件
    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_UNSPEC;     // 支持 IPv4 和 IPv6
    hints.ai_socktype = SOCK_STREAM; // TCP 流式套接字

    // 查询 host 、 DNS 等
    if ((status = getaddrinfo(hostname, NULL, &hints, &res)) != 0) {
        fprintf(stderr, "getaddrinfo error: %s\n", gai_strerror(status));
        return -1 ; 
    }

    struct addrinfo *p;
    for (p = res; p != NULL; p = p->ai_next) {
        void *addr;

        // 获取地址结构
        if (p->ai_family == AF_INET) { // IPv4
            struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr;
            addr = &(ipv4->sin_addr);
        } else { // IPv6
            struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr;
            addr = &(ipv6->sin6_addr);
        }
        ++count ;

        // 将网络地址转换为字符串
        inet_ntop(p->ai_family, addr, ipstr, sizeof(ipstr));

        // 申请空间,解析数据
        ip_rslt = (char (*) [INET6_ADDRSTRLEN]) realloc (ip_rslt, count* INET6_ADDRSTRLEN) ;
        snprintf(ip_rslt[count-1], INET6_ADDRSTRLEN, "%s", ipstr ) ;
    }
    *ip_list = ip_rslt ;

    // 释放资源
    freeaddrinfo(res);

    return count;

}


/**
 * @brief 测试
 *
 * @return 
 */
int main() {
    int count = 0 ;
    char (*ip_rslt)[INET6_ADDRSTRLEN] = NULL ; 

    // 测试一
    char * hostname = "node9" ;
    count = get_ip_by_hostname(hostname, &ip_rslt) ;
    printf("IP addresses for %s:\n", hostname);
    for( int i = 0  ; i < count ; ++i ) {
        printf("  %d: %s\n", i+1, ip_rslt[i]);

    }
    if(ip_rslt) free(ip_rslt) ;
    ip_rslt = NULL ;

    // 测试二
    hostname = "localhost" ;
    count = get_ip_by_hostname(hostname, &ip_rslt) ;
    printf("IP addresses for %s:\n", hostname);
    for( int i = 0  ; i < count ; ++i ) {
        printf("  %d: %s\n", i+1, ip_rslt[i]);

    }
    if(ip_rslt) free(ip_rslt) ;
    ip_rslt = NULL ;

    // 测试三
    hostname = "www.163.com" ;
    count = get_ip_by_hostname(hostname, &ip_rslt) ;
    printf("IP addresses for %s:\n", hostname);
    for( int i = 0  ; i < count ; ++i ) {
        printf("  %d: %s\n", i+1, ip_rslt[i]);

    }
    if(ip_rslt) free(ip_rslt) ;
    ip_rslt = NULL ;
}

测试

其中 主机 node9 是在 /etc/hosts 中配置的。

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注