简介:这份资源是计算机网络原理课程的Wireshark实验报告,面向正在学习HTTP协议、需要完成抓包分析作业的高校学生与网络初学者。报告以访问百度为例,完整呈现了从清除浏览器缓存、捕获三次握手到解析请求与响应报文的全过程,并逐项拆解请求行、请求头部、空行与请求正文,以及状态行、响应头部、响应体等结构,同时归纳了Host、User-Agent、Connection、Content-Type、Content-Length等常用首部字段的含义与作用。资源包内含1个docx文档,压缩后约223KB,体积轻便,便于直接查阅与二次整理。目前已有9262人学习下载,说明其在同类实验报告中具有较高的参考价值。读者可借此快速理解HTTP请求/响应模型、状态码分类及TCP/IP分层抓包思路,为网络调试、性能优化与安全分析打下基础,也适合作为实验报告撰写的模板与排错参考。
1. 从一次 302 说起:Wireshark 抓 HTTP 到底在抓什么
很多人第一次用 Wireshark 抓 HTTP,打开浏览器访问百度,抓完一看,响应码是 302,不是课本上写的 200 OK,于是开始怀疑自己是不是抓错了端口、选错了网卡。其实这不是抓包翻车,而是浏览器缓存和 HSTS 在起作用——你访问的www.baidu.com被本地缓存或强制跳转接管了,真正的 HTTP 请求根本没发出去,或者发出去的是跳转指令。这份实验报告资源解决的正是这个问题:它用 Wireshark 对 WLAN 端口抓包,完整走一遍访问网站的过程,把 HTTP 请求报文和响应报文的每一层结构拆开给你看,从 Frame 物理层一路看到 Hypertext Transfer Protocol 应用层。适合正在做计算机网络原理实验的学生,也适合想搞懂 HTTP 协议真实报文长什么样的网络测试从业者。关键词就三个:Wireshark、HTTP 协议、网络协议分析,但要把它们串起来,得先过缓存这一关。
2. 抓包前的环境准备:WLAN 端口选择与缓存清理
2.1 为什么必须选 WLAN 而不是以太网
Wireshark 安装完之后,接口列表里会列出所有网卡。如果你用的是笔记本连 Wi-Fi,要抓的是无线网卡对应的接口,通常名字里带 "WLAN" 或 "Wi-Fi"。选错接口的后果是:抓到的包全是本地回环或者干脆一片空白。常见做法是打开 Wireshark 后先看接口列表的实时流量波形,哪个有波动就选哪个。如果用的是有线连接,那就选以太网接口,但实验报告里明确要求对 WLAN 端口抓包,所以无线网卡是首选。
选好接口后不要急着点开始,先做两件事:一是关闭其他占用网络的程序,比如正在同步的网盘、后台更新的软件,否则抓到的包会混入大量无关流量;二是确认 Wireshark 的捕获过滤器留空,先抓全量,后面再用显示过滤器筛 HTTP。捕获过滤器写tcp port 80虽然能减少噪音,但会漏掉三次握手的前两个包,对分析完整流程不利。
2.2 清缓存这一步省不得
实验报告里有一句血泪经验:第一步应当是清除浏览器缓存,否则看不到完整的三次握手,响应报文的状态码也会是 302 而不是 200 OK。原因在于,浏览器访问过的资源会存在本地缓存里,再次访问时直接读缓存,根本不发 HTTP 请求;即使发了,服务器也可能返回 302 重定向到 HTTPS 或者别的地址。清缓存的操作因浏览器而异,Chrome 里是Ctrl+Shift+Delete,勾选"缓存的图片和文件",时间范围选"全部"。更彻底的做法是开一个无痕窗口,无痕模式默认不读本地缓存,但注意无痕模式仍然可能受 HSTS 影响。
提示:如果清了缓存还是 302,检查一下地址栏是不是自动跳到了 HTTPS。HTTP 实验要访问的是
http://开头的地址,不是https://。
2.3 启动抓包并触发 HTTP 请求
环境准备好之后,操作顺序很关键:
# 第一步:在 Wireshark 里选中 WLAN 接口,点击开始捕获 # 第二步:切换到浏览器,输入 http://www.baidu.com 并回车 # 第三步:页面加载完成后,回到 Wireshark 点击停止捕获 # 第四步:在显示过滤器栏输入 http,回车筛选这四步看起来简单,但顺序错了就白抓。必须先开捕获再访问网站,否则请求已经发完了你才开始抓,什么都看不到。停止捕获的时机也要注意,等页面完全加载完再停,不然响应体可能还没传完。筛选用的显示过滤器http是 Wireshark 内置的协议过滤器,输入后只会显示 HTTP 协议的包,TCP 握手包和 TLS 包会被隐藏。如果想看完整的 TCP 三次握手,把过滤器清空,找到第一个 SYN 包,右键选择 "Follow TCP Stream",就能看到这条 TCP 连接上的所有数据。
3. 逐层拆解 HTTP 报文:从 Frame 到 Hypertext Transfer Protocol
3.1 双击第 258 号分组看到了什么
实验报告里双击的是分组号 258 的那条记录,弹出的窗口按协议层次展开,从上到下依次是:
| 层次 | 协议 | 含义 |
|---|---|---|
| 物理层 | Frame | 数据帧概况,包含帧编号、捕获时间、帧长度 |
| 数据链路层 | Ethernet II | 以太网帧头部,包含源 MAC 和目的 MAC |
| 网络层 | Internet Protocol Version 4 | IP 包头,包含源 IP 和目的 IP |
| 传输层 | Transmission Control Protocol | TCP 段头,包含源端口、目的端口、序列号 |
| 应用层 | Hypertext Transfer Protocol | HTTP 报文内容 |
这个层次结构不是 Wireshark 随便排的,它严格对应 TCP/IP 四层模型。Frame 是 Wireshark 自己加的,用来描述这个包的物理属性;Ethernet II 是链路层封装;IP 层负责寻址和路由;TCP 层负责可靠传输;HTTP 层才是应用真正关心的内容。每一层都包裹着上一层的数据,这就是封装。理解了这个结构,再看 HTTP 报文就不会觉得它是一团乱码了。
3.2 请求报文的三段式结构
HTTP 请求报文的固定格式分三部分:请求行、请求头部、空行,可选部分请求正文。实验报告里给出的请求报文实例是:
POST /cgi-bin/httpconn HTTP/1.1\r\n Host: 220.194.118.239\r\n Accept: */*\r\n User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)\r\n Connection: Keep-Alive\r\n Cache-Control: no-cache\r\n Accept-Encoding: gzip, deflate\r\n Content-Type: application/octet-stream\r\n Content-Length: 228\r\n \r\n [请求正文 228 字节]逐行拆开看:第一行POST /cgi-bin/httpconn HTTP/1.1是请求行,三个字段用空格分隔——请求方法 POST、请求 URI/cgi-bin/httpconn、协议版本 HTTP/1.1。接下来的每一行都是"关键字: 值"对,这是请求头部。Host告诉服务器要访问哪台主机,User-Agent携带浏览器和操作系统信息,Connection: Keep-Alive表示使用持久连接,Content-Length: 228说明请求正文有 228 字节。最后那个空行\r\n是分隔符,空行之后就是请求正文。GET 请求没有正文,所以空行之后直接结束;POST 请求的正文就是提交的数据。
3.3 响应报文的四段式结构
响应报文比请求报文多一段,格式是:状态行、响应头部、空行、响应体。实验报告里的响应报文实例:
HTTP/1.1 200 OK\r\n Server: httpsf2\r\n Connection: Keep-alive\r\n Content-Type: text/octet\r\n Content-Length: 143\r\n \r\n [响应体 143 字节]状态行HTTP/1.1 200 OK同样三个字段:协议版本、状态码、状态码描述。状态码是三位数字,200 到 299 表示成功,300 到 399 表示重定向,400 到 499 表示客户端出错,500 到 599 表示服务端出错。实验报告里特别提到,HTTP/1.1 引入了 100 到 199 的信息性状态码。响应头部里的Content-Type告诉浏览器返回的数据是什么类型,Content-Length告诉浏览器数据有多大。空行之后是响应体,通常是一段 HTML、图片或其他资源。
3.4 用 Python 解析原始报文
如果想脱离 Wireshark 自己解析 HTTP 报文,可以用 Python 的 socket 模块手动构造和解析。下面这段代码演示了如何发送一个 GET 请求并打印响应:
import socket # 构造 HTTP 请求报文 request_line = "GET / HTTP/1.1\r\n" headers = "Host: www.baidu.com\r\nConnection: close\r\n\r\n" request = request_line + headers # 建立 TCP 连接并发送请求 with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.connect(("www.baidu.com", 80)) s.sendall(request.encode()) # 接收响应数据 response = b"" while True: chunk = s.recv(4096) if not chunk: break response += chunk # 分离响应头和响应体 header_part, _, body_part = response.partition(b"\r\n\r\n") print("=== 响应头 ===") print(header_part.decode(errors="ignore")) print("=== 响应体长度 ===") print(len(body_part))这段代码的逻辑是:先拼出请求行和请求头,注意Connection: close告诉服务器发完就关连接,这样recv才能读到 EOF 退出循环。partition(b"\r\n\r\n")用空行把响应头和响应体切开,前半部分是状态行加响应头,后半部分是响应体。参数方面,端口 80 是 HTTP 默认端口,如果访问 HTTPS 要改成 443 并加 SSL 包装。这段代码跑出来的结果可以和 Wireshark 抓到的包对照,验证你对报文结构的理解是否正确。
4. 请求头与响应头:哪些字段真正影响行为
4.1 常用请求头的实际作用
实验报告列了一长串请求头,但真正影响服务器行为的没几个。Host是 HTTP/1.1 里唯一 mandatory 的请求头,没有它服务器不知道你要访问哪个虚拟主机。User-Agent决定服务器返回桌面版还是移动版页面,用浏览器开发者工具改成手机 UA 就能看到不同结果。Accept-Encoding告诉服务器支持哪种压缩格式,服务器如果返回Content-Encoding: gzip,Wireshark 里看到的响应体是压缩后的二进制,需要导出后解压才能读。Cookie是身份凭证,服务器靠它识别用户会话,抓包时能看到明文 Cookie 说明没用 HTTPS,这也是为什么登录操作必须走 HTTPS。
Connection头控制连接是否复用,Keep-Alive表示请求完不关连接,后续请求复用同一条 TCP 连接,减少握手开销。Cache-Control: no-cache不是不缓存,而是每次使用缓存前必须向服务器验证。Pragma头在实验报告里提到"一般仅用于开发模式",这个说法是准确的,它和Cache-Control功能重叠,现代浏览器优先认后者。
4.2 常用响应头的排查价值
响应头里对调试最有用的几个:Content-Type决定浏览器怎么渲染响应体,如果服务器返回text/html但浏览器下载了文件,说明 MIME 类型配错了。Content-Length和实际收到的字节数对不上,说明传输被截断。Location配合 302 状态码使用,告诉你重定向到了哪个地址,实验里遇到 302 时看这个头就知道跳去哪了。Set-Cookie是服务器给浏览器种 Cookie,抓包时能看到 Cookie 的 name、value、domain、path、过期时间。Last-Modified和If-Modified-Since配合实现缓存验证,浏览器第二次请求同一资源时带上If-Modified-Since,服务器比对后如果没变化就返回 304,不传响应体,省带宽。
4.3 用显示过滤器精准定位报文
Wireshark 的显示过滤器是分析 HTTP 的核心技能。常用的几个:
# 只看 HTTP 请求 http.request # 只看 HTTP 响应 http.response # 按请求方法筛选 http.request.method == "POST" # 按状态码筛选 http.response.code == 200 # 按主机名筛选 http.host == "www.baidu.com" # 按 URI 筛选 http.request.uri contains "cgi-bin" # 组合条件:只看百度的 GET 请求 http.host == "www.baidu.com" && http.request.method == "GET"这些过滤器的参数含义很直白:http.request.method对应请求行里的方法字段,http.response.code对应状态行里的状态码,http.host对应 Host 请求头。组合用&&表示与,||表示或。实际排查时,先抓全量,再用过滤器缩小范围,比一开始就设捕获过滤器灵活得多。如果过滤器写错了,Wireshark 的过滤栏会变红,鼠标悬停能看到错误提示。
5. 避坑与排查:五个让实验翻车的细节
5.1 抓不到包或包列表为空
现象:点了开始捕获,浏览器也访问了网站,但 Wireshark 里一条记录都没有。原因通常是选错了网卡接口,或者捕获过滤器写得太严把包全滤掉了。解决:停止捕获,重新选接口,看接口列表里哪个有流量波形;检查捕获过滤器是否为空,不确定就清空重来。
5.2 响应码是 302 不是 200
现象:清缓存后访问百度,响应码仍然是 302。原因有两个:一是浏览器地址栏自动把http://跳成了https://,二是 HSTS 策略强制跳转。解决:手动输入完整的http://www.baidu.com,不要只输www.baidu.com;如果还不行,换一个明确支持 HTTP 的网站,比如http://example.com或者学校的内网地址。
5.3 看不到三次握手
现象:筛选http后只看到 HTTP 请求和响应,看不到 SYN、SYN-ACK、ACK。原因:显示过滤器http把 TCP 包过滤掉了。解决:清空过滤器,找到第一个 HTTP 请求包,右键选择 "Follow TCP Stream",Wireshark 会自动关联这条 TCP 连接上的所有包,包括握手和挥手。
5.4 响应体是乱码
现象:展开 HTTP 响应报文,响应体显示为乱码或二进制。原因:服务器返回了压缩内容,Content-Encoding是gzip或deflate,Wireshark 默认不解压。解决:在 Wireshark 偏好设置里找到 Protocols → HTTP,勾选 "Uncompress entity bodies",重启捕获后响应体会自动解压。或者导出原始数据,用gzip -d手动解压。
5.5 时间戳不是北京时间
现象:Wireshark 的 Time 列显示的是 UTC 时间,和本地时间差 8 小时。原因:Wireshark 默认用 UTC 显示时间戳。解决:View → Time Display Format → Time of Day,然后在 Preferences → Appearance → Columns 里确认时间列格式。这个不影响协议分析,但写实验报告时时间对不上会很尴尬。
6. 进阶技巧:用 tshark 批量提取 HTTP 字段
Wireshark 图形界面适合交互式分析,但如果要批量处理抓包文件、提取特定字段做统计,命令行工具tshark更高效。它随 Wireshark 一起安装,在终端里直接调用。下面这条命令从抓包文件里提取所有 HTTP 请求的方法、主机和 URI:
tshark -r capture.pcap -Y "http.request" \ -T fields \ -e http.request.method \ -e http.host \ -e http.request.uri \ -E separator=,参数逐个解释:-r capture.pcap指定读取的抓包文件;-Y "http.request"是显示过滤器,只处理 HTTP 请求;-T fields表示输出为字段模式;-e后面跟字段名,每个-e提取一列;-E separator=,指定列分隔符为逗号,方便导入表格。跑出来的结果类似:
GET,www.baidu.com,/ GET,www.baidu.com,/favicon.ico POST,220.194.118.239,/cgi-bin/httpconn如果要统计每个状态码出现的次数,把过滤器改成http.response,提取http.response.code,再用sort | uniq -c管道处理:
tshark -r capture.pcap -Y "http.response" \ -T fields -e http.response.code | sort | uniq -c | sort -rn这条命令的输出会按出现次数从高到低排列状态码,一眼就能看出这次抓包里有多少 200、多少 302、多少 404。做实验报告时,这种统计比逐个包翻看效率高得多。我一般会在图形界面里定位到关键包,然后用 tshark 批量导出字段做汇总,两边对照,既不会漏细节,也能快速出结论。
还有一个实用技巧是导出特定 TCP 流的原始数据。在 Wireshark 里选中一个 HTTP 包,Follow TCP Stream 之后,左下角有个 "Save as" 按钮,能把这条流的所有数据存成二进制文件。如果响应体是图片或压缩包,存下来直接就能用。命令行等价操作是:
tshark -r capture.pcap -Y "tcp.stream eq 0" \ -T fields -e data.data | tr -d '\n' | xxd -r -p > stream0.bin这里tcp.stream eq 0指定第一条 TCP 流,-e data.data提取数据字段,tr -d '\n'去掉换行,xxd -r -p把十六进制转回二进制。这条命令的坑在于,如果流里有多个数据段,data.data会分多行输出,必须先拼在一起再转换,否则得到的文件是碎的。从那以后我每次导出 TCP 流都强制走一遍xxd -r -p验证文件头,确认不是半截数据才敢用。希望帮到你。
本文还有配套的精品资源,点击获取