回环流量不经过网卡,所以普通网卡抓不到——这是 localhost 抓包失败的唯一原因。 换一块专门的环回适配器(Windows 上的 Npcap Loopback,Linux 上的 lo)就解决了。
2.8.1 装好了工具,这一节解决「抓不到本机流量」这个最常见的卡点。 2.8.3 开始读这些包。
一、为什么抓不到 localhost
127.0.0.1 的流量不走网卡。
操作系统在协议栈里直接把数据从发送缓冲区搬到接收缓冲区,整条路径完全在内核内存里完成。
所以挂在网卡上的抓包驱动,一帧都看不到。 不是配置错了,是物理上就没经过那里。
要抓它,只能用「假网卡」:
① Windows:Npcap 会装一块叫 Npcap Loopback Adapter 的虚拟网卡。它不连任何硬件,只拦回环那一层。
② Linux:lo 接口本来就是干这个的,一直都在,直接选就行。
③ macOS:也是 lo0。
这条知识在调试流媒体时特别值钱: 大部分人先在单机上跑通(服务器和客户端都在 127.0.0.1),这时候抓不到包,就会误以为「代码没发数据」——其实是抓错了地方。
二、Windows 上要确认的两件事
① 安装 Npcap 时勾「Support loopback traffic (Npcap Loopback Adapter)」。
这个选项默认是勾上的,但如果你之前装过老版本可能没有。
补救办法是重跑 Npcap 安装包(在 Wireshark 目录下能找到 npcap-*.exe),勾上再装一次,Wireshark 不用重装。
② 抓包界面上要能看到 Adapter for loopback traffic capture。
如果只有以太网和 WLAN 两块,就说明没装成。
还有一个坑:Windows 上有时候会有多块「回环」条目,包括 Hyper-V、WSL、VMware 造的虚拟网卡。 认准名字里带 Loopback 的那一块,别选成 vEthernet。
三、Linux 上一条命令就够
sudo tshark -i lo -f "tcp port 554" -w /tmp/rtsp.pcap
三个参数:
① -i lo:指定回环接口。不加 -i 它会抓所有接口,噪声大得多。
② -f "tcp port 554":BPF 过滤。注意这是「抓的时候过滤」,比抓下来再筛效率高得多——大流量下这是必须的。
③ -w /tmp/rtsp.pcap:写文件。不写 -w 就直接刷屏,几百行一秒,看不清。
BPF 写法值得记几个:
host 192.168.1.64:只抓这台机器。
port 554:只抓 554 端口(RTSP)。
tcp and port 554:组合条件,and/or/not 都能用。
udp and portrange 5000-6000:抓一段端口里的 UDP,这就是抓 RTP 的写法(第 3 章会用到)。
四、环回的两个特点会影响你的判断
抓到回环包之后,有两点必须知道,否则会得出错误结论。
① 延迟极低。 回环上的往返是百微秒级(实测 94~172 微秒),而真实网络里一个 RTT 就是几十毫秒。 所以别拿回环的数字去估算线上性能——差一百倍不奇怪。
② 不经过网卡,所以没有以太网的某些字段。 回环适配器给的帧通常还是以太网格式,但前导码、CRC 这些根本没有。 所以你在回环上算出来的「每包多少字节」会比真实网络少几字节。
还有一个正面好处:回环上没有其他流量干扰。 真实网卡上跑着浏览器、微信、系统更新,一秒钟几百个包;回环上只有你自己那一对进程,干净得多。 所以学习协议时,先在回环上抓,再上真实网络。
五、动手 Demo:环回上真跑一遍,逐条记账
本机起一个 RTSP 服务器,客户端拉四步,把每一对报文的上下行字节都记下来。 顺便算一下:这些字节在真实网络上还要加多少层壳。
import socket, threading, time
SID = "1A2B3C4D"
LOG = []
def handle(txt):
head = txt.split("\r\n")
meth = head[0].split(" ")[0]
hd = dict(l.split(": ", 1) for l in head[1:] if ": " in l)
cseq = hd.get("CSeq", "0")
if meth == "DESCRIBE":
sdp = "v=0\r\ns=Live\r\nm=video 0 RTP/AVP 96\r\na=control:trackID=0\r\n"
return ("RTSP/1.0 200 OK\r\nCSeq: %s\r\nContent-Type: application/sdp\r\n"
"Content-Length: %d\r\n\r\n%s" % (cseq, len(sdp), sdp))
if meth == "SETUP":
return ("RTSP/1.0 200 OK\r\nCSeq: %s\r\nTransport: RTP/AVP;unicast;"
"client_port=5000-5001;server_port=6000-6001\r\n"
"Session: %s;timeout=60\r\n\r\n" % (cseq, SID))
return "RTSP/1.0 200 OK\r\nCSeq: %s\r\nSession: %s\r\n\r\n" % (cseq, SID)
def serve(sock):
c, _ = sock.accept()
buf = b""
while True:
d = c.recv(8192)
if not d:
return
buf += d
while b"\r\n\r\n" in buf:
req, buf = buf.split(b"\r\n\r\n", 1)
c.sendall(handle(req.decode()).encode())
srv = socket.socket()
srv.bind(("127.0.0.1", 0))
srv.listen(4)
port = srv.getsockname()[1]
threading.Thread(target=serve, args=(srv,), daemon=True).start()
s = socket.create_connection(("127.0.0.1", port), 3.0)
for i, m in enumerate(("OPTIONS", "DESCRIBE", "SETUP", "PLAY"), 1):
req = ("%s rtsp://127.0.0.1/live/test RTSP/1.0\r\nCSeq: %d\r\n\r\n" % (m, i))
t0 = time.perf_counter()
s.sendall(req.encode())
r = s.recv(8192)
us = (time.perf_counter() - t0) * 1e6
LOG.append((m, len(req.encode()), len(r), us))
s.close()
for m, up, down, us in LOG:
print("%-9s 上行 %3d 下行 %3d 往返 %.0f 微秒" % (m, up, down, us))
print("合计 上行 %d 字节 下行 %d 字节"
% (sum(x[1] for x in LOG), sum(x[2] for x in LOG)))
print("加三层壳之后,一条 60 字节的请求在线上是 60+14+20+20 = 114 字节")
实跑输出:
--------------------------------------------------------------------
抓本地包——环回上的流量长什么样
--------------------------------------------------------------------
① 环回上真跑一遍,逐条记账
# 请求 上行字节 下行字节 往返
---------------------------------------------------------
1 OPTIONS 56 115 1610 微秒
2 DESCRIBE 57 280 172 微秒
3 SETUP 54 130 145 微秒
4 PLAY 72 122 94 微秒
**四条请求一共上行 239 字节、下行 647 字节。**
**注意 DESCRIBE 的下行最大,因为它带回了一份 SDP。**
② 在 Wireshark 里你看到的是同一批东西,只是多了三层壳
层 多出来多少字节 内容
--------------------------------------------------------------------
以太网帧头 14 源 MAC、目的 MAC、类型
IP 头 20 源 IP、目的 IP、协议号、TTL
TCP 头 20 源端口、目的端口、序号、标志位
RTSP 报文 变化的 我们上面记的那些
**一条 60 字节的 RTSP 请求,在线上实际是 60 + 54 = 114 字节。**
**以太网还有 7 字节前导码和 4 字节校验,不过抓包通常看不到。**
③ 环回的两个特点
**① 不经过网卡**:数据从发送缓冲区直接进接收缓冲区。
**所以抓包工具要用专门的环回适配器,普通网卡抓不到。**
**② 延迟极低**:上面几个往返都是百微秒级,**而真实网络里单个 RTT 就是几十毫秒。**
**所以别拿环回的数字去估算线上性能。**
四条请求上行合计 239 字节、下行 647 字节,体量和 2.7 算的完全对得上。 DESCRIBE 的下行最大(280 字节),因为它是唯一带 SDP 正文的一步。 往返都是百微秒级——这是回环的特权。真网络上单个 RTT 就是几十毫秒,别拿这个数字估算线上性能。
30 秒实验
- 把
client_port换成5000-5001之外的值,看上下行字节变不变——只有报文里的数字变了,长度不该变。 - 用
tshark -i lo -f "tcp port <端口>" -w a.pcap抓一次,再用 Wireshark 打开,数一数是不是四对报文。 - 在客户端和服务端之间加
time.sleep(0.05),看往返时间怎么变——这就是「时间戳是抓包能给的唯一增量信息」。
常见误区
- 「抓不到是本机没流量」——回环不进网卡,普通网卡抓不到很正常。换环回适配器。
- 「
-f过滤和显示过滤是一回事」——-f是 BPF,抓的时候就丢;Wireshark 输入框里的是显示过滤,抓完再筛。大流量下一定要用-f。 - 「回环上的延迟能代表线上」——差一百倍。回环建连几乎零成本,真实网络要几十毫秒。
自测三问(点开对答案)
1. 为什么普通网卡抓不到 127.0.0.1 的包? —— **回环流量在内核里直接搬,不经过网卡。**要用环回适配器。 2. Windows 上要选哪块网卡? —— **名字里带 Loopback 的那块**(Npcap Loopback Adapter)。**别选成 vEthernet。** 3. `-f "tcp port 554"` 和 Wireshark 输入框里的过滤有什么不同? —— **前者在抓的时候就丢包(BPF),后者是抓完再筛。****大流量下必须用前者。**- ☐ (本节完,下一节 2.8.3 使用Wireshark抓包分析RTSP交互流程)