资讯详情

Qt5与WinPcap开发网络抓包工具:环境搭建、抓包引擎与协议解析

📅 2026/10/10 18:58:26 | 华诺云谱 👁 阅读
Qt5与WinPcap开发网络抓包工具:环境搭建、抓包引擎与协议解析
简介一套基于QT5与WinPcap开发的仿WireShark网络抓包程序面向网络工程师、安全分析人员及协议学习开发者可完成本地数据包的捕获、解析、过滤与统计用于网络故障排查和协议分析。程序界面和核心交互参考WireShark包含数据包列表、协议树和过滤栏等模块。压缩包共392个文件大小8.15MB涵盖143个HTML说明文件、29个H头文件、27个C源文件、16个C源文件、3个UI界面文件以及VC工程配置、静态库和PNG/GIF图片素材构成一个可编译研究的完整项目工程。已有197人学习下载。对于想深入理解抓包原理或QT5与WinPcap应用开发的读者可从源码和工程结构中梳理出抓包流程、界面联动及WinPcap API调用方式并借助HTML文档快速定位关键模块是网络编程方向的实用参考。1. 为什么还有人用WinPcap仿WireShark这个Sniffer项目的技术定位与适用人群在国产网管软件和很多高校的课程设计里基于QT5和WinPcap实现的网络抓包程序Sniffer一直是个常青树选题。即便官方早已停止维护WinPcap但企业内网流量排查、协议分析和教学演示中这套组合仍然比从零封装更省事——因为WinPcap的API足够底层驱动模型几十年来没大变QT5又能在Windows上快速做出一套带表格和过滤框的界面。这个项目适合三类人一是计算机网络课程要交抓包工具的大四学生二是公司里需要临时看某个网段流量、不想上商业监控平台的运维三是想系统理解数据包从网卡到应用层全链路的新人。这篇文章会把从SDK配置到协议解析的完整路径拆开沿路标好坑。2. 搭出可编译的QT5与WinPcap环境选型、SDK配置和64位陷阱2.1 为什么选择WinPcap而不是Npcap或libpcapWinPcap是Windows平台上的经典抓包驱动QT5负责界面两者组合在思路上是Wireshark的简化复刻。常见做法是直接下载WinPcap的开发者包里面包含头文件、导入库和示例代码不用自己安装驱动——运行时目标机器才需要安装WinPcap运行库。Npcap是它的替代品很多新项目改用Npcap但本标题既然明确锁定WinPcap就继续按这个栈来。如果遇到驱动安装失败可以用Npcap的WinPcap兼容模式顶替后面避坑章节会细说。2.2 安装开发者包并确认目录结构先把WinPcap_Driver和WinPcap SDK通常叫WpdPack都准备好。下载后解压WpdPack到固定目录我一般放在项目的third_party里这样pro文件写相对路径就能引用。代码块# 解压后确认目录结构示意 WpdPack/ ├── Include/ │ ├── pcap.h │ ├── pcap-stdinc.h │ ├── pcap-int.h │ └── remote-ext.h ├── Lib/ │ ├── wpcap.lib │ ├── Packet.lib │ └── x64/ │ ├── wpcap.lib │ └── Packet.lib └── docs/ └── html/这段目录结构是从WinPcap开发者包里解出来的常见形态。Lib目录下默认有32位版本x64子目录里才放着64位导入库这个细节直接关系到后面QT5编译器的选择。如果你用的编译器是MSVC 2017 64位却链接了根目录的wpcap.lib那大概率在运行时炸掉因为32位静态库没法被64位进程正确解析。参数说明Include目录是给预处理器找pcap.h用的Lib目录是给链接器找wpcap.lib用的任何一个配错编译报错或链接报错都从这里找起。2.3 在QT5工程文件里正确链接WinPcap库环境配置的核心就是pro文件。这里给出一个可以直接套用的片段里面同时包含了Qt 5的core、gui、widgets模块包含路径设置和WinPcap的链接。代码块# Sniffer.pro QT core gui widgets TARGET Sniffer TEMPLATE app CONFIG c11 # WinPcap 开发者包路径 WINPCAP_ROOT $$PWD/third_party/WpdPack INCLUDEPATH $${WINPCAP_ROOT}/Include # 注意先接 wpcap再接 ws2_32顺序错会链接失败 LIBS -L$${WINPCAP_ROOT}/Lib -lwpcap -lws2_32 SOURCES main.cpp \ MainWindow.cpp \ CaptureEngine.cpp \ PacketDecoder.cpp HEADERS MainWindow.h \ CaptureEngine.h \ PacketDecoder.h这段配置里最关键的有两个点。第一是-lws2_32WinPcap在Windows上依赖Winsock2库来处理网络字节序和socket相关操作漏掉这个库会看到一堆类似unresolved external symbol __imp____WSAStartup8的链接错误。第二是链接顺序MSVC的链接器是按依赖顺序解析符号的wpcap依赖ws2_32所以wpcap写在前面。参数说明WINPCAP_ROOT用$$PWD拼出绝对路径避免不同电脑上工作目录不一致导致找不到头文件-L指定库目录-l指定库名这是qmake里最常规的写法。顺手说一句QT5里文件拖拽支持经常失效这个问题未必出在WinPcap上后面避坑部分会单独提。2.4 编译器选型的血泪经验要么用32位工具链要么换SDK这里要直接把64位编译器的坑摆出来。WinPcap老SDK里的wpcap.lib默认是32位导入库用MSVC 2017 64位工具链时会链接失败报错信息像这样代码块LNK1112: module machine type x86 conflicts with target machine type x64原因很清楚链接器拿到的导入库是32位机器码但你的可执行文件目标是64位机器类型不匹配。解决办法有两条路。第一条在QT5里不切换64位编译器继续用MSVC 2017 32位或MinGW 32位构建这样抓包示例代码直接照搬WpdPack里的例子就能跑少很多适配工作。第二条改用Npcap的开发者SDK它提供了x64的wpcap.lib且接口完全兼容链接时仍然用-lwpcap但导入库路径指向Npcap SDK的Lib/x64。我个人更推荐第二条因为现在已经没几个新机器愿意装32位应用了再加上目标是仿Wireshark的工具未来可能还要扩展追包、导出等功能一开始就上64位能少走回头路。3. 抓包引擎的骨架从网卡枚举到实时抓包线程3.1 先枚举所有网卡给用户一个设备下拉框抓包程序第一步是找到当前机器上有哪些网络接口。Wireshark启动时会列出所有可抓包网卡我们仿制的程序也一样。WinPcap里用pcap_findalldevs_ex或pcap_findalldevs完成枚举返回一个pcap_if_t链表链表节点上带着设备名称、描述和地址族信息。代码块// CaptureEngine.cpp #include pcap.h #include QList #include QString struct DeviceInfo { QString name; QString description; }; bool enumerateDevices(QListDeviceInfo* outList) { pcap_if_t* alldevs nullptr; char errbuf[PCAP_ERRBUF_SIZE] {0}; // 枚举本机所有网络接口 int ret pcap_findalldevs(alldevs, errbuf); if (ret ! 0) { // PCAP_ERROR 时 errbuf 里有原因 return false; } for (pcap_if_t* dev alldevs; dev ! nullptr; dev dev-next) { DeviceInfo info; info.name QString::fromLocal8Bit(dev-name); // 设备名如 \Device\NPF_{...} info.description QString::fromLocal8Bit(dev-description); // 网卡描述可能为空 outList-append(info); } pcap_freealldevs(alldevs); // 遍历完必须释放否则内存泄漏 return true; }这段代码有两个容易看走眼的点。第一个是dev-description在某些虚拟网卡或蓝牙网卡上可能是null直接fromLocal8Bit会构造出空字符串界面下拉框里显示一行空白看起来像Bug但其实是数据本身为空渲染时要兜底。第二个是释放链表的pcap_freealldevs绝对不能省枚举出的链表是驱动层拷贝出来的数据不回收的进程每次打开设备列表都漏一块内存抓包程序长期挂着会越来越臃肿。参数说明PCAP_ERRBUF_SIZE是WinPcap定义的错误缓冲区大小约定为256字节够装一条英文错误信息pcap_findalldevs的返回值0表示成功-1表示失败失败时错误信息在errbuf里打印出来就能看到是权限问题还是驱动问题。3.2 打开网卡时把snaplen、promisc和时间超时设对拿到用户选中的设备名后接着就是打开网卡准备抓包。WinPcap里的核心函数是pcap_open_live它有四个关键参数决定抓包行为snaplen决定单包最大截取长度promisc决定是否开启混杂模式to_ms决定读取超时还有一个缓冲区大小在pcap_set_buffer_size里单独设。代码块#include pcap.h pcap_t* openLiveDevice(const char* deviceName, bool promisc, QString* error) { char errbuf[PCAP_ERRBUF_SIZE] {0}; // snaplen 65535完整保留以太网帧避免IPTCP头被截断 // promisc 1混杂模式接收目的MAC不是本机的包 // to_ms 1000超时1秒兼顾实时性和CPU占用 pcap_t* handle pcap_open_live( deviceName, // 设备名例如 \Device\NPF_{A1B2...} 65535, // snaplen promisc ? 1 : 0, // promiscuous mode 1000, // read timeout (ms) errbuf // 错误信息缓冲区 ); if (handle nullptr) { *error QString::fromLocal8Bit(errbuf); return nullptr; } // 设置内核缓冲区为4MB避免高流量下用户态来不及读而丢包 if (pcap_set_buffer_size(handle, 4 * 1024 * 1024) ! 0) { // 这个失败通常不影响抓包可以只警告不退出 } return handle; }snaplen设成65535是为了拿全整个帧如果设小了比如1500的理论以太网MTU能容下但碰上巨型帧或带VLAN tag的帧TCP载荷尾部可能被截掉后续重组和流量统计会偏差。to_ms设成1000毫秒意味着网卡驱动攒了最多一秒的数据包再交给用户态这样在低速网络下也能攒一批包一起处理降低调度开销。参数说明混杂模式是sniffer的灵魂不开它只能看到发往本机MAC的包例如ARP广播本来能收到但因为网卡硬件过滤直接丢掉了所以抓网段内其他主机的流量必须开promisc如果抓包程序部署在云主机上交换机端口本身可能隔离了其他流量这个模式下也看不到别人的包这是网络环境限制不是程序Bug。3.3 抓包线程怎么设计回调里emit信号不要在抓包线程刷界面WinPcap提供两种读取模式阻塞式pcap_next_ex和回调式pcap_loop。在QT5程序里最简单的方案是起一个std::thread在线程里跑pcap_loop或循环调pcap_next_ex每抓到一包就emit一个携带原始数据的QT信号主线程收到信号后再解析和刷新表格。这套模型不复杂但线程边界不能踩错否则界面冻结只是前奏。代码块// CaptureEngine.cpp 简化版 #include QThread #include pcap.h void CaptureEngine::startCapture(pcap_t* handle, bool keepRunning) { // pcap_loop 的最后一个参数是用户上下文回调里会原样透传 pcap_loop(handle, 0, CaptureEngine::packetHandler, reinterpret_castu_char*(this)); } void CaptureEngine::packetHandler(u_char* userData, const struct pcap_pkthdr* header, const u_char* packetData) { CaptureEngine* self reinterpret_castCaptureEngine*(userData); // 组装成可跨线程传递的结构体 RawPacket raw; raw.timestampSec header-ts.tv_sec; raw.timestampUsec header-ts.tv_usec; raw.caplen header-caplen; raw.data.assign(packetData, packetData header-caplen); // 通过信号跨线程通知主界面 emit self-packetCaptured(raw); }这里面的关键是把this指针通过pcap_loop的userData参数传进回调这样回调里能访问到emit所在的QObject实例。注意QT信号槽默认的连接方式是AutoConnection跨线程时自动转为队列调用主线程事件循环每转一圈才处理一批包不会出现两个线程同时争抢界面控件。但这个模型的代价是主线程如果忙于重绘表格队列里的包会越积越多所以后面解析表格要控制刷新频率。参数说明pcap_loop的第二个参数代表抓包数量传0表示无限抓直到breakloop对持续运行的工具正好caplen是实际抓到的字节数不一定等于原始包长因为snaplen可能把包截断做统计时用header-len拿原始长度用caplen拿实际可分析的字节数。3.4 停止抓包pcap_breakloop不能省停止动作没有看起来那么容易。如果在界面上有一个“停止”按钮你可能会想在槽函数里直接pcap_close(handle)或直接终止线程——这两种做法都可能崩溃因为抓包线程正阻塞在pcap_loop内部读取驱动缓冲区你从外部把句柄关了驱动还在往这块内存写数据结果是未定义行为。正确做法是先调pcap_breakloop(handle)它会让pcap_loop在读完当前这一批包之后立即返回而不是继续等待下一批数据。返回后抓包线程自然退出再在抓包线程末尾调用pcap_close释放句柄最后join线程。一句话总结先breakloop再close顺序反了就会偶发程序崩溃而偶发Bug最难排查。4. 像WireShark那样解析每个包以太网帧、IP、TCP和UDP的字节拆解4.1 手工拆帧并正确转换网络字节序抓包程序的价值在解析。你现在拿到的是一段裸字节u_char* packetData它从以太网帧头开始。一个完整以太网帧的布局是目标MAC 6字节、源MAC 6字节、EtherType 2字节然后是payload。如果EtherType是0x0800后面是IPv4报头如果是0x0806就是ARP报文。所有这些多字节字段在网络传输时按大端序排列在x86机器上必须用ntohs或ntohs家族函数转换。代码块#include cstring #include netinet/in.h // Windows 上可用 winsock2.h 代替 struct EtherHeader { uint8_t dstMac[6]; uint8_t srcMac[6]; uint16_t etherType; // 从字节流中先读到 uint16_t }; bool parseEthernetFrame(const u_char* data, size_t len, ParsedFrame* out) { if (len 14) { return false; // 帧头都不完整丢弃 } EtherHeader hdr; std::memcpy(hdr, data, 14); // memcpy 而不是结构体强转避免对齐问题 hdr.etherType ntohs(hdr.etherType); // 目标/源MAC格式化为 AA:BB:CC:DD:EE:FF formatMac(hdr.dstMac, out-dstMac); formatMac(hdr.srcMac, out-srcMac); // 判断上层协议 if (hdr.etherType 0x0800) { out-protocol Protocol::IPv4; } else if (hdr.etherType 0x0806) { out-protocol Protocol::ARP; } else if (hdr.etherType 0x86DD) { out-protocol Protocol::IPv6; } else { out-protocol Protocol::Unknown; } return true; }这里两个容易翻车的点。第一个是直接用结构体强转指针比如EtherHeader* hdr reinterpret_castEtherHeader*(data)这种写法在VC编译器的对齐策略下如果结构体含有uint16_t字段编译器可能插入隐式填充字节导致布局和网络报文不一致。所以先memcpy到本地栈结构体再逐字段解析最稳妥。第二个是字节序EtherType字段在内存里是0x0800的大端序直接读出来变成0x0008判断EtherType时怎么都对不上必须ntohs。参数说明len在这里是caplen是驱动实际交付的包长以太网帧最小60字节不算FCS如果len小于14说明帧头都不完整这样的包要么是抓包驱动bug要么是snaplen太小直接丢弃。4.2 协议识别不只是看EtherType还要走进TCP/UDP端口光解析到IPv4还不够Wireshark里你能看到TCP、UDP、ICMP、DNS等逐层缩进。仿制版要做到同样效果需要继续往IP头后面解析。IPv4头的结构是版本IHL共1字节、服务类型1字节、总长度2字节、标识2字节、标志和分片偏移2字节、TTL 1字节、协议1字节、头部校验2字节、源地址4字节、目的地址4字节。关键字段是第10字节的protocol值6是TCP17是UDP1是ICMP2是IGMP。代码块#include cstring bool parseIPv4Header(const u_char* data, size_t len, ParsedFrame* out) { if (len 20) { return false; // IP头最少20字节 } uint8_t versionIhl data[0]; uint8_t ihl versionIhl 0x0F; // 低4位是头部长度单位4字节 if (ihl 5) { return false; // IHL不能小于5否则IP头长度非法 } uint8_t protocol data[9]; // 协议字段 uint16_t totalLen (data[2] 8) | data[3]; // 大端直接拼出来 char srcIp[16]; char dstIp[16]; snprintf(srcIp, sizeof(srcIp), %u.%u.%u.%u, data[12], data[13], data[14], data[15]); snprintf(dstIp, sizeof(dstIp), %u.%u.%u.%u, data[16], data[17], data[18], data[19]); out-srcIp QString::fromLocal8Bit(srcIp); out-dstIp QString::fromLocal8Bit(dstIp); out-protocol mapProtocolNumber(protocol); if (protocol 6 || protocol 17) { // TCP/UDP 固定头在 IP 头之后 const size_t ipHeaderLen ihl * 4; if (len ipHeaderLen 4) { return false; // 端口字段都拿不全 } const u_char* segment data ipHeaderLen; out-srcPort (segment[0] 8) | segment[1]; out-dstPort (segment[2] 8) | segment[3]; } return true; }这段代码特意没用结构体而是手动按字节偏移取值这样最直观也最安全。ihl乘以4得到IP头的实际字节数因为IP头是不定长的。TCP和UDP的源端口/目的端口都在各自段的最前4字节所以紧挨着拼端口号即可。这里也不用ntohs因为直接按大端从字节流里拼数得到的本来就是主机序数值。参数说明如果versionIhl高4位不是4说明不是IPv4包低4位ihl是头部长度单位数比如值为5表示头部20字节值为6表示24字节带Option解析TCP/UDP端口时如果跳过固定20字节而不管ihl遇到带Option的包会把源端口读歪。4.3 解析结果灌进QTableWidget注意刷新频率和排序解析完成后数据要展示到界面上。常见做法是MainWindow持有一个QTableWidget每来一个RawPacket信号就做一次解析并插入新行。这个逻辑写起来很快但一秒钟几百包的流量会让表格卡成PPT。以下是带节流的插入版本照着这个思路能撑住大部分教学场景。代码块void MainWindow::onPacketCaptured(RawPacket raw) { ParsedFrame frame; if (!decodePacket(raw, frame)) { return; // 解析不了就不显示 } // 控制刷新频率每500毫秒批量插入一次 m_pendingFrames.append(frame); if (!m_flushTimer-isActive()) { m_flushTimer-start(500); } } void MainWindow::flushPendingFrames() { QTableWidget* table ui-tableWidget; table-setUpdatesEnabled(false); // 批量插入时暂停重绘 for (const ParsedFrame frame : m_pendingFrames) { int row table-rowCount(); table-insertRow(row); table-setItem(row, 0, new QTableWidgetItem(frame.timestampStr)); table-setItem(row, 1, new QTableWidgetItem(frame.srcIp : QString::number(frame.srcPort))); table-setItem(row, 2, new QTableWidgetItem(frame.dstIp : QString::number(frame.dstPort))); table-setItem(row, 3, new QTableWidgetItem(frame.protocolStr)); table-setItem(row, 4, new QTableWidgetItem(QString::number(raw.caplen))); } m_pendingFrames.clear(); table-setUpdatesEnabled(true); table-resizeColumnsToContents(); // 列宽自适应但别每包都调 }这段代码的核心是setUpdatesEnabled(false)和定时器批量刷新。如果每来一个包都直接insertRow表格每行都要触发一次布局计算和重绘几千行数据就能把主线程卡死反过来攒500毫秒的包一次插进去体验会丝滑很多。另一个细节是表格只保留最近N行或者提供“暂停/继续”按钮否则长期运行内存只增不减。参数说明raw.caplen是实际字节数不是原始包长如果做流量统计比如算某IP发了几MB要用header-len否则被截断的包会让统计偏低。想复现Wireshark那种分片切割实时保存的效果可以在flush时记录当前缓冲区里的包数每攒满指定数量就写一次pcap文件但保存的每条记录要确保你写的是pkthdr加原始包数据的完整结构。5. 抓包程序的避坑与排查驱动装不上、过滤器不生效、网络序看走眼5.1 目标机器提示找不到wpcap.dll程序秒退现象开发机上编译能跑换一台机器拷过去双击exe直接闪退或者弹窗说“找不到wpcap.dll”。原因WinPcap开发者包里的wpcap.lib是导入库程序运行时要到系统System32或程序当前目录找wpcap.dll动态库而目标机器没装WinPcap运行库。解决把安装包里的wpcap.dll和packet.dll放到exe同级目录或者让用户先装一次WinPcap driver。很多课程设计报告只提交源码演示时在没装WinPcap的机器上现场翻车所以交付清单里最好写明运行依赖。顺带一提如果报错信息是“0xc000007b”那是32位和64位DLL混用比如64位exe找到了32位wpcap.dll重新按位数匹配即可。5.2 Vivado或旧版安装器下WinPcap装不上驱动签名的问题现象在Win10或Win11上点击WinPcap安装包进度条抖动几下后回滚事件查看器里看到“驱动阻止加载”。或者是在用Vivado等工具链时它的版本检测到没装WinPcap就报安装失败但手动装也装不上。原因新版Windows要求驱动有微软签名老的WinPcap驱动版本没被新内核信任安装器回滚。解决第一种是右键驱动程序→属性→数字签名看是否“无签名”有签名但被阻止可以先bcdedit /set testsigning on开测试签名模式仅调试用或者插上旧WinPcap安装包以兼容模式运行。第二种是直接在当前机器装Npcap并勾选“WinPcap API兼容模式”这样老软件调pcap_open_live时走Npcap驱动不装WinPcap也能跑。5.3 过滤器语法写“对”了却一个包都抓不到现象界面里输入TCP过滤器点开始表格一个包都不进但同一网卡不填过滤器就能抓到包。原因过滤器并不是对所有网卡设备都生效常见是设备选错了。蓝牙网卡和虚拟网卡的设备名和物理网卡不同如果你的下拉框默认选了第一个枚举出的设备那可能是个Windows蓝牙PAN适配器或虚拟机虚拟网卡上面根本没有常规TCP流量。解决先用pcap_findalldevs打印所有设备名再对照ipconfig确认目标网卡的MAC或IP属于哪个接口。另一个原因是过滤表达式本身绑定了VLAN偏移量Wireshark里好用的udp port 53在经典WinPcap过滤引擎里不会自动解析带VLAN tag的帧需要写成vlan and udp port 53这是一个很隐蔽的坑。5.4 回调里直接刷界面抓包一多界面就假死现象程序运行起来前几个包还算顺畅后来表格越来越卡拖动窗口都在打太极。原因pcap_loop的回调运行在抓包线程里如果你在回调里直接调用tableWidget-insertRow()QT界面控件被非GUI线程操作轻则线程争抢导致重绘卡顿重则直接崩溃。解决回调里只做数据的序列化和emit信号所有界面刷新放到主线程槽函数中。如果槽函数里还有复杂解析逻辑把解析动作挪到worker线程主线程只接收已经格式化好的字符串列表。再不行给表格加一句“正在抓包点击置顶暂停刷新”用户也会更放心。5.5 QT5无法拖拽文件到窗口不是WinPcap的锅却总被一起问现象有的抓包程序支持把本地.pcap文件拖进窗口直接打开但拖拽时鼠标变成禁止图标文件放不进窗口。原因QT5里接收系统拖拽事件需要设置setAcceptDrops(true)并且重写dragEnterEvent和dropEvent很多人只改窗口属性忘了重写事件函数。解决在我的经验里至少同时做三件事构造函数里this-setAcceptDrops(true)dragEnterEvent里调用event-acceptProposedAction()dropEvent里取event-mimeData()-urls()的第一个本地路径。如果文件图标有盾牌角标那是UAC权限问题用管理员身份启动程序再拖一次。5.6 关闭程序时偶发崩溃反复调代码找不到根因现象点退出按钮或者关闭主窗口程序偶尔崩溃多跑几次概率触发。原因多半是抓包线程还在阻塞读包主窗口的槽函数直接删除了pcap_t*句柄或直接调用了close。解决关闭顺序必须严格是——置breakloop标志并等待抓包线程退出join再在同一个线程或确认线程退出后pcap_close句柄最后再销毁主窗口。我一般在MainWindow的关闭事件里先发一个停止信号确认工作线程finished信号到达再event-accept()否则强制拦截关闭。这套逻辑写顺了后面再做基于Npcap的版本也能平移。6. 进阶技巧用BPF过滤器把抓包目标精确到“想看的那一种流量”到了这一步一个能用的仿Wireshark工具已经有了。但真正的威力隐藏在WinPcap自带的BPF过滤器里——这是除了协议解析之外性价比最高的一项增强。BPF的语法和Wireshark显示过滤器不完全等效但抓包前限定范围比抓完再筛选更省内存。举个例子只抓DNS请求udp port 53只抓某个IP进出的HTTP包host 192.168.1.100 and tcp port 80平时做网络排查我习惯先给程序预设一个“快速过滤”下拉框把tcp port、udp port 53、arp、icmp这些常用模板放进去用户选完直接调用pcap_compile和pcap_setfilter。需要说明的是BPF过滤是在驱动层完成的不匹配的包根本不会进入用户态缓冲区这比全量抓到用户态再丢弃高效得多。一个容易忽略的地方是过滤表达式里写port而不写协议会自动匹配TCP和UDP而如果你的目标网卡上有大量VLAN帧最好在表达式前加前缀vlan and。最后你们如果之前用Wireshark做蓝牙抓包或者BLE指定设备过滤那套显示过滤器语法和这里的BPF不同注意区分。我自己的习惯是开发完抓包功能先花半小时把过滤框做成带历史记录的下拉框因为做工具的人都知道真正排查问题的人不会每次重新敲一遍语法。希望能帮到你。本文还有配套的精品资源点击获取
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑