
简介这是一份广工计算机网络课程设计报告主题为“使用ARP协议获取局域网内部活动主机物理地址的程序实现”完整覆盖从ARP协议原理、以太网帧与ARP帧结构分析到基于Visual C与Winpcap库的代码实现与运行结果展示适合计算机专业学生作为同类课程设计或网络编程实践的参考模板。文档以PDF格式提供共1个文件压缩包大小约458KB内容精炼但结构完整包含设计题目、技术参数、实现步骤、核心代码片段及运行截图可直接对照学习数据帧封装、ARP请求构造、响应解析及混杂模式抓包等关键环节。目前已有169人浏览学习对于希望快速理解ARP地址解析机制并完成C课设报告的同学这份资料能提供清晰的思路梳理和可复用的代码框架帮助节省从零搭建实验环境与撰写文档的时间。1. 用 C 写一个 ARP 探测程序这堂课设到底在考察什么如果你是计算机或网络工程专业的学生十有八九会碰上这么一道课程设计题目用 ARP 协议获取局域网内部活动主机的物理地址。说白了一句话——你在一台连着局域网的 Windows 机器上写一个 C 程序遍历 192.168.x.1 到 192.168.x.254 这一段 IP向每个 IP 发 ARP 请求谁回了 ARP 响应谁就是此刻还活着的机器响应包里带着的那串十六进制 MAC 地址就是你要的物理地址。这个题目看起来是考协议实际上是考三样东西你懂不懂以太网帧的结构、你会不会操作 WinPcap 的抓包 API、你能不能把一个“发出去-收回来-解析”的循环写利索。这道题能落地解决的问题其实很实用比如网管想知道公司里哪些 IP 被占用了运维想知道某个网段里还有多少设备在线甚至你做内网资产梳理也得靠它。纯靠 ping 挨个探活能探到 IP 通不通但拿不到对方的 MAC 地址ping 不通的机器也不一定就是关机了可能是禁了 ICMP。ARP 探测就没有这个问题——只要这台机器要跟局域网里其他机器通信它就必然在收到 ARP 请求时回应。这也是为什么这道题放到“计算机网络课程设计”里而不是“C 编程课设”它考察的是你从协议到代码的完整链路理解。这篇笔记就从 ARP 原理讲起给你一套可以直接抄作业的 WinPcap C 实现再把我当年在这道题上踩过的坑一条条列出来。新手照着做能跑通熟手可以直接跳到第四章看边界条件。2. ARP 协议查询活动主机的核心原理先搞懂这两件事再做课设2.1 ARP 的工作流程与物理地址获取时机ARPAddress Resolution Protocol地址解析协议解决的是一个问题在一个局域网里IP 地址是逻辑地址设备真正往网线上发数据时用的是 MAC 地址。当一个设备想跟同一个网段的另一台设备通信时它只知道对方的 IP不知道对方 MAC这个时候就得靠 ARP 广播问一句“192.168.1.100你的 MAC 是多少”这句话的格式叫 ARP 请求包。它被封装在以太网帧里目的 MAC 地址填的是 FF:FF:FF:FF:FF:FF广播地址这样交换机收到后就会把它转发给同一个 VLAN 里的所有端口。网段里所有设备都会收到这个帧但只有 IP 是自己 192.168.1.100 的那台机器会回一个 ARP 应答包应答包里带着它自己的 MAC 地址。你的程序收到这个应答包就同时拿到了两个信息这个 IP 还活着它的物理地址是什么。值得注意的是Windows 系统收到 ARP 请求后即使目标 IP 不是自己也会把这个映射记到本机 ARP 缓存里甚至会更新自己缓存中的相应条目。WinPcap 捕获的是原始以太网帧这个帧是由网卡驱动层直接过滤并送上来的不经过系统的 TCP/IP 协议栈所以能拿到最干净的 ARP 包。这里有一个高频误区ARP 缓存。Windows 自带的 arp -a 命令能列出一部分 IP 和 MAC 的对应关系但缓存里的条目有过期时间大概 20 秒到 2 分钟就会刷新你能看到的永远是“最近跟这台机器有过通信”的记录。课程设计要的是“实探测”你自己构造 ARP 包发出去收到的响应才是当时当下活着的机器列表这就是主动扫描和被动缓存查看的本质区别。2.2 为什么选 WinPcap 而不是 Raw Socket做这个题目第一件事就是选技术路线。C 向网卡发 ARP 请求包从主流做法看有三条路Raw Socket、WinPcap、NpcapWinPcap 的继任者兼容其接口标准。如果是在 Linux 上做课设Raw Socket 可以用 socket(AF_PACKET, SOCK_RAW) 实现代码量并不算大但绝大多数课程设计场景是 Windows。在 Windows 上Raw Socket 想发出自定义以太网帧是比较受拘束的——Windows 的 Winsock2 对原始套接字的发送协议类型限制比较多构造 ARP 报文虽然可以做到但发送底层帧时不够直接而且 Windows XP SP2 之后系统对 Raw Socket 的发送行为加了限制特别是针对一些特定协议导致程序行为可能不稳定。对于课设来说我们不需要跟系统底层较劲选 WinPcap 即可原因有三第一WinPcap 的优势是绕过了系统协议栈直接读写网卡。ARP 请求的以太网帧目的地址是广播地址而 WinPcap 的 pcap_sendpacket 接口可以把这个帧原封不动地发出去不经过操作系统的 TCP/IP 协议栈省掉了很多系统层面的不确定因素。第二WinPcap 自己提供了一个过滤引擎可以用 pcap_setfilter 设置只接收 ARP 数据包ether proto 0x0806这样程序就不会被混在同一个网段里的其他广播帧干扰。第三这类课设在网上的参考代码绝大多数都是基于 WinPcap 写的遇到问题比如找不到设备、抓不到包也好跟人讨论复盘。如果是新装环境建议直接装 Npcap装的时候记得勾选 WinPcap API 兼容模式因为 WinPcap 官方项目已经停止维护新设备驱动安装容易出问题Npcap 兼容老 API 且应用于新版 Windows。装完之后开发时包含的头文件和链接的库仍然是 wpcap.lib 和 pcap.h代码层面基本可以无缝迁移。2.3 开发环境准备与工程配置本题目推荐的开发环境是 Visual Studio 2019 或 2022配 C 控制台应用空项目即可加上 WinPcap 开发者包Developers Pack。开发者包解压后会得到 Include 和 Lib 两个目录里面分别有 pcap.h、Packet32.h 和 wpcap.lib、Packet.lib 这些文件。用 VS 的话有两种配置方式一种是直接把这两个目录拷贝到项目目录下在工程属性里设置“附加包含目录”和“附加库目录”另一种是把 wpcap.lib 和 Packet.lib 放到项目目录在代码里用 #pragma comment(lib, wpcap.lib) 引入库这种配置方式对新手更直观也方便迁移到不同机器上编译。环境配置这一步预估一小时以内能完成关键验证点是一个简单的 pcap_findalldevs_ex 枚举代码能列出自己网卡即可。网络接口名称通常是形如 rpcap://\Device\NPF_{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX} 这样的长字符串。要注意的是程序运行的时候需要管理员权限因为 NPFNetGroup Packet Filter驱动要求在管理员权限下才能打开设备句柄。3. 用 C 实现网内活动主机物理地址探测完整代码与参数设定3.1 枚举本机网卡与选择探测接口这段代码是整个程序的入口前置部分。我们先用 pcap_findalldevs_ex 枚举当前机器上所有网卡设备然后让用户选择一个用于发送 ARP 请求的网卡。#include pcap.h #include iostream #include cstdio #include string #pragma comment(lib, wpcap.lib) #pragma comment(lib, ws2_32.lib) int main() { pcap_if_t* alldevs; pcap_if_t* d; char errbuf[PCAP_ERRBUF_SIZE]; // 枚举所有网络接口设备 if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, alldevs, errbuf) -1) { std::cerr 获取网卡列表失败: errbuf std::endl; return -1; } int idx 0; for (d alldevs; d; d d-next) { std::cout [ idx ] (d-description ? d-description : 未知设备) std::endl; std::cout 地址: (d-name ? d-name : ) std::endl; } std::cout 请选择网卡编号: ; int choice; std::cin choice; // 定位到用户选择的网卡 pcap_if_t* selected alldevs; for (int i 0; i choice; i) { selected selected-next; } ... }这段代码里面值得注意的是 pcap_findalldevs_ex 的第一个参数传 PCAP_SRC_IF_STRING表示枚举本机所有接口。通过 _ex 版本能返回 rpcap:// 开头的接口描述这在远程抓包场景下还能连远程主机但这里我们用本机枚举就够了。打印地址时 description 字段在很多机器上是中文描述比如“Realtek PCIe GbE Family Controller”能帮你判断选的是哪块物理网卡。如果机器上有虚拟网卡VMware、VirtualBox 或者 WSL 的虚拟网卡要注意选以太网那块不要选回环或者虚拟网卡网卡选错了包发不出去也收不回来这是第一次做这个课设最容易翻车的地方。3.2 打开网卡并设置抓包过滤规则选完网卡之后先要把它打开同时给它设置一个抓包过滤器让这之后接收的所有数据包都是 ARP 包。这一步如果不做好程序后面会收到大量广播帧分不清自己人。pcap_t* pcap_handle; pcap_handle pcap_open(selected-name, // 网卡名称 65536, // 抓取的最大字节数 PCAP_OPENFLAG_PROMISC, // 混杂模式 1000, // 读超时毫秒 NULL, // 远程认证null表示不需要 errbuf); // 错误信息缓冲区 if (pcap_handle NULL) { std::cerr 打开网卡失败: errbuf std::endl; return -1; } // 编译并设置过滤规则只接收ARP报文 struct bpf_program fcode; char filter_exp[] arp; if (pcap_compile(pcap_handle, fcode, filter_exp, 1, 0xFFFFFFFF) -1) { std::cerr 过滤规则编译失败 std::endl; return -1; } if (pcap_setfilter(pcap_handle, fcode) -1) { std::cerr 过滤规则设置失败 std::endl; return -1; } pcap_freecode(fcode);这里有三个参数需要认真说因为很多同学在这三个值上踩了坑。第一个是 snaplen 传 65536这是允许网卡捕获大于以太网帧最大长度1518 字节的数据包在局域网里其实根本存不下这么大的帧按惯例设置成 65536 只是防焊接。PCAP_OPENFLAG_PROMISC 混杂模式在本次课设中并不是必需的因为这个模式下网卡才会看到所有广播包其实 ARP 请求本身就是广播但保持混杂模式可以保证其它出问题的不被网卡过滤掉。第三个参数 timeout 是读超时时间 1000 毫秒它决定了 pcap_next_ex 在没有抓到包时等待多久返回超时在后续循环抓包时会频繁用到。过滤规则字符串写的 arp这个过滤表达式是 BPF 语法winpcap 编译处理后会把它编译成网卡级内核过滤指令只把 ARP 包送到应用层。这里做了两件事让程序收不到大流量下的无用包同时为后续循环扫描的快速轮询提供了保障。3.3 构造 ARP 请求包字节对齐与核心字段以太网帧是最关键的一段这里需要手动封装一个完整的 ARP 请求报文。整个报文分三部分以太网帧头14 字节、ARP 报文28 字节、帧校验序列4 字节不用自己算网卡硬件添加。#pragma pack(push, 1) typedef struct EtherNetHeader { u_char dest_mac[6]; // 目的MAC广播地址 FF:FF:FF:FF:FF:FF u_char src_mac[6]; // 源MAC即本机网卡MAC u_short eth_type; // 上层协议类型ARP为0x0806 } ETHERNET_HEADER; typedef struct ArpHeader { u_short hw_type; // 硬件类型以太网为1 u_short proto_type; // 协议类型IPv4为0x0800 u_char hw_len; // 硬件地址长度6 u_char proto_len; // 协议地址长度4 u_short opcode; // 操作码1为ARP请求2为ARP应答 u_char src_mac[6]; // 发送方MAC u_char src_ip[4]; // 发送方IP u_char dst_mac[6]; // 目标MAC请求时填0 u_char dst_ip[4]; // 目标IP } ARP_HEADER; typedef struct ArpPacket { ETHERNET_HEADER ether; ARP_HEADER arp; } ARP_PACKET; #pragma pack(pop)#pragma pack(push, 1) 这一段是整个代码里最容易被忽略的。C 编译器默认会把结构体的成员按 4 字节对齐比如 ARP_HEADER 里 u_char src_mac[6] 之后如果是 u_char src_ip[4]对齐后中间不会插入填充字节但如果不加 #pragma pack(1)部分平台会把某些成员偏移量调整到偶数或者 4 的倍数的位置这就会导致你构造出来的报文长度不是标准的 42 字节网卡或者对端机器解析时会读错字段。以太网帧头里的目的 MAC 地址填 FF:FF:FF:FF:FF:FFARP 头里的目标 MAC 填 00:00:00:00:00:00这两个是不同的位置别搞混。帧头的目的地址是用作二层交换的广播ARP 里的目标 MAC 是预留给三层应答方填写的收到 ARP 请求的设备会把自己的 MAC 填到这个字段再回给请求方。opcode 字段必须置为 htons(1)网络字节序如果你忘了用 htons 转小端机上这个字段会变成 0x0100对端不认。3.4 遍历网段并发送请求主循环与超时控制先获取本机 IP 和 MAC 地址这个获取方式最稳定的做法是用 winsock 的 gethostname 和 GetAdaptersAddresses 来拿真实接口的 IP 和 MAC。这里不展开那一大段 API 调用直接说明一下流程用 GetAdaptersAddresses 枚举本机网络接口找到与你打开网卡相同的那一个取出 FirstUnicastAddress 里的 IP 和 PhysicalAddress 字段。需要注意的是虚拟网卡和物理网卡有多个时要用 FriendlyName 匹配否则可能拿到一个错误的源 MAC 导致对端不回包。IP 与 MAC 取到手之后构造一个 ARP 包模板然后进入主循环// 假设本机IP是192.168.1.88遍历整个/24网段 ARP_PACKET requestPacket; memset(requestPacket, 0, sizeof(ARP_PACKET)); // 填充以太网帧头 memset(requestPacket.ether.dest_mac, 0xFF, 6); memcpy(requestPacket.ether.src_mac, localMac, 6); requestPacket.ether.eth_type htons(0x0806); // 填充ARP字段 requestPacket.arp.hw_type htons(1); requestPacket.arp.proto_type htons(0x0800); requestPacket.arp.hw_len 6; requestPacket.arp.proto_len 4; requestPacket.arp.opcode htons(1); memcpy(requestPacket.arp.src_mac, localMac, 6); memcpy(requestPacket.arp.src_ip, localIp, 4); memset(requestPacket.arp.dst_mac, 0, 6); // 遍历从 .1 到 .254 for (int host 1; host 254; host) { requestPacket.arp.dst_ip[0] network[0]; requestPacket.arp.dst_ip[1] network[1]; requestPacket.arp.dst_ip[2] network[2]; requestPacket.arp.dst_ip[3] host; // 发送这个ARP请求 if (pcap_sendpacket(pcap_handle, (u_char*)requestPacket, sizeof(ARP_PACKET)) ! 0) { std::cerr 发送ARP请求到 192.168.1. host 失败 std::endl; } Sleep(10); // 每发包之间等10毫秒 }这个循环的核心思路是把目标 IP 的最后一个人字节从 1 改到 254其余字段不变。这里有个关键细节ARP 请求一次只探测一个目标 IP所以循环里发送了 254 个不同的 ARP 请求包而不是一个广播包探所有。有人会问 ARP 请求本身就是广播帧难道不应该一次广播就问出来吗——不是的ARP 请求的二层目的地址是广播但三层 ARP 报文里的目标 IP 是一个具体地址只有 IP 匹配的那台机器才会响应。你要探测整个局域网就得把这个过程重复 254 次。Sleep(10) 这个延时设置值得说一下。理论上发完一个 ARP 请求之后立刻发下一个对端响应是异步回来的不会因为你发第二包就不回第一包。但 10 毫秒的间隔有实际作用比如避免本机网卡发送队列拥堵以及让后续的收包循环能分批拿到响应。这个值如果设成 0 也行但某些老网卡的驱动在连续高速发送小包时丢包率会明显上升为了课设稳定性保留这个 10ms 是划算的。整个发送过程 254 个包约 3 秒。3.5 接收 ARP 响应并解析出 MAC 地址发完一批 ARP 请求之后接着就要循环接收 ARP 响应包。这里用非阻塞方式处理因为很有可能某些 IP 没有设备在线收不到响应包时程序不能死等。struct pcap_pkthdr* header; const u_char* recv_packet; int recv_result; int timeout_count 0; while (timeout_count 50) { recv_result pcap_next_ex(pcap_handle, header, recv_packet); if (recv_result 1) { // 拿到了一个ARP包类型经过滤已确定为ARP const ARP_PACKET* packet (const ARP_PACKET*)recv_packet; // 只处理ARP应答包opcode为2 if (ntohs(packet-arp.opcode) 2) { char sender_ip[INET_ADDRSTRLEN]; sprintf(sender_ip, %d.%d.%d.%d, packet-arp.src_ip[0], packet-arp.src_ip[1], packet-arp.src_ip[2], packet-arp.src_ip[3]); if (isNewRecord(sender_ip)) { // 去重缓存里没有这个IP printf(%s - %02X-%02X-%02X-%02X-%02X-%02X\n, sender_ip, packet-arp.src_mac[0], packet-arp.src_mac[1], packet-arp.src_mac[2], packet-arp.src_mac[3], packet-arp.src_mac[4], packet-arp.src_mac[5]); // 记录到结果集合... recordResult(sender_ip, packet-arp.src_mac); } } } else if (recv_result 0) { // 超时没有包到达 timeout_count; } else { // 发生错误 std::cerr 抓包失败错误码: recv_result std::endl; break; } }pcap_next_ex 的返回值逻辑值得仔细看一眼返回 1 表示收到一个包返回 0 表示超时在这期间没包来返回 -1 表示出错。这里设置最多容忍 50 次超时每次超时是之前 pcap_open 里设置的那个 1000 毫秒所以整个收包阶段最长约 50 秒就会自动结束。对于每个收到的 ARP 应答包取它的 src_ip 和 src_mac 两个字段因为应答包的发送方就是目标主机自己。这里有一个判重逻辑 isNewRecord本机可能因为某种原因收到重复的 ARP 应答比如目标主机有多个网卡都响应了同一个 IP对同一个 IP 只需要记录第一次出现的 MAC 即可。课程设计成绩往往看重这份输出列表的排版输出格式里 MAC 地址用大写十六进制并且以短横线分隔比直接打印 %02x 的连续数字可读性强很多。有些同学在这里会用 printf 打印连续不带分隔符的 MAC扣分可惜这属于基本功不扎实的硬伤。3.6 完整程序的执行流程与判断标准上面几个片段组合起来程序的执行流程是枚举网卡→选择网卡→打开网卡→获取本机 IP 和 MAC→构造 ARP 请求包模板→循环发送 254 个请求→循环接收响应并打印。整个探测时间发送段约 3 秒接收段最坏可能 50 秒正常情况 5-10 秒内会收敛。判断程序是否跑通有个非常直观的标准拿一台手机连上同一个 WiFi在电脑上运行你的程序应该能在输出列表里看到手机的 IP 和 MAC。手机保持在亮屏状态因为熄屏休眠后某些手机会短暂停止响应 ARP。另一个验证标准是程序输出里必须包含网关的 IP 和它的 MAC网关通常是 192.168.1.1永远在线它是你探测结果里最稳定的一个活主机。如果你的程序跑完没看到网关多半是网卡选错或者过滤器写错了。4. ARP 探测最容易翻车的 5 个坑现象与解决4.1 程序打开网卡就报错权限不够和驱动没装现象是代码走到 pcap_open 这一步时 errbuf 里提示 Error opening adapter: 拒绝访问 或者 No such device exists。前者是权限问题后者是驱动问题。拒绝访问的原因多半是你运行程序的命令行窗口没有管理员权限在 VS 里调试时没有以管理员身份启动 devenv.exe。Npcap 驱动的默认设置只允许管理员访问 NPF 设备的句柄非管理员直接打开设备就会被拒绝。解决方法是右键以管理员身份运行 VS 或者直接以管理员身份运行编译好的 exe。驱动问题解决方法是到控制面板里检查 Npcap 是否安装成功装完驱动后要重启一次相关服务实在不行重装 Npcap 并勾选 Support raw 802.11 traffic 的旁路选项。重装后再用 pcap_findalldevs_ex 枚举一次能看到 \Device\NPF_ 开头的设备名就说明驱动层通了。4.2 网卡枚举能列出设备但发不了包选错了虚拟网卡现象是程序选择网卡后发送 URL 请求不发报错但实际上一个响应都收不到。这一般是因为你选择了 VMware 或 VirtualBox 的虚拟网卡或者误选了 WSL 的虚拟交换机适配器传输 MAC 到真正的物理链路层。解决方法是打开控制面板的网络适配器页面确认你选的网卡名字与当前联网的真实网卡一致。还有一个判断技巧在程序里打印出选中网卡的 IP 地址信息并和 ipconfig 命令对比如果是物理网卡IP 应该跟你的局域网地址是同一个网段如果显示的是 192.168.137.1Windows 自带虚拟 WiFi 热点网卡那就不对了。另外注意win10 的新版系统上有线网卡和无线网卡同时在线时无线网卡的名字后缀可能有 Wireless、还带着 802.11 字样不要选到蓝牙设备。4.3 包发出去了但收不到任何 ARP 响应源 IP 和源 MAC 不一致这种坑非常隐蔽。如果你的程序构造 ARP 请求包时填的源 MAC 是网卡的 MAC但源 IP 却填了不对应的 IP比如用了 VM 虚拟机网卡的 IP 或者在多网卡机器上填错了另一个网卡的 IP那么目标主机收到请求后会把应答包发到你填的那个 IP 对应的 MAC 条目而不是你发请求时用的网卡 MAC。由于你的 WinPcap 只监听了你打开的那块网卡这个应答包实际上是从另一块网卡收到的或者直接丢弃了你就永远等不到响应。验证方法Wireshark 里同时抓两块网卡的包对比发出请求的网卡和收到响应的网卡是不是同一块。解决方法是确保从 GetAdaptersAddresses 或者用 pcap 自带函数拿到的 IP 和 MAC 属于你打开那块网卡的同一条适配器记录不要单独拼凑。这段代码逻辑在课程设计里也常被老师提问你要解释得明白。4.4 只探测到部分 IP交换机端口隔离和 AP 隔离现象是程序能探测到网关也能探测到几台电脑但有一部分 IP 完全没有响应。这在网络环境里可能是正常的不是你的代码 bug。现代交换机有端口隔离功能部分接入层交换机关闭了不同端口之间的二层互通导致 ARP 广播传到不了目的主机无线网络则常见「AP 隔离」配置连接同一个无线 AP 的客户端之间互相不可见只能与网关通信。遇到这种情况解决思路是更换测试环境直接用一台交换机把两台电脑和一个路由器连起来测试不要经过办公网络的 AP。用手机热点测试也有问题手机热点默认开启客户端隔离只能热点和手机之间通信其他客户端之间是不互通的。这个坑能帮你理解局域网二层隔离的概念在答辩时讲出来反而是加分项。4.5 能收到请求的响应但解析出来的 MAC 全是 00:00:00这种现象极少见但发生在一个朋友的程序里原因是取 src_mac 时用了结构体初始化 memset 成零后的某个错误内存位置或者 #pragma pack 的 push/pop 配对写错了导致结构体偏移错位把 ARP 应答包的 src_mac 字段读成了一个无效值。解决方法是先用原始字节逐位打印出整包十六进制数据手工对照 RFC 826 里的字段偏移量数一遍 src_mac 到底偏移多少字节。以太网帧头 14 字节 ARP 硬件类型 2 字节 协议类型 2 字节 硬件长度 1 字节 协议长度 1 字节 操作码 2 字节src_mac 从偏移 14822 字节开始共 6 字节。把这个数对照上基本能定位是结构体对齐问题还是取址问题。5. 验证方法与课设答辩进阶把探测结果做扎实5.1 探测结果如何对照验证程序写完你必须拿出两套交叉验证手段来证明自己的结果是对的不能光说“它能打出 MAC 来就算成功”。第一套验证工具是命令行自带的 arp -a 命令程序探测完在另一台电脑上 ping 一下目标 IP然后执行 arp -a查看这个 IP 对应的 MAC。目标主机只要和你通了一次网络就会出现在 ARP 缓存表里。这个表里的 MAC 和你的程序输出的 MAC 应该完全一致。第二套验证工具是 Wireshark。打开 Wireshark 选择同一块网卡在抓包过滤栏输入arp然后运行你的程序。你会在 Wireshark 里看到一串 ARP 请求广播帧以及随之而来的若干个 ARP 应答单播帧。这个双重验证不仅证明你的探测器有效还能帮你数一数你发出的 ARP 请求有没有被交换机丢弃是排查发包速率问题的好工具。5.2 做一个活跃主机在线状态表格让课设从“完成”变成“优秀”最简单的加分做法是给程序加一个输出表格整理逻辑——把探测结果统一存到一个结构体数组里按 IP 排序然后统一打印成一个带对齐的表格同时在最后一列标明哪些是网关设备、哪些是同一台主机的多网卡同一 MAC 多 IP信息量一下就打满了。本机网卡: Realtek PCIe GbE Family Controller 本机IP: 192.168.1.88 本机MAC: A4-B1-C2-D3-E4-F5 扫描网段: 192.168.1.0/24 请求数:254 响应数:12 在线主机列表: IP MAC 备注 192.168.1.1 00-1A-2B-3C-4D-5E 网关 192.168.1.36 CC-33-44-55-66-77 192.168.1.88 A4-B1-C2-D3-E4-F5 本机 192.168.1.101 B8-88-E3-14-7A-01表格里的“备注”列要你自己写判断逻辑。网关怎么识别你可以用 GetAdaptersInfo 拿到本机的网关 IP然后在结果列表里匹配。识别出网关能证明程序具备基本网络知识这是答辩时老师最喜欢抽问的一点。5.3 扫描时间与探测速度的权衡值得再强调一次的是发送 254 个 ARP 请求的速度控制问题。我写这个程序的过程中做了几轮对比完全不延时一口气发完 254 个包程序 0.2 秒就全部发完但收包阶段会持续 20~30 秒才能收完所有响应而且有大概 15% 的响应会丢每包间延时 10ms发送 2.5 秒响应率接近 100%收包阶段 2 秒内基本收敛如果延时拉到 100ms发送就要 25 秒钟时间成本太高。所以 10ms 是我测下来比较合适的值。通信技术好一点的交换机内部有 ARP 请求速率限制甚至在端口上做了风暴控制超过一定速率就丢弃广播帧。不要在一个大型局域网里用 0ms 延时去扫很容易被交换机的安全策略拉黑端口。控制住发送速率不仅稳定也更有工程实践感。5.4 答辩时主动讲解的细节和一份 C 课设经验最后聊聊答辩技巧。老师一定会问三个问题里的至少两个ARP 报文格式是怎样的为什么收不到某些主机的响应你的程序有没有考虑可靠性第一个问题你要能现场画一遍报文格式14 字节以太网头、28 字节 ARP 报文各字段的值与含义这是送分题。第二个问题要把网卡选错、AP 隔离、防病毒软件拦截都答出来体现你有排障能力。第三个问题把你的判重记录逻辑和超时控制讲清楚老师会认可你的工程意识。我自己当年做这个课设最深刻的教训是构造 ARP 包时直接用了一个没有加 #pragma pack 的结构体结果在 VS 的默认 4 字节对齐下ARP 报文里多了两个填充字节整个包长度变成 44 字节而不是 42 字节抓包工具里看字段全对但对端就是不应答我查了三个小时才在对比 Wireshark 时发现以太网类型字段后面多了两个空字节。后来我把代码里所有网络报文结构体都加了 pack(1)从此再没翻过车。这个课设做完你对 BPF 过滤器、原始套接字、网络字节序这些概念的理解会非常扎实把这些经验留在笔记里希望帮到你。本文还有配套的精品资源点击获取