|
|
VC++ (MFC) 中,将域名转换为 IP 地址
在 VC++ (MFC) 中,将域名转换为 IP 地址最标准、现代的方法是使用 Winsock 2 API 中的 `getaddrinfo` 函数。它比旧的 `gethostbyname` 更安全且支持 IPv6。
这是一个封装好的静态辅助函数,你可以直接添加到你的 `CRDPManagerDlg` 类中或者作为一个独立的工具函数使用。
### 代码实现
- #include <winsock2.h>
- #include <ws2tcpip.h>
- // 需要链接库: Ws2_32.lib
- #pragma comment(lib, "Ws2_32.lib")
复制代码- // 将域名转换为 IP 地址字符串
- // 参数:
- // strDomain: 域名 (例如 "www.google.com" 或 "localhost")
- // 返回值:
- // 解析成功返回 IP 地址字符串 (例如 "142.250.1.100")
- // 解析失败返回空字符串 ""
- CString DomainToIP(CString& strDomain)
- {
- // 确保 Winsock 已初始化 (通常在 App InitInstance 中做,这里为了保险检查一下)
- // 如果你的程序其他地方已经初始化了 WSAStartup,可以去掉这部分
- WSADATA wsaData;
- if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
- return _T("");
- }
- // CString 转为 ANSI 字符串 (getaddrinfo 需要 char*)
- CT2A asciiDomain(strDomain);
- // ---------------------------------------------------------
- // 1. 判断是否已经是 IP 地址
- // ---------------------------------------------------------
- struct addrinfo hintsCheck = { 0 };
- hintsCheck.ai_flags = AI_NUMERICHOST; // 关键标志:仅接受数字地址字符串
- hintsCheck.ai_family = AF_UNSPEC; // 支持 IPv4 和 IPv6
- struct addrinfo* resultCheck = NULL;
- // 如果 getaddrinfo 返回 0,说明它是一个有效的 IP 字符串
- if (getaddrinfo(asciiDomain, NULL, &hintsCheck, &resultCheck) == 0)
- {
- freeaddrinfo(resultCheck);
- // 是 IP,直接返回原值
- return strDomain;
- }
- // ---------------------------------------------------------
- // 2. 是域名,执行 DNS 解析
- // ---------------------------------------------------------
- struct addrinfo hints;
- struct addrinfo* result = NULL;
- struct addrinfo* ptr = NULL;
- ZeroMemory(&hints, sizeof(hints));
- hints.ai_family = AF_UNSPEC; // 允许 IPv4 或 IPv6
- hints.ai_socktype = SOCK_STREAM; // TCP
- hints.ai_protocol = IPPROTO_TCP;
-
- // 执行 DNS 查询
- DWORD dwRetval = getaddrinfo(asciiDomain, NULL, &hints, &result);
- if (dwRetval != 0) {
- // 解析失败
- WSACleanup();
- return _T("");
- }
- CString strIP = _T("");
- // 遍历结果链表,优先找 IPv4
- for (ptr = result; ptr != NULL; ptr = ptr->ai_next) {
- void* addr;
- char ipstr[INET6_ADDRSTRLEN];
- if (ptr->ai_family == AF_INET) { // IPv4
- struct sockaddr_in* ipv4 = (struct sockaddr_in*)ptr->ai_addr;
- addr = &(ipv4->sin_addr);
- // 转换网络字节序为字符串
- inet_ntop(ptr->ai_family, addr, ipstr, sizeof(ipstr));
- strIP = ipstr;
- break; // 找到一个 IPv4 就停止
- }
- else if (ptr->ai_family == AF_INET6) { // IPv6
- // 如果还没找到 IP,先存个 IPv6 备用
- if (strIP.IsEmpty()) {
- struct sockaddr_in6* ipv6 = (struct sockaddr_in6*)ptr->ai_addr;
- addr = &(ipv6->sin6_addr);
- inet_ntop(ptr->ai_family, addr, ipstr, sizeof(ipstr));
- strIP = ipstr;
- }
- }
- }
- freeaddrinfo(result); // 释放内存
- // 注意:如果你的程序是长期运行的,不要在这里调用 WSACleanup()
- // 应该在程序退出时统一调用。如果只是临时用一下,可以取消注释下面这行。
- // WSACleanup();
- return strIP;
- }
复制代码
### 使用示例
- CString strDomain = _T("www.baidu.com");
- CString strIP = DomainToIP(strDomain);
- if (!strIP.IsEmpty()) {
- AfxMessageBox(_T("解析成功: ") + strIP);
- } else {
- AfxMessageBox(_T("解析失败"));
- }
复制代码
### 注意事项
1. **Winsock 初始化**:确保你的应用程序在启动时(例如在 `CMyApp::InitInstance` 中)调用了 `WSAStartup`,并在退出时调用了 `WSACleanup`。如果只在这个函数里调用,频繁初始化/清理会影响性能。
2. **Unicode/ANSI**:代码中使用了 `CT2A` 宏,这使得它在 Unicode (默认) 和多字节字符集项目配置下都能正常工作。
3. **IPv4 优先**:代码逻辑是优先返回 IPv4 地址。如果域名只有 IPv6 地址,它也会返回 IPv6 地址。
|
|