macOS平台下多URL DNS解析的高效并行方案优化问询
macOS 批量DNS解析高效优化方案
1. 优先用异步DNS API:getaddrinfo_a
macOS原生支持POSIX标准的getaddrinfo_a,这是最省心的单线程异步解析方案,完全不用搞多线程。
- 操作步骤:
- 先把输入的URL处理成纯域名(比如把
https://www.xxx.com/path里的域名抽出来) - 给每个域名创建
struct gaicb结构,填好域名、服务类型(随便填个80就行,不影响IP解析)和地址类型(比如指定IPv4) - 调用
getaddrinfo_a(GAI_NOWAIT, 请求列表, 数量, NULL)一次性发所有异步请求 - 用
gai_suspend等着,有请求完成就处理,提取第一个IP,然后继续等剩下的
- 先把输入的URL处理成纯域名(比如把
- 优势:单线程跑完全部请求,系统帮你处理异步逻辑,内存和CPU占用比多线程低太多,不会有大量线程空等的浪费
2. 自定义异步:kqueue + res_nsend
如果需要自己控制DNS服务器这类细节,可以用macOS的kqueue事件框架配合底层DNS函数res_nsend:
- 操作步骤:
- 初始化DNS解析器上下文
- 给每个域名构造DNS查询包,用非阻塞套接字发请求
- 把所有套接字加到kqueue里监听读事件
- 进循环,kqueue触发读事件就去读响应包,解析出第一个IP,处理完关套接字,直到所有请求搞定
- 优势:完全自己掌控流程,适合有特殊DNS需求的场景,同样单线程,资源效率拉满
关键注意点
- 一定要先把URL转成纯域名,不然
getaddrinfo会解析失败 - 要处理解析失败的情况,比如域名不存在,得对应输出标记或空值,保证结果和输入URL的顺序完全对应
- macOS自带DNS缓存,重复解析同一个域名直接拿缓存,不用自己再做缓存浪费资源
示例代码(getaddrinfo_a实现)
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <netdb.h> #include <unistd.h> #define MAX_REQUESTS 1000 int main() { // 示例域名列表,实际要从输入的URL提取域名 const char *hosts[MAX_REQUESTS] = {"www.apple.com", "www.google.com", "www.github.com"}; int total = 3; struct gaicb *reqs[MAX_REQUESTS]; struct sigevent se = {0}; // 初始化每个解析请求 for (int i = 0; i < total; i++) { reqs[i] = malloc(sizeof(struct gaicb)); memset(reqs[i], 0, sizeof(struct gaicb)); reqs[i]->gaicb_name = hosts[i]; reqs[i]->gaicb_service = "80"; reqs[i]->gaicb_hints = malloc(sizeof(struct addrinfo)); memset(reqs[i]->gaicb_hints, 0, sizeof(struct addrinfo)); reqs[i]->gaicb_hints->ai_family = AF_INET; // 只获取IPv4地址 reqs[i]->gaicb_hints->ai_socktype = SOCK_STREAM; } // 批量发起异步解析请求 int ret = getaddrinfo_a(GAI_NOWAIT, reqs, total, &se); if (ret != 0) { perror("getaddrinfo_a 调用失败"); return 1; } // 等待并处理结果 int remaining = total; while (remaining > 0) { ret = gai_suspend(reqs, total, NULL); if (ret == -1) { perror("gai_suspend 出错"); break; } for (int i = 0; i < total; i++) { if (reqs[i] == NULL) continue; int err = gai_error(reqs[i]); if (err == 0) { // 解析成功,提取第一个IPv4地址 char ip[INET_ADDRSTRLEN]; inet_ntop(AF_INET, &((struct sockaddr_in*)reqs[i]->gaicb_result->ai_addr)->sin_addr, ip, INET_ADDRSTRLEN); printf("%s -> %s\n", hosts[i], ip); // 释放资源 freeaddrinfo(reqs[i]->gaicb_result); free(reqs[i]->gaicb_hints); free(reqs[i]); reqs[i] = NULL; remaining--; } else if (err != EINPROGRESS) { // 解析失败 printf("%s -> 解析失败: %s\n", hosts[i], gai_strerror(err)); free(reqs[i]->gaicb_hints); free(reqs[i]); reqs[i] = NULL; remaining--; } } } return 0; }
内容的提问来源于stack exchange,提问作者Zohar81
相关产品推荐
相关产品推荐

