C++如何读取系统网络连接状态?(/proc/net/tcp或GetExtendedTcpTable)

1次阅读

linux下读/proc/net/tcp常读不到established连接,因其仅含ipv4连接且状态为十六进制码(01表示established),非字符串;ipv6需查/tcp6,地址为小端十六进制,解析须用sscanf按宽提取并转字节序。

C++如何读取系统网络连接状态?(/proc/net/tcp或GetExtendedTcpTable)

Linux 下直接读 /proc/net/tcp 为什么常读不到 ESTABLISHED 连接?

因为 /proc/net/tcp 默认只显示 IPv4 的 TCP 连接,且状态字段是十六进制数(如 01 表示 ESTABLISHED),不是字符串。很多脚本或 c++ 程序直接用 std::String::find("ESTABLISHED") 肯定失败。

  • 状态码需查内核头文件:01 = ESTABLISHED,06 = TIME_WAIT,0A = LISTEN —— 不是十进制
  • 每行格式固定:索引、本地地址:端口、远端地址:端口、状态、队列、超时、重传、UID、超时时间、inode;地址是十六进制小端(如 0100007F:0016 表示 127.0.0.1:22)
  • IPv6 连接在 /proc/net/tcp6,不能忽略;若只读 tcp,会漏掉所有 IPv6 连接
  • 解析时跳过第一行(表头),但有些发行版(如 Alpine)可能没表头,得靠字段数判断是否有效行

C++ 解析 /proc/net/tcp 的最小安全读法

别用 std::ifstream 一行行 getline 后再 stringstream 拆——字段含空格又不严格对齐,容易错位。推荐用 sscanfstd::from_chars(C++17)按固定宽度提取。

  • open("/proc/net/tcp", O_RDONLY),用 read() 一次性读入缓冲区,避免 /proc 文件被并发修改导致半截数据
  • 对每行调用:sscanf(line, "%*d: %64[^:]:%X %64[^:]:%X %X", local_addr, &local_port, remote_addr, &remote_port, &state)
  • local_addrremote_addr 是 char 数组,需手动转字节序(inet_ntop(AF_INET, &addr_num, buf, sizeof(buf))
  • UID 字段在第 8 列(从 0 开始数),但某些容器环境 UID 为 0 或 -1,不能直接用于权限判断

windows 上调 GetExtendedTcpTable 为什么返回 ERROR_INSUFFICIENT_BUFFER

这不是错误,是正常流程。Windows 要求你先传 NULL 获取所需缓冲区大小,再分配内存重试。很多人卡在这步,反复传 NULL 或直接 malloc 固定大小就崩了。

  • 第一次调用:DWORD size = 0; GetExtendedTcpTable(NULL, &size, FALSE, AF_INET, TCP_TABLE_OWNER_PID_ALL, 0) → 返回 ERROR_INSUFFICIENT_BUFFERsize 被设为真实需要字节数
  • 第二次:用 malloc(size) 分配,再传入该指针;注意结构体MIB_TCPROW_OWNER_PID 数组,首 DWORD 是条目数
  • 必须用 AF_INET + TCP_TABLE_OWNER_PID_ALL 才能拿到 PID;若用 TCP_TABLE_BASIC_ALL,PID 字段无效
  • 返回的 PID 可能为 0(系统进程),需用 OpenProcess + QueryFullProcessImageName 才能拿到进程名,且要开 PROCESS_QUERY_LIMITED_INFORMATION 权限

跨平台检测“是否有活跃外连”该怎么写才可靠?

别指望一个函数覆盖所有场景。Linux 和 Windows 的语义差异大:Linux 的 /proc 是快照,Windows 的 GetExtendedTcpTable 是内核查询,但都可能漏掉 udp、本地回环、或刚关闭的连接。

立即学习C++免费学习笔记(深入)”;

  • 如果目标只是“当前有无到公网 IP 的 ESTABLISHED 连接”,Linux 优先扫 /proc/net/{tcp,tcp6},过滤掉 127.0.0.0/8::110.0.0.0/8 等私有网段
  • Windows 上别只看 dwState == MIB_TCP_STATE_ESTAB,还得检查 dwRemoteAddr != 0 且非内网地址(需自己实现 CIDR 判断)
  • UDP 没连接状态,/proc/net/udp*GetExtendedUdpTable 只能看监听端口,无法判断“是否发过包”
  • 真正健壮的做法:加一层心跳检测——比如尝试 connect 一个已知稳定的公网地址(如 1.1.1.1:53),超时即认为网络不通;/proc 或 API 结果只是辅助佐证

路径、权限、地址族、状态编码、缓冲区生命周期——这些地方一松动,程序就在不同机器上表现不一致。尤其是容器里 /proc 是挂载的,Windows 上权限弹窗会卡住后台服务。

text=ZqhQzanResources