资讯动态

Wireshark抓包实验:TCP、UDP、HTTP、FTP、DNS协议解析

发布时间:2026/10/20 18:10:59 来源:尧图企业网站定制
计算机网络实验里抓包分析是理解协议最直接的手段之一。标题里的“Ethereal”可能很多新同学不认识它是 Wireshark 的前身后来改名后功能一脉相承过滤语法和界面布局基本没变。这套实验我反复带过几轮先用脚本把 TCP 套接字和 UDP 套接字跑起来抓包看三次握手、四次挥手和无连接通信再顺手抓 HTTP、FTP、DNS 三种协议报文把课本上的协议字段一一落到真实流量上。对于正在学计算机网络、做课程实验或者准备面试的读者来说这篇文章可以帮你把“协议”从抽象的图变成看得见摸得着的包。整套实验不需要特殊硬件一台电脑、一个 Wireshark、几段 Python 脚本就够了。下面我把环境搭建、TCP/UDP 捕包分析、HTTP/FTP/DNS 报文拆解和常见踩坑点全部展开每一步都给可直接复现的方案。1. 实验环境准备与工具选型1.1 为什么选 Wireshark 而不是 tcpdump 或 CharlesWireshark 的前身就是 Ethereal两者在抓包核心能力上没有本质区别。它既是一个抓包器也是一个非常强悍的协议解析器内置了上千种协议的支持。它会自动把 TCP 握手标志、HTTP 请求响应、FTP 命令、DNS 记录类型识别出来以分层树状结构展示这对学习协议特别友好。相比之下tcpdump 是纯命令行工具适合在服务器上快速抓包但新手看十六进制输出很容易发懵。Charles 更多是面向 HTTP/HTTPS 调试对于 DNS、FTP、原始 TCP/UDP 报文支持很弱。Wireshark 是同时满足“看得见底层细节”和“能按协议语义解读”的工具。实际操作中如果在 Linux 服务器上临时抓包我会先 tcpdump 存成 pcap 文件再拉回本地用 Wireshark 慢慢看。纯课程实验场景直接用图形界面最省事。1.2 最小复现环境Python 套接字脚本与抓包位置抓包实验最怕环境嘈杂。为了得到干净、可控的流量我建议用 Python 自带 socket 模块写最小客户端和服务端流量只在一台机器的回环接口上跑。这样不用牵涉局域网广播、ARP、网关转发等额外噪声抓包结果非常“教科书”。抓包位置要特别注意如果客户端和服务端都运行在 127.0.0.1数据包走的接口是 Loopback不是物理网卡。在 Wireshark 首页选择“Loopback: lo”或 Windows 下的“Npcap Loopback Adapter”然后再点开始捕获。很多同学第一次实验“怎么也抓不到包”八成是选了以太网或 WLAN 网卡目光盯着物理接口流量却在回环接口里穿行。为了后续减少干扰我习惯在抓包前先给 Wireshark 设置捕获过滤器只留目标端口。比如 TCP 实验固定端口 8888就写tcp port 8888UDP 实验固定端口 9999就写udp port 9999。这样抓包文件很小分析时一眼就能找到关键报文。1.3 捕获过滤器与显示过滤器不要搞混Wireshark 里有两类过滤条件容易被初学者混淆。捕获过滤器是在抓包之前生效属于内核级 BPF 过滤语法偏底层比如tcp port 80、host 192.168.1.10。显示过滤器是抓包之后对已捕获数据包做筛选语法更加丰富比如tcp.port 80、http.request、dns.flags.response 0。实验建议是抓包阶段用捕获过滤器收敛范围比如只抓某个端口或某台主机分析阶段再用显示过滤器按协议、标志位、字段值筛选。显示过滤器可以随时修改而且不会丢失已经抓到的数据适合反复试。比如查看 TCP 三次握手可以用tcp.flags.syn 1把所有 SYN 包筛出来查看 HTTP 请求用http.request查看 DNS 查询用dns.qr 0。2. TCP 套接字捕包分析三次握手与四次挥手2.1 构建一对 TCP Socket 程序先准备服务端脚本监听本地 8888 端口。以下代码非常适合演示import socket srv socket.socket(socket.AF_INET, socket.SOCK_STREAM) srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) srv.bind((127.0.0.1, 8888)) srv.listen(1) print(server waiting...) conn, addr srv.accept() print(client connected:, addr) data conn.recv(1024) print(received:, data) conn.sendall(bhello from server) conn.close() srv.close()客户端脚本如下import socket cli socket.socket(socket.AF_INET, socket.SOCK_STREAM) cli.connect((127.0.0.1, 8888)) cli.sendall(bhello from client) resp cli.recv(1024) print(response:, resp) cli.close()操作顺序先运行服务端让它在 accept 处阻塞等待启动 Wireshark选择 Loopback 接口捕获过滤器填tcp port 8888然后运行客户端。结束后停止抓包你会看到一连串以 8888 端口为宿主的 TCP 报文。2.2 三次握手的关键报文解析过滤结果中前面三个包对应 TCP 三次握手。Wireshark 默认使用“相对序列号”让数值从 0 开始显示方便学习。实际线上的绝对序列号是随机初始化的但实验时看相对序号更容易理解。下表是我抓到的典型结果包号方向标志相对Seq相对Ack说明1客户端 - 服务端SYN00请求建立连接2服务端 - 客户端SYN ACK01同意连接确认客户端的SYN3客户端 - 服务端ACK11确认服务端的SYNACK第一个包只设置了 SYN 标志表示客户端请求同步序列号。第二个包同时设置 SYN 和 ACK服务端既表示“我这边初始序号也是 0”又表示“我收到了你的序号 0所以我期望你下一个包序号是 1”。第三个包是客户端纯 ACK确认收到服务端序号握手完成。很多新手看不懂为什么第三次握手的 Ack 是 1。这里的 Ack 表示“期望接收的下一个字节序号”。服务端在第二个包里带有 SYN虽然没有数据长度但在 TCP 语义中 SYN 和 FIN 本身各占一个序号。客户端收到序号 0 的 SYN 后下一个期望序号自然就是 1。在 Wireshark 的 TCP 头部展开中你还能看到 Window Size、MSS、SACK permitted 等协商字段。其中 MSS 是“最大报文段长度”双方在握手时交换各自能接收的最大单段数据长度如果 MTU 是 1500 字节扣除 IP 头、TCP 头后MSS 常见值就是 1460。这些参数直接影响后续分段行为。2.3 数据传输中的序列号与确认号握手完成后客户端发送了bhello from client。这个字符串长度为 17 字节抓包显示为客户端 - 服务端PSH ACKSeq1Len17服务端 - 客户端ACKSeq1Ack18这里 PSH 标志是 Push意思是“请把数据立即交给应用程序”不要等缓冲区填满。Len17 是数据负载长度。服务端收到后回一个纯 ACKAck18说明它期望客户端下一个包从序号 18 开始。展开 TCP 头你可以看到确认号变成了 18正好是“上一个 Seq Len”。服务端随后回发hello from server客户端同样回一个 ACKSeq 和 Ack 各自推进。这个“一来一回确认号跟着数据长度走”的规律是 TCP 可靠传输的核心。课堂上讲的累积确认、滑动窗口在这里都有了直观载体。你可以故意把客户端 sendall 改成长文本抓包看到 TCP 分段再对比 MSS。2.4 四次挥手与连接关闭客户端脚本执行cli.close()后会触发连接关闭。正常情况下抓包能看到四次挥手客户端 - 服务端FIN ACKSeq18服务端 - 客户端ACKSeq1Ack19服务端 - 客户端FIN ACKSeq1Ack19客户端 - 服务端ACKSeq19Ack2注意如果服务端在收到 FIN 后立刻关闭第二步和第三步可能会合并成“带数据的确认包 FIN”所以实际抓包不一定是四个包。课堂上说的四次挥手是典型过程但要理解本质主动关闭方说“我没数据了”被动关闭方先确认再把自己的数据发完最后也说“我也没数据了”主动方再确认。关闭之后主动关闭方会进入 TIME_WAIT 状态抓包界面最后还有一个灰色的四重组记录。TIME_WAIT 的目的是让网络中迟到的报文自然消失同时保证最后一个 ACK 万一丢失还能重传。理论上等待 2 个 MSL实验时你可以用命令查看端口状态但不用纠结拿不到包。2.5 TCP 实验实操避坑第一个坑服务端 accept 之后只收一次数据如果你的 sendall 和 recv 次数不匹配另一方可能阻塞在 recv连接一直不关闭。做实验时先设计好收发次数。第二个坑Windows 防火墙可能拦截 127.0.0.1 上的监听但通常少见如果客户端连接超时先检查服务端是否打印了 “client connected”再检查端口是否被占用。第三个坑Wireshark 默认的相对序列号可能在分析重传时产生困惑如果想看真实序列号可以取消勾选“TCP - Relative sequence numbers”。第四个坑抓包时不要开迅雷之类的下载工具否则大量并发连接会淹没你的实验包。3. UDP 套接字捕包分析无连接的对话3.1 构建一对 UDP Socket 程序UDP 套接字代码比 TCP 更短。服务端绑定 9999 端口等待接收一个数据报原样回一条消息import socket s socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.bind((127.0.0.1, 9999)) print(udp server waiting...) data, addr s.recvfrom(1024) print(received:, data, from, addr) s.sendto(bhello from udp server, addr)客户端直接向 9999 端口发数据不需要 connect也可以 recvfrom 等待回应import socket c socket.socket(socket.AF_INET, socket.SOCK_DGRAM) c.sendto(bhello udp, (127.0.0.1, 9999)) data, addr c.recvfrom(1024) print(response:, data, from, addr) c.close()抓包时捕获过滤器写udp port 9999。运行后正常情况下只会看到两个 UDP 包一个从客户端随机端口发往 9999另一个从服务端 9999 发往客户端的临时端口。全程没有握手包、没有 ACK干净利落。3.2 UDP 数据报头部逐个字段看点开第一个 UDP 包协议树会展开四个字段Source Port、Destination Port、Length、Checksum各占 16 位。客户端源端口是操作系统分配的临时端口目的端口是 9999。Length 是 UDP 头部加数据的总长度比如hello udp是 9 字节数据头部 8 字节Length 就是 17。Checksum 字段在 IPv4 下可以填 0表示发送方不计算校验和但大多数系统都会计算。校验时会将 IP 伪头部、UDP 头部和数据一起算覆盖源 IP、目的 IP、协议号以及 UDP 总长度防止数据报在传输中损坏。Wireshark 会直接显示校验结果是否通过你可以看到 “Checksum: 0x... [correct]”。UDP 的最大特点是无状态。服务端 recvfrom 拿到数据报后只能看到“从哪个地址来的”没有连接上下文也不需要维护发送缓冲区、接收窗口和重传状态。这也是为什么 UDP 头部这么轻但应用层要自己处理丢包和乱序。3.3 UDP 与 TCP 抓包特征对比对比维度TCPUDP建立连接需要三次握手无需握手连接状态双方维护复杂状态机无连接状态可靠性序号、确认、重传、窗口无确认无重传头部开销至少 20 字节固定 8 字节抓包特征SYN、ACK、FIN、PSH 一系列交互一包请求一包响应典型协议HTTP、FTP、SMTPDNS、DHCP、视频流实际抓包时TCP 流可以用 Wireshark 的“Follow TCP Stream”重组出一个完整字节流UDP 则没有“流”的概念只能按五元组硬拼。实验时不要用分析 TCP 的思路套 UDP看到两个报文一来一回就是完整的 UDP 通信过程。4. HTTP 协议抓包分析一次 Web 请求的完整旅程4.1 用本地 HTTP 服务构造明文请求分析 HTTP 最忌讳直接抓公网流量因为现代网站普遍启用 HTTPS抓到的内容是加密的哪怕是 Wireshark 也看不到明文报文。课程实验要的是看清协议文本所以我建议在本地起一个 HTTP 服务。先创建一个测试目录里面放一个简单的index.htmlhtmlheadtitletest/title/headbodyh1hello http/h1/body/html然后在该目录执行python -m http.server 8080 --bind 127.0.0.1接着用 curl 发起一次请求curl -v http://127.0.0.1:8080/index.htmlWireshark 显示过滤器填http or tcp.port 8080开始抓包后重新执行 curl。你会得到一连串包先是一个完整的 TCP 三次握手然后有请求报文紧接着响应报文。HTTP 是纯文本协议直接看包列表的 Info 列就能看到 “GET /index.html HTTP/1.1” 和 “HTTP/1.1 200 OK”。4.2 请求报文与响应报文逐行拆解HTTP 请求报文由请求行、首部行、空行和可选消息体组成。抓到的请求展开后关键行是请求行GET /index.html HTTP/1.1Host127.0.0.1:8080User-Agentcurl/...Accept*/*空行表示头部结束Host 字段特别重要。HTTP/1.1 允许一台服务器托管多个域名虚拟主机靠 Host 区分。即使你访问的是 IP 加端口Host 也必须带上端口号。User-Agent 标识客户端类型服务端可以据此做内容协商。空行虽然不是头部字段但在报文中是真实存在的两个字节\r\n\r\n很多协议解析问题就出在这里。响应报文首行是状态行HTTP/1.1 200 OK。然后是响应头ServerSimpleHTTP/0.6 Python/3.xContent-Typetext/htmlContent-Length报文体字节数最后是空行和 HTML 正文。Content-Length 告诉接收方消息体有多长这样 HTTP 接收方不需要靠空行判断结束。如果响应是Connection: keep-alive同一个 TCP 连接还可以继续发第二个请求你可以在实验里连续 curl 两个文件观察第二次请求时是否复用了之前的 TCP 连接。4.3 用 Follow TCP Stream 快速还原完整会话Wireshark 有一个极其好用的功能选中任意一个 HTTP 包右键选择“Follow TCP Stream”弹窗会把同一个 TCP 连接上所有数据按时间顺序重组成完整文本。左边是客户端发送的内容右边是服务端返回的内容请求头和响应头一目了然。这个功能尤其适合教学演示。哪怕你只看懂零散报文Follow TCP Stream 一开协议交互过程就变成了一段可读的对话。注意只有 HTTP/1.x 明文流量才能这样直接读HTTP/2 是二进制分帧流重组里可能是一堆乱码。实验里我们刻意用 Python 自带服务就是为了绕开这套复杂度。5. FTP 协议抓包分析控制连接和数据连接的双通道5.1 快速搭建本地 FTP 并登录FTP 是个老协议但设计非常经典值得拆开看。为了快速复现我推荐使用 pyftpdlib 搭一个本地匿名 FTP。安装依赖pip install pyftpdlib启动服务cd /tmp mkdir ftpdata python -m pyftpdlib -p 2121 -d /tmp/ftpdata -w-w表示允许匿名登录-d指定共享目录。记住这里端口故意用 2121避免和系统默认 FTP 冲突。然后打开另一个终端用 curl 模拟客户端curl -v ftp://127.0.0.1:2121/或者用系统自带ftp命令ftp 127.0.0.1 2121抓包时过滤器写ftp or ftp-data or tcp.port 2121。开始抓包后再执行登录命令。你会看到控制连接上依次出现220 FTP Server ReadyUSER anonymous331 Anonymous login ok, send your complete email address as your passwordPASS anonymous230 Login successfulSYST、PWD、TYPE I、PASV、LIST 等后续命令这些命令和响应都是明文文本Wireshark 的 Info 列直接显示命令内容。5.2 控制连接与数据连接PASV 模式下的二级连接FTP 最独特的地方在于它使用两条 TCP 连接控制连接和数据连接。控制连接固定走 21 端口这里改成 2121用来传命令和状态码数据连接是临时的用于传输目录列表或文件内容。FTP 有两种建立数据连接的模式。主动模式下客户端通过 PORT 命令告诉服务端“我临时开放了某个端口”服务端主动去连接这个端口。被动模式下客户端发 PASV服务端回复227 Entering Passive Mode (127,0,0,1,p1,p2)告诉客户端“我开放了某个端口你来连接”。现代网络环境下被动模式更常见因为客户端不必开放入站端口。抓包时如果看到PASV和227接着就会出现一条新的 TCP 连接目标端口就是服务端在 227 响应里用两个字节拼出的端口。Wireshark 会把这些数据包标记为 FTP-DATA。传输完文件后数据连接通常立即关闭。所以一次文件传输会经历“控制连接命令 数据连接握手 数据传输 数据连接挥手”的完整过程。5.3 FTP 明文凭据的安全启示观察抓包结果你会发现FTP 的PASS命令后面跟着明文密码。在真实网络环境中任何中间设备都能直接读取这些内容。这就是为什么现代场景下常用 SFTP 或 FTPS 来加密传输。做这个实验不是为了鼓励使用 FTP而是为了直观理解“明文协议”的风险。抓包课程里看到明文验证码往往比课本上几百字的安全论述更有冲击力。这个安全点也可以迁移到 HTTPHTTP 同样是明文Cookie、登录密码都可能被抓到。HTTPS 在 TCP 之上加入了 TLS 加密Wireshark 里只能看到 Application Data 字段看不到明文内部结构。6. DNS 协议抓包分析域名解析的幕后6.1 使用 nslookup 构造一条 DNS 查询DNS 是典型的“看起来简单拆开很丰富”的协议。实验时用nslookup example.com或者 Linux 下的dig example.com发起解析请求然后抓包过滤器写dns或者udp port 53。运行nslookup example.comWireshark 里会出现两个包一个是客户端发出的 DNS 查询另一个是本地 DNS 服务器返回的响应。查询包的目的端口是 53源端口是随机临时端口。响应包的源端口是 53目的端口和查询包一致。DNS 默认走 UDP但如果响应数据超过 UDP 载荷能力会转向 TCP 53。课程实验通常只需关心 UDP 场景。6.2 DNS 报文的结构化字段展开 DNS 查询包报文结构清晰可见。最上面是 DNS Header固定 12 字节Transaction ID0x1234 之类的随机值用于匹配请求和响应Flags查询标志比如 0x0100 表示标准查询且 RD递归期望置位QDCOUNT问题区记录数通常是 1ANCOUNT应答区记录数查询时是 0NSCOUNT、ARCOUNT权威区和附加区记录数展开下面的 Queries 区域可以看到 QNAME 是按“长度标签”编码的。比如example.com编码成07 example 03 com 00每段前面一个字节表示标签长度最后以 0x00 表示域名结束。QTYPE 是 1表示 A 记录QCLASS 是 1表示 Internet 类。响应包除了 Header 中的 QR 变为 1、RA 置 1Answers 区域会包含解析结果。Answer 里的 NAME 经常显示为0xc00c这样的压缩指针前两位二进制 11 表示这是一个指针后面的 14 位指向报文中的某个偏移位置从而复用之前出现的域名避免重复存储整个域名。6.3 TTL 与递归解析的观察响应中的 TTL 字段表示这条记录可以在本地缓存多久单位是秒。比如 TTL3600意味着这一小时内客户端或本地解析器再次查询该域名时可以直接用缓存回答不需要再次发起递归查询。再次执行nslookup example.com抓包可能看到查询包但如果本地缓存命中响应包可能直接从负缓存或内存解析器返回不一定经过网络。用普通nslookup只能看到客户端与本地 DNS 服务器之间的一问一答。想看递归过程可以在 Linux 下执行dig trace example.com它会从根服务器开始依次查询顶级域服务器、权威服务器每一级迭代都会产生独立的 DNS 包。此时再过滤dns能看到多个 Transaction ID、多个响应能直观理解递归和迭代的差别。6.4 DNS 实验的一些实操心得DNS 报文看起来字段多但核心只需要看四块Transaction ID、Flags、Queries、Answers。先确定请求和响应的 ID 一样再确认响应里 Answer 的地址和预期一致最后看一眼 TTL整个实验就完成了。抓 DNS 时不要开着系统里其他后台应用否则邮件、即时通讯的域名查询会混进来干扰判断。我个人的习惯是命令行里先清缓存再抓包。Windows 下ipconfig /flushdnsLinux 下根据发行版不同重启解析器服务或者干脆等 TTL 过期。这样每次抓包都是“冷查询”报文里的 TTL 和服务器信息更完整。最后分享一个小技巧做实验报告时不要只贴 Wireshark 截图可以用tshark -r 文件.pcapng -Y http -T fields -e frame.number -e ip.src -e ip.dst -e http.request.method -e http.request.uri把关键字段导成 CSV整理成表格后放进报告里清晰又省空间。抓包分析这件事本质上就是“用什么工具不重要能不能把协议状态机对应到真实报文”才重要。多抓几次多对照 RFC 和课本等你能不看工具说明就准确读出 SYN、ACK、PASV、递归标志时计算机网络这门课最硬的部分就真的过去了。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑