如何用C/C++编程检测Windows/Linux系统的活跃本地IP地址
检测Windows和Linux系统中的活跃本地IP(无互联网环境)
核心逻辑
活跃IP的本质是系统路由表中默认路由绑定的网卡对应的IP——系统会依据路由表选择数据包发送的网卡,该网卡的IP就是当前用于通信的活跃IP。
Linux平台实现
Linux下通过解析/proc/net/route路由表文件,找到默认路由对应的网卡,再匹配网卡获取IP。
步骤
- 读取
/proc/net/route,筛选目标为00000000的默认路由条目。 - 提取该条目的网卡名称(如
eth0、wlan0)。 - 用
getifaddrs()遍历网卡列表,匹配名称后获取对应IPv4地址(排除回环地址127.0.0.1)。
示例代码
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <ifaddrs.h> #include <netinet/in.h> #include <arpa/inet.h> // 获取默认路由绑定的网卡名称 char* get_default_route_interface() { FILE* fp = fopen("/proc/net/route", "r"); if (!fp) return NULL; char line[256], iface[32], dest[16]; fgets(line, sizeof(line), fp); // 跳过表头 while (fgets(line, sizeof(line), fp)) { sscanf(line, "%s %s", iface, dest); if (strcmp(dest, "00000000") == 0) { fclose(fp); return strdup(iface); } } fclose(fp); return NULL; } // 根据网卡名称获取对应IPv4地址 char* get_ip_by_interface(const char* iface) { struct ifaddrs *ifaddr, *ifa; if (getifaddrs(&ifaddr) == -1) return NULL; for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { if (ifa->ifa_addr == NULL) continue; if (ifa->ifa_addr->sa_family == AF_INET && strcmp(ifa->ifa_name, iface) == 0) { struct sockaddr_in* addr = (struct sockaddr_in*)ifa->ifa_addr; char* ip = malloc(INET_ADDRSTRLEN); inet_ntop(AF_INET, &addr->sin_addr, ip, INET_ADDRSTRLEN); freeifaddrs(ifaddr); if (strcmp(ip, "127.0.0.1") != 0) return ip; free(ip); } } freeifaddrs(ifaddr); return NULL; } int main() { char* iface = get_default_route_interface(); if (!iface) { printf("无法找到默认路由网卡\n"); return 1; } char* active_ip = get_ip_by_interface(iface); if (active_ip) { printf("活跃本地IP: %s\n", active_ip); free(active_ip); } else { printf("无法获取网卡对应IP\n"); } free(iface); return 0; }
Windows平台实现
Windows下调用系统API获取路由表,找到默认路由对应的网卡LUID,再通过网卡信息匹配获取IP。
步骤
- 调用
GetIpForwardTable2获取IPv4路由表,筛选目标前缀长度为0的默认路由。 - 提取路由条目的
InterfaceLuid标识。 - 调用
GetAdaptersAddresses遍历网卡,匹配InterfaceLuid后获取IPv4地址(排除回环)。
示例代码
#include <windows.h> #include <iphlpapi.h> #include <stdio.h> #include <stdlib.h> #pragma comment(lib, "iphlpapi.lib") // 获取默认路由绑定的网卡LUID NET_LUID get_default_route_interface_luid() { NET_LUID luid = {0}; DWORD size = 0; GetIpForwardTable2(AF_INET, NULL, &size, FALSE, 0); // 先获取缓冲区大小 MIB_IPFORWARD_TABLE2* table = (MIB_IPFORWARD_TABLE2*)malloc(size); if (!table) return luid; if (GetIpForwardTable2(AF_INET, table, &size, FALSE, 0) == NO_ERROR) { for (DWORD i = 0; i < table->NumEntries; i++) { MIB_IPFORWARD_ROW2* row = &table->Table[i]; if (row->DestinationPrefix.PrefixLength == 0 && row->InterfaceIndex != 0) { luid = row->InterfaceLuid; break; } } } free(table); return luid; } // 根据网卡LUID获取对应IPv4地址 char* get_ip_by_interface_luid(NET_LUID luid) { DWORD size = 0; GetAdaptersAddresses(AF_INET, GAA_FLAG_INCLUDE_PREFIX, NULL, NULL, &size); // 获取缓冲区大小 IP_ADAPTER_ADDRESSES* adapters = (IP_ADAPTER_ADDRESSES*)malloc(size); if (!adapters) return NULL; if (GetAdaptersAddresses(AF_INET, GAA_FLAG_INCLUDE_PREFIX, NULL, adapters, &size) == NO_ERROR) { IP_ADAPTER_ADDRESSES* adapter = adapters; while (adapter) { if (adapter->Luid.Value == luid.Value) { IP_ADAPTER_UNICAST_ADDRESS* addr = adapter->FirstUnicastAddress; while (addr) { if (addr->Address.lpSockaddr->sa_family == AF_INET) { struct sockaddr_in* ipv4 = (struct sockaddr_in*)addr->Address.lpSockaddr; char* ip = malloc(INET_ADDRSTRLEN); inet_ntop(AF_INET, &ipv4->sin_addr, ip, INET_ADDRSTRLEN); free(adapters); if (strcmp(ip, "127.0.0.1") != 0) return ip; free(ip); } addr = addr->Next; } } adapter = adapter->Next; } } free(adapters); return NULL; } int main() { NET_LUID luid = get_default_route_interface_luid(); if (luid.Value == 0) { printf("无法找到默认路由网卡\n"); return 1; } char* active_ip = get_ip_by_interface_luid(luid); if (active_ip) { printf("活跃本地IP: %s\n", active_ip); free(active_ip); } else { printf("无法获取网卡对应IP\n"); } return 0; }
注意事项
- 上述代码仅支持IPv4,如需适配IPv6,修改地址族参数为
AF_INET6并调整对应结构体即可。 - Linux下普通用户即可读取路由表,Windows下多数场景无需管理员权限。
- 若系统存在多个默认路由,可增加筛选逻辑(如优先选择metric值最小的路由)。
内容的提问来源于stack exchange,提问作者FalsinSoft
相关产品推荐
相关产品推荐

