最直接有效的网络连通性检测方式是用非阻塞connect()向8.8.8.8:53或1.1.1.1:443发起TCP连接,通过真实握手暴露路由、网关、防火墙等实际问题,避免getaddrinfo()仅查缓存或InternetGetConnectedState()依赖系统假在线状态的误判。

用 connect() 尝试连接已知可靠地址判断连通性
最直接有效的方式不是查本地网卡状态,而是模拟一次真实网络行为:向一个高可用的公共服务(如 DNS 或 HTTPS 服务)发起 TCP 连接。操作系统内核会真实走路由、ARP、SYN 握手流程,失败时能暴露真实问题(如网关不通、DNS 不可达、防火墙拦截)。
关键点:connect() 在非阻塞 socket 下可快速返回(配合 select() 或 poll() 设定超时),避免卡住主线程;目标地址推荐用 8.8.8.8:53(Google DNS,UDP 端口虽不需握手,但 TCP 53 仍可测)或 1.1.1.1:443(Cloudflare,HTTPS 常开)。
- 不要用
localhost或127.0.0.1—— 它只测本机协议栈,不反映物理链路 - 避免依赖 DNS 解析 —— 直接使用 IP,否则 DNS 失败会被误判为“无网络”
- 超时建议设为 2–3 秒:太短易误判(尤其移动网络首包延迟高),太长影响响应
为什么 getaddrinfo() 成功不代表有网
getaddrinfo() 只查本地 hosts、DNS 缓存或系统 resolver 配置,不发任何网络包。即使 DNS 服务器宕机、网线拔掉,只要缓存里有记录或 hosts 文件写了映射,它照样返回 0。
典型误用场景:
- 调用
getaddrinfo("google.com", "443", &hints, &result)返回成功,但后续connect()失败 - 公司内网禁用外网 DNS,但 hosts 里配了测试域名 →
getaddrinfo()成功,实际无法出网
结论:它只能辅助验证 DNS 配置是否加载正常,不能替代连通性探测。
Windows 上用 InternetGetConnectedState() 的局限性
这个 WinINet API 看似方便,但它只检查系统是否“认为自己在线”——依据是注册表键 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\NlaSvc\Parameters\Internet\IsConstrained 和 NLA 服务上报的状态,和真实网络无关。
- 插着网线但交换机断电?它可能仍返回 “connected”
- WiFi 显示已连接但认证失败(如企业 802.1X 超时)?它大概率返回 “disconnected”,但有时又不更新
- 不支持自定义目标、超时、代理环境,纯黑盒判断
仅适合 UI 层快速灰显/启用按钮,绝不可用于业务逻辑决策(比如自动重连、降级策略)。
跨平台最小可行代码片段(Linux/macOS/Windows)
以下逻辑在所有主流平台均可编译运行(需链接 -lws2_32 on Windows):
int check_network(const char* ip, const char* port) {
struct addrinfo hints = {}, *res;
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
if (getaddrinfo(ip, port, &hints, &res) != 0) return -1;
<pre class="brush:php;toolbar:false;">int sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (sock == -1) { freeaddrinfo(res); return -1; }
// 设置非阻塞
#ifdef _WIN32
u_long mode = 1; ioctlsocket(sock, FIONBIO, &mode);
#else
int flags = fcntl(sock, F_GETFL, 0); fcntl(sock, F_SETFL, flags | O_NONBLOCK);
#endif
int ret = connect(sock, res->ai_addr, res->ai_addrlen);
bool connected = (ret == 0) || (errno == EINPROGRESS || errno == WSAEWOULDBLOCK);
if (connected) {
fd_set writefds; FD_ZERO(&writefds); FD_SET(sock, &writefds);
struct timeval tv = {.tv_sec = 3};
ret = select(sock + 1, NULL, &writefds, NULL, &tv);
connected = (ret > 0 && FD_ISSET(sock, &writefds));
}
close(sock); freeaddrinfo(res);
return connected ? 0 : -1;} // 调用:check_network("8.8.8.8", "53");
注意:Windows 下必须初始化 Winsock(WSAStartup()),且 close() 要换成 closesocket() —— 这些细节容易漏,一漏就 crash 或始终返回失败。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











