应使用原始套接字构造icmp包或tcp连接探测:windows需管理员权限并调用wsastartup,linux需cap_net_raw权限,macos则宜改用非特权tcp探测,并通过select控制超时、避免阻塞。

用 ICMP 发包判断连接是否正常,但别直接调 ping 命令
Windows 下最直接的方式不是调用系统 ping 命令(比如 system("ping -n 1 -w 1000 8.8.8.8")),因为不可靠、难捕获结果、跨平台差。真正可控的做法是自己构造 ICMP Echo Request 包,用原始套接字发送并等待响应。
注意:这需要管理员权限(Windows)或 root 权限(Linux),否则 socket(AF_INET, SOCK_RAW, IPPROTO_ICMP) 会失败,报错 WSAEACCES 或 Operation not permitted。
- Windows 上必须以管理员身份运行程序;启用
SeCreateRawSocketPrivilege(通常默认有) - Linux 需要
sudo setcap cap_net_raw+ep ./your_program,或改用root启动 - macOS 默认禁用原始 ICMP 套接字,
SOCK_RAW创建会失败,得换方案(见下一条)
macOS 和部分 Linux 发不了 ICMP?改用 connect() + TCP 探测
当 ICMP 不可用时,更通用的替代方案是尝试建立一个轻量级 TCP 连接(比如连目标 IP 的 80 或 443 端口),靠连接是否成功来间接判断网络可达性——它不测“是否能 ping 通”,而是测“是否能建立三层以上通信”。
优点:无需特权、全平台兼容、代码简洁;缺点:不能区分是网络层不通还是目标端口关闭/防火墙拦截。
立即学习“C++免费学习笔记(深入)”;
- 用
socket(AF_INET, SOCK_STREAM, 0)创建 TCP 套接字 - 设置非阻塞模式(避免卡死),调用
connect() - 用
select()或poll()等待最多 1–2 秒,检查是否就绪且getsockopt(... SO_ERROR ...)返回 0 - 别连
localhost或已知本地服务,要连公网稳定地址如8.8.8.8:53(DNS UDP 更快,但 TCP 更稳)
WSAStartup 忘初始化?sendto 失败前先查这个
Windows 下所有 Winsock 函数调用前必须调 WSAStartup,否则 socket() 返回 INVALID_SOCKET,sendto() 直接崩溃或返回 SOCKET_ERROR 且 WSAGetLastError() 是 WSANOTINITIALISED。
常见疏漏点:
- 只在 main 开头调一次,但多线程里没做同步保护(其实
WSAStartup是线程安全的,可多次调,但WSACleanup只能最后调一次) - 忘记检查返回值:
if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0) - 用了高版本 API(如
WSASend)却传了MAKEWORD(1,1),导致某些扩展函数不可用
超时控制别只靠 setsockopt(SO_RCVTIMEO)
对 ICMP 探测,仅设接收超时(SO_RCVTIMEO)不够——如果发包阶段就被丢弃(比如路由不可达、TTL=0),你根本等不到响应,recvfrom 会一直阻塞(除非也设了发送超时,但 Windows 不支持 SO_SNDTIMEO for ICMP)。
更稳妥的做法是用事件驱动或轮询:
- Windows:用
WSAEventSelect()+WSAWaitForMultipleEvents()控制总耗时 - 跨平台:用
select(),把 socket 加入readfds,timeout 设为期望总超时(如 1500ms) - 别在循环里反复
recvfrom不清缓冲区,ICMP 响应可能延迟到达,被下次探测覆盖
实际项目中,三次探测取两次成功才算“通”,比单次更抗抖动。但别盲目重试——第一次失败后间隔 300ms 再发,避免被当成扫描流量限速。











