最可靠方式是用非阻塞connect()向1.1.1.1:53或8.8.8.8:53发起tcp连接,通过真实握手暴露路由、网关、防火墙等问题;需设超时2–3秒,用select()监听可写后调getsockopt()检查so_error,避免依赖ping、getaddrinfo()或internetgetconnectedstate等不可靠方法。

用 connect() 尝试连接一个稳定地址最可靠
检测“是否有网络”在 C++ 里没有标准 API,gethostbyname 或检查本地网卡状态都不可靠——前者可能 DNS 挂了但网络通,后者可能网卡 up 但路由/网关断。真正有意义的判断是:能否建立 TCP 连接。选一个高可用、低延迟、不被墙的地址(比如 1.1.1.1:53 或 8.8.8.8:53)发起非阻塞 connect() 是最贴近实际使用场景的方式。
实操建议:
- 用
socket(AF_INET, SOCK_STREAM, 0)创建 socket,设置为非阻塞(fcntl(fd, F_SETFL, O_NONBLOCK)),避免卡住 - 调用
connect()后检查返回值:-1且errno == EINPROGRESS表示正在连接;若立刻返回0,说明已连通 - 用
select()或poll()等待可写事件(连接完成时 socket 可写),再用getsockopt(..., SO_ERROR, ...)检查是否成功 - 超时建议设为 2–3 秒:太短易误判(尤其移动网络),太长影响响应
别用 ping 或 system("ping -c1 ...")
调用系统 ping 命令看似简单,但问题一堆:需要 root 权限发 ICMP 包(Linux/macOS 默认不允许普通用户)、Windows 上防火墙常拦截、容器或受限环境根本不可用。更关键的是,ICMP 通 ≠ TCP 通(例如某些网络只放行 HTTP/HTTPS)。你真正关心的是能不能发 HTTP 请求,不是能不能发 ping 包。
常见错误现象:
-
system("ping -c1 8.8.8.8")在 Docker 容器里返回1(失败),但curl https://google.com却能成功 - 代码里硬编码调用
ping,结果在 CentOS Stream 或 Alpine 镜像里直接报/bin/sh: ping: not found - 没检查
ping的退出码语义——1可能是超时,也可能是目标不可达,还可能是权限拒绝,无法区分
跨平台要考虑 Windows 的 WSAStartup 和 errno 映射
Windows 下必须先调用 WSAStartup() 初始化 Winsock,否则所有 socket 函数都失败;而且 connect() 失败时不能直接看 errno,得用 WSAGetLastError(),它和 POSIX errno 值不一致(比如连接拒绝在 Linux 是 ECONNREFUSED,Windows 是 WSAECONNREFUSED)。
兼容性要点:
- Windows 上记得在程序开头调用
WSAStartup(MAKEWORD(2,2), &wsaData),退出前调用WSACleanup() - 错误检查别写
if (errno == EINPROGRESS),Windows 下要改用宏封装,例如:#ifdef _WIN32<br> int err = WSAGetLastError();<br>#else<br> int err = errno;<br>#endif
- 不要依赖
getaddrinfo()返回的地址族顺序——有些网络只通 IPv4,有些只通 IPv6,最好遍历结果,对每个addrinfo尝试connect(),任一成功即判定为连通
HTTP HEAD 请求虽准但开销大,仅适合已有连接池的场景
用 libcurl 发个 HEAD / 到 https://cloudflare.com 确实能反映真实业务链路(DNS + TLS + HTTP),但它启动连接、做 TLS 握手、验证证书,耗时远高于纯 TCP 探测(通常多 300–800ms)。如果你只是做“开机自检”或 UI 上显示“网络状态”,没必要扛这个成本。
适用条件:
- 项目已经依赖
libcurl且有现成的连接复用逻辑 - 检测目标明确是某特定服务(如你的后端 API 地址),而非泛泛的“有没有网”
- 能接受 1–2 秒延迟,并做好超时和证书错误(
CURLE_SSL_CACERT)的处理 - 不推荐在嵌入式或资源紧张环境用——
libcurl静态链接后体积增加明显
真正容易被忽略的是:很多开发者把“能解析域名”当成“有网络”,但 getaddrinfo() 成功只说明本地 DNS 缓存或 hosts 有记录,完全不涉及网络 I/O。连通性检测必须走到 socket 层,哪怕只是连个 DNS 端口。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











