Linux下将getaddrinfo获取的数据转换为struct hostent结构的方案咨询
实现方案
核心思路为手动分配内存构造hostent结构体,从getaddrinfo返回的链表中提取对应字段填充即可,不需要依赖旧的gethostbyname系列接口,具体实现如下:
前置调整
先修改你的hints配置,加上AI_CANONNAME标志,用于获取规范主机名填充hostent的h_name字段:
hints.ai_flags = AI_CANONNAME; // 新增这一行,要求返回规范主机名
完整的addrinfo转hostent实现代码
#include <stdlib.h> #include <string.h> #include <netdb.h> #include <arpa/inet.h> // 自定义释放构造出的hostent的函数,用法和原有freehostent对齐 void free_custom_hostent(struct hostent *ent) { if (!ent) return; // 释放主机名 free(ent->h_name); // 释放别名数组 if (ent->h_aliases) { for (int i = 0; ent->h_aliases[i]; i++) { free(ent->h_aliases[i]); } free(ent->h_aliases); } // 释放地址列表 if (ent->h_addr_list) { for (int i = 0; ent->h_addr_list[i]; i++) { free(ent->h_addr_list[i]); } free(ent->h_addr_list); } free(ent); } // 从getaddrinfo返回的结果构造hostent struct hostent *addrinfo_to_hostent(struct addrinfo *ai_list, int *err_num) { struct hostent *ent = NULL; struct addrinfo *ai = NULL; int addr_count = 0; // 先统计IPv4地址数量 for (ai = ai_list; ai; ai = ai->ai_next) { if (ai->ai_family == AF_INET) { addr_count++; } } if (addr_count == 0) { *err_num = HOST_NOT_FOUND; return NULL; } // 分配hostent结构体 ent = calloc(1, sizeof(struct hostent)); if (!ent) { *err_num = NO_RECOVERY; return NULL; } // 填充基础字段 ent->h_addrtype = AF_INET; ent->h_length = sizeof(struct in_addr); // 填充规范主机名 if (ai_list->ai_canonname) { ent->h_name = strdup(ai_list->ai_canonname); } else { // 没有拿到规范主机名的情况可以自行填充空字符串或者业务需要的默认值 ent->h_name = strdup(""); } if (!ent->h_name) { free_custom_hostent(ent); *err_num = NO_RECOVERY; return NULL; } // 填充别名数组,这里简化为空数组,如果业务需要别名可以自行扩展逻辑 ent->h_aliases = calloc(1, sizeof(char *)); if (!ent->h_aliases) { free_custom_hostent(ent); *err_num = NO_RECOVERY; return NULL; } // 填充地址列表 ent->h_addr_list = calloc(addr_count + 1, sizeof(char *)); if (!ent->h_addr_list) { free_custom_hostent(ent); *err_num = NO_RECOVERY; return NULL; } int idx = 0; for (ai = ai_list; ai; ai = ai->ai_next) { if (ai->ai_family != AF_INET) continue; ent->h_addr_list[idx] = malloc(sizeof(struct in_addr)); if (!ent->h_addr_list[idx]) { free_custom_hostent(ent); *err_num = NO_RECOVERY; return NULL; } memcpy(ent->h_addr_list[idx], &((struct sockaddr_in *)ai->ai_addr)->sin_addr, sizeof(struct in_addr)); idx++; } ent->h_addr_list[idx] = NULL; // 地址列表以NULL结尾 *err_num = 0; return ent; }
调用示例
// 你的原有getaddrinfo逻辑执行完后 if (result != 0) { // 错误处理,可通过gai_strerror(result)获取错误信息,映射到原有err_num即可 fprintf(stderr, "getaddrinfo error: %s\n", gai_strerror(result)); return; } int err_num = 0; struct hostent *server_host = addrinfo_to_hostent(p_addr, &err_num); // 这里server_host就可以直接给原有业务代码使用 // 用完记得释放,和原来的freehostent用法一致 free_custom_hostent(server_host); // 记得释放getaddrinfo分配的链表 freeaddrinfo(p_addr);
反向解析(原getipnodebyaddr场景)适配
如果是IP转主机名的场景,先调用getnameinfo拿到主机名,再用上述相同的逻辑构造hostent即可,核心代码如下:
char host_buf[NI_MAXHOST]; int ret = getnameinfo((struct sockaddr *)sa, sa_len, host_buf, sizeof(host_buf), NULL, 0, NI_NAMEREQD); if (ret == 0) { // 拿到主机名后走上述getaddrinfo+转hostent的流程即可 }
注意事项
- 上述示例默认你只需要IPv4地址,如果后续要支持IPv6可以扩展对应逻辑,调整
h_addrtype和h_length字段即可。 - 错误码使用了
<netdb.h>中定义的标准错误宏,和原有getipnodebyname的错误码兼容,不需要修改原有业务的错误判断逻辑。 - 如果业务代码依赖
h_aliases字段,可自行扩展别名收集逻辑,从addrinfo链表或者DNS返回结果中提取别名信息填充即可。
内容的提问来源于stack exchange,提问作者Apollo17
相关产品推荐
相关产品推荐

