使用getifaddrs获取Linux CAN接口报文统计数据失败如何解决
问题原因
你遇到的是Debian Jessie对应glibc 2.19版本的getifaddrs实现缺陷:CAN总线接口的地址族为AF_CAN,旧版glibc不会为这类非常规网络接口生成AF_PACKET类型的地址条目,所以你拿到的ifa_addr为NULL,无法通过该接口获取统计数据。而ifconfig是通过更底层的rtnetlink接口获取统计数据,不受该问题影响。
正确获取CAN接口统计的两种方法
方法1:读取sysfs文件(推荐,实现简单)
Linux下所有网络接口的统计数据都暴露在sysfs的/sys/class/net/<接口名>/statistics/目录下,每个统计项对应一个纯文本文件,直接读取即可,不需要依赖系统调用兼容性:
- 收包数:
/sys/class/net/can0/statistics/rx_packets - 发包数:
/sys/class/net/can0/statistics/tx_packets - 接收丢包数:
/sys/class/net/can0/statistics/rx_dropped - 发送错误数:
/sys/class/net/can0/statistics/tx_errors - 接收字节数:
/sys/class/net/can0/statistics/rx_bytes - 发送字节数:
/sys/class/net/can0/statistics/tx_bytes
C++读取示例:
#include <fstream> #include <string> #include <iostream> unsigned long read_sysfs_stat(const std::string& iface, const std::string& stat) { std::string path = "/sys/class/net/" + iface + "/statistics/" + stat; std::ifstream file(path); unsigned long val = 0; if (file.is_open()) { file >> val; file.close(); } return val; } int main() { std::cout << "can0 rx packets: " << read_sysfs_stat("can0", "rx_packets") << std::endl; std::cout << "can0 dropped: " << read_sysfs_stat("can0", "rx_dropped") << std::endl; std::cout << "can0 tx packets: " << read_sysfs_stat("can0", "tx_packets") << std::endl; return 0; }
方法2:使用rtnetlink套接字
如果需要批量获取多个接口的统计数据,可以使用netlink套接字和内核交互获取rtnl_link_stats结构体,和ifconfig的实现逻辑一致,示例代码如下:
#include <asm/types.h> #include <linux/if_link.h> #include <linux/rtnetlink.h> #include <sys/socket.h> #include <net/if.h> #include <cstring> #include <cstdio> #include <cstdlib> #include <unistd.h> int main() { int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (sock < 0) { perror("socket"); return 1; } struct { struct nlmsghdr nh; struct ifinfomsg ifi; char buf[1024]; } req; memset(&req, 0, sizeof(req)); req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT; req.nh.nlmsg_type = RTM_GETLINK; req.ifi.ifi_family = AF_UNSPEC; if (send(sock, &req, req.nh.nlmsg_len, 0) < 0) { perror("send"); close(sock); return 1; } char buf[8192]; ssize_t len = recv(sock, buf, sizeof(buf), 0); if (len < 0) { perror("recv"); close(sock); return 1; } for (struct nlmsghdr* nh = (struct nlmsghdr*)buf; NLMSG_OK(nh, len); nh = NLMSG_NEXT(nh, len)) { if (nh->nlmsg_type == NLMSG_DONE) break; if (nh->nlmsg_type != RTM_NEWLINK) continue; struct ifinfomsg* ifi = (struct ifinfomsg*)NLMSG_DATA(nh); char ifname[IFNAMSIZ]; if_indextoname(ifi->ifi_index, ifname); struct rtattr* rta = IFLA_RTA(ifi); int rtl = RTM_PAYLOAD(nh); for (; RTA_OK(rta, rtl); rta = RTA_NEXT(rta, rtl)) { if (rta->rta_type == IFLA_STATS) { struct rtnl_link_stats* stats = (struct rtnl_link_stats*)RTA_DATA(rta); printf("接口 %s 收包数: %u 丢包数: %u 发包数: %u\n", ifname, stats->rx_packets, stats->rx_dropped, stats->tx_packets); } } } close(sock); return 0; }
内容的提问来源于stack exchange,提问作者hoz
相关产品推荐
相关产品推荐

