如何使用C++捕获到达指定端口的IP数据包
核心问题说明
你当前的实现逻辑和需求不匹配,普通TCP服务器根本无法读取原始以太网数据包,具体问题如下:
- 普通IPv6 TCP套接字只能接收系统TCP/IP协议栈校验通过、且完成完整TCP握手后递交给对应端口的应用层数据,不仅拿不到以太网帧头、IP头、TCP头等原始包结构,只要回放的pcap包不符合以下任意一个条件,都会被协议栈直接丢弃,不会送到你的应用:
- 数据包目标IP是当前监听的IPv6回环地址
::1 - 数据包是TCP协议且目标端口为42557
- pcap中包含完整的TCP三次握手流程,能和你的TCP服务器成功建立连接
- 数据包目标IP是当前监听的IPv6回环地址
- Wireshark能抓到包是因为它使用
libpcap/Npcap这类抓包库,通过原始套接字直接从网卡驱动层面读取所有流量,绕过了系统TCP/IP协议栈的常规处理逻辑。
关于是否需要修改数据包目标IP为localhost:如果你坚持用TCP服务器的逻辑实现,就必须把pcap中数据包的目标IP改为IPv4127.0.0.1或者IPv6::1,且保证pcap包含完整TCP握手流程,否则协议栈不会处理lo接口上目标IP非本地回环地址的流量。
正确实现方案
要读取原始以太网数据包,直接用和Wireshark同底层的libpcap库实现即可,无需走TCP监听的逻辑,步骤如下:
- 安装对应平台的libpcap开发依赖(Linux下是
libpcap-dev,Windows下用Npcap SDK),C可直接调用其C API,也可以用封装好的PcapPlusPlus等C封装库简化开发 - 打开
lo接口,设置抓包过滤规则port 42557,即可只捕获端口为42557的所有数据包 - 注册回调即可直接获取完整的以太网帧原始二进制内容
参考代码片段:
#include <pcap.h> #include <stdio.h> // 抓包回调,每抓到一个匹配的包就会触发 void packet_cb(u_char* args, const pcap_pkthdr* pkthdr, const u_char* packet) { // packet指针指向的就是完整以太网帧的原始数据,按需解析即可 printf("收到包,总长度:%d字节\n", pkthdr->len); } int main() { char errbuf[PCAP_ERRBUF_SIZE]; // 打开lo接口,开启混杂模式,超时1000ms pcap_t* handle = pcap_open_live("lo", 65535, 1, 1000, errbuf); if (!handle) { printf("打开lo接口失败:%s\n", errbuf); return -1; } // 设置过滤规则,只抓42557端口的流量 bpf_program filter; const char filter_rule[] = "port 42557"; bpf_u_int32 net, mask; pcap_lookupnet("lo", &net, &mask, errbuf); pcap_compile(handle, &filter, filter_rule, 0, net); pcap_setfilter(handle, &filter); // 开始循环抓包 pcap_loop(handle, 0, packet_cb, nullptr); pcap_close(handle); return 0; }
编译指令(Linux):g++ 你的代码文件名.cpp -o packet_capture -lpcap
内容的提问来源于stack exchange,提问作者Vikash
相关产品推荐
相关产品推荐

