c++怎么判断网络连接是否正常_c++ ping功能实现【实战】

3次阅读

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

c++怎么判断网络连接是否正常_c++ ping功能实现【实战】

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) 会失败,报错 WSAEACCESOperation 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 的 80443 端口),靠连接是否成功来间接判断网络可达性——它不测“是否能 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_SOCKETsendto() 直接崩溃或返回 SOCKET_ERRORWSAGetLastError()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 再发,避免被当成扫描流量限速。

text=ZqhQzanResources