10. 从域名获取ip

一、前言

平常网络编程中,我们常会有需要获取某一个域名ip的需要,比如百度的域名为www.baidu,com,但不知道它的ip地址,我们也没办法在代码中用socket连接上去。

所以本文封装了一个跨平台的函数用于获取对应域名的所有ip地址,方便大家使用

二、最终效果

首先演示一下封装后的效果,代码如下:

#include<iostream>
#include"domain.h"
using namespace std;
int main() {
    auto ret = GetIPAddresses("www.baidu.com");
    for (auto i : ret) {
        cout << i << endl;
    }
}

结果:

在这里插入图片描述

其中的domain.h这个头文件中封装的便是这个完整的函数,代码在后文,你可以直接将其复制粘贴到你自己的一个头文件中,然后包含使用即可。

三、原理

这个封装的函数主要用到了函数getaddrinfo,它的作用就是通过域名,来查询其对应的ip地址

其在msvc编译器的头文件ws2tcpip.h中,在g++编译器的头文件netdb.h中。

它有四个参数,依次为:

  • pNodeName:表示要解析的主机名或 IP 地址(一般为域名)。如果该值为 NULL,则将返回本地主机的地址信息。
  • pServiceName:表示服务名称或端口号。可以是一个服务名称(如 “http”),也可以是一个十进制的端口号字符串(如 “80”)。如果该值为 NULL,则表示不关心服务名称或端口号。
  • pHints:表示一些筛选条件,用于帮助获取特定类型的地址信息。它是一个 addrinfo 结构体类型,可以通过设置该结构体中的成员变量来指定筛选条件。如果该值为 NULL,则表示没有任何筛选条件。
  • ppResult:表示返回的地址信息链表。该链表中包含了符合条件的所有地址信息列表。如果函数执行成功,该链表将被填充,否则该链表将为空。

一般来说,我们只需要填写第一个参数和第四个参数即可,即:通过域名,获取ip地址结果。

不过在后面的代码中,我还用到了第三个参数,来限定条件,但大多数时候并没有什么具体的区别。

  addrinfo hints {};
  hints.ai_family = AF_UNSPEC;        // 不限定IPv4或IPv6
  hints.ai_socktype = SOCK_STREAM;    // 使用TCP协议

唯一需要注意的就是第4个参数,因为那是将要返回的查询结果,定义如下:

typedef struct addrinfo
{
    int                 ai_flags;       // AI_PASSIVE, AI_CANONNAME, AI_NUMERICHOST
    int                 ai_family;      // PF_xxx
    int                 ai_socktype;    // SOCK_xxx
    int                 ai_protocol;    // 0 or IPPROTO_xxx for IPv4 and IPv6
    size_t              ai_addrlen;     // Length of ai_addr
    char *              ai_canonname;   // Canonical name for nodename
    _Field_size_bytes_(ai_addrlen) struct sockaddr *   ai_addr;        // Binary address
    struct addrinfo *   ai_next;        // Next structure in linked list
}ADDRINFOA, *PADDRINFOA;

实际上,它就是一个包含每一个结果的链表,其中的ai_next就保存的下一个记录的地址,如果为NULL就说明后面没有了。

其中主要需要注意的就是ai_addr字段,这个里面就存放着查询到的ip地址,但其是二进制形式,我们后面还需要将其转化为点分十进制,比如127.0.0.1这种形式,方便我们查看。

至于其他字段,则都是一些描述信息,比如ai_family表示当前这个地址用的什么协议(ipv4ipv6等等),ai_socktype代表使用的是什么类型的sockettcpudp等等)

所以后面代码中使用的一个for循环来遍历这个链表:

for (addrinfo* ptr = result; ptr != NULL; ptr = ptr->ai_next)

只要它不为NULL,就说明它的后面还有值,就一直进行遍历,直到遍历所有查询到的数据。

其内部还判断了一下它是ipv4还是ipv6的地址:

switch (ptr->ai_family)
作者:余识
全部文章:0
会员文章:0
总阅读量:0
c/c++pythonrustJavaScriptwindowslinux