getaddrinfo神秘面纱

1. getaddrinfo能做什么

  • 名字到地址转换:解析DNS地址
  • 服务到端口转换:将IP地址和端口转换为一个socket地址

2. 重要数据结构

struct addrinfo {
    int              ai_flags;
    int              ai_family;
    int              ai_socktype;
    int              ai_protocol;
    socklen_t        ai_addrlen;
    struct sockaddr *ai_addr;
    char            *ai_canonname;
    struct addrinfo *ai_next;
};
  • ai_family
    • AF_INET:IPv4
    • AF_INET6:IPv6
    • AF_UNSPEC:协议无关
  • ai_protocol
    • IPPROTO_IP:IP协议
    • IPPROTO_IPV4:IPv4协议
    • IPPROTO_IPV6:IPv6协议
    • IPPROTO_UDP:UDP
    • IPPROTO_TCP:TCP
  • ai_socktype
    • SOCK_STREAM
    • SOCK_DGRAM
  • ai_flags
    • AI_PASSIVE:被动的,常用于bind和accept,用于server端
    • AI_CANONNAME:返回主机的规范名称
    • AI_NUMERICHOST:地址为数字

3. getaddrinfo原型

int getaddrinfo(const char *node, const char *service,
    const struct addrinfo *hints, struct addrinfo **res);
  • node可选值
    • 点分十进制IPv4地址
    • IPv6地址
    • DNS地址
      • 当node为DNS地址时,有时会遇到getaddrinfo返回过慢的问题,为什么?
        • 当getaddrinfo用来解析DNS时,底层实现使用res_nxxx系列接口(如:res_nsearch)
  • 影响getaddrinfo行为的hints.ai_flags
    • 当为AI_PASSIVE,node为NULL,server为一个端口,则返回一个0.0.0.0:port对应的socket地址,该地址可用于bind和accept
    • 当不设置AI_PASSIVE时,node不为NULL,则返回一个可用于connect、sendto或sendmsg的socket地址;如果node为NULL,则返回127.0.0.1的socket地址
  • service:设置socket地址对应的端口号。如果service为NULL,则端口默认被初始化为0
  • res:返回一个指向addrinfo结构体链表

注:node或service中任何一个可以为NULL,但不能两者都为NULL

4. 用例

#include 
#include 
#include 
#include 
#include 
#include 
#include 

int main(int argc, const char *argv[])
{
    if (argc < 2) {
        fprintf(stderr, "Please input a valid address!\n");
        return -1;
    }

    char buffer[256] = {0};
    struct addrinfo hints = {0}, *result = NULL, *tmp = NULL;
    struct sockaddr_in *addr = NULL;
    struct sockaddr_in6 *addr6 = NULL;

    hints.ai_socktype = SOCK_DGRAM;
    hints.ai_family = AF_INET;
    hints.ai_flags = 0;
    hints.ai_protocol = 0;

    int ret = getaddrinfo(argv[1], NULL, &hints, &result);
    if (ret) {
        fprintf(stderr, "errno: %d: %s\n", ret, gai_strerror(ret));
        return -1;
    }

    for (tmp = result; tmp; tmp = tmp->ai_next) {
        if (AF_INET == tmp->ai_family) {
            addr = (struct sockaddr_in*)tmp->ai_addr;
            printf("IPv4 Address: %s:%d\n", inet_ntop(AF_INET, &addr->sin_addr, buffer, sizeof(buffer)), htons(addr->sin_port));
        } else {
            addr6 = (struct sockaddr_in6*)tmp->ai_addr;
            printf("IPv6 Address: %s:%d\n", inet_ntop(AF_INET6, &addr6->sin6_addr, buffer, sizeof(buffer)), htons(addr6->sin6_port));
        }
    }
    freeaddrinfo(result);

    return 0;
}

你可能感兴趣的:(网络,linux,服务器,网络)