ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Linux网络编程必会:Wireshark抓包实战与TCP排查技巧

Linux网络编程必会:Wireshark抓包实战与TCP排查技巧 搞Linux网络编程Wireshark 这个抓包工具是怎么都绕不开的。它强在哪不是能抓多少包而是抓完之后你能把 TCP 连接的每一次握手、每一段数据、每一个重传都看得清清楚楚。我自己这些年做 Linux 下的通信程序、排查线上连接问题几乎每次都得靠 Wireshark 和 tcpdump 配合先在远端抓包保存再拉到本地用 Wireshark 慢慢看。TCP 这协议你光看 RFC 文档和教科书描述永远觉得抽象但只要你亲手抓过一次三次握手和四次挥手seq 和 ack 的变化立刻就有了实感。这篇东西我不会跟你讲大而全的协议理论就围绕“Linux 软件编程 Wireshark TCP”讲实际操作怎么装、怎么抓、怎么看、遇到问题怎么排查。无论你是做应用开发、嵌入式还是刚转运维的新人只要你在 Linux 下写或者维护 TCP 通信相关的程序照着这套方法走一遍基本就能自己上手分析问题了。1. 环境准备与工具选型1.1 为什么 Linux 下首选 WiresharkWireshark 是目前最主流的开源网络分析工具本质就是一个抓包引擎加一个强大的图形分析前端。Linux 下其实还有 tcpdump、ngrep、tshark 这些命令行工具但为什么我首选 Wireshark因为它给出的“可读性”是命令行工具没法比的。它会把每个报文解析成多级协议树把 TCP 的标志位、序列号、窗口大小、选项字段全部拆开摆在界面上你双击一个包就能看到从 Ethernet 帧头一直到应用层数据的逐层细节。在服务器上图形界面可能不现实所以我会用 tshark 或者 tcpdump 抓包存成 pcap 文件再拿回本地用 Wireshark 看。这也正是 Wireshark 的另一个优势分析能力与抓包能力解耦它能读几乎所有常见的抓包格式而且分析时的过滤、统计、追踪流功能比命令行强太多。很多做嵌入式开发的朋友在板子上跑 tcpdump 抓包拷回电脑用 Wireshark 看这也是标准工作流。如果你完全没接触过抓包工具我建议你装好 Wireshark 之后先自己往本机发一个 HTTP 请求抓一下回环接口把三次握手、HTTP 请求响应、四次挥手整个过程看一遍。这一步做完你对 TCP 的理解会比啃三遍书都有效。1.2 安装方式和权限踩坑Linux 下安装 Wireshark 非常简单Debian/Ubuntu 系列用 aptRHEL/CentOS 系列用 dnf 或 yum# Debian/Ubuntu sudo apt update sudo apt install wireshark tshark # RHEL/CentOS/Fedora sudo dnf install wireshark-cli wireshark安装过程中 Debian 系会弹一个交互式对话框问你是否允许非 root 用户抓包选“Yes”。如果你当时没选或者装完之后想改可以用 dpkg-reconfigure 重新配置sudo dpkg-reconfigure wireshark-common这里有个很常见的坑很多新手装完 Wireshark用普通用户打开发现接口列表是空的或者抓包时提示权限不足。原因很简单抓包需要读取网络接口的原始数据帧这属于特权操作。Wireshark 的抓包引擎实际上调用了 libpcap而 libpcap 需要 root 权限或者对 /usr/bin/dumpcap 有特殊权限。Debian 系的安装包会创建一个叫 wireshark 的用户组把你自己加进去就能免 root 抓包sudo usermod -aG wireshark $USER加完组之后一定要重新登录一次或者开一个新的终端会话否则组权限不会生效。我更推荐的做法是给 dumpcap 设置 setcap 权限这样无需把用户加进任何组也能抓包sudo setcap cap_net_raw,cap_net_admineip /usr/bin/dumpcap设置之后普通用户直接开 Wireshark 就能抓到包省去加组、重登录这些麻烦。要注意Python 的 pcap 库、tcpdump 等工具同样受这些权限限制在脚本里跑抓包命令时如果提示权限问题优先想到这里。1.3 确认抓包环境别抓错网卡进 Wireshark 界面第一件事不是点“开始抓包”而是先看接口列表。Linux 下的接口名通常比较规矩eth0、ens33、enp0s3 这类是有线网卡wlan0 是无线网卡lo 是回环接口。如果你在虚拟机上做实验还可能出现 virbr0、docker0 之类的虚拟接口。新手最容易犯的错误就是“我明明在访问某个 IP为什么抓不到包”——多半是接口选错了。比如虚拟机里默认 NAT 网卡叫 ens33但你选了 lo当然什么都看不到。抓包之前先敲一下 ip addr 确认该用哪块网卡ip addr show如果是远程排查问题在服务器上通常要抓所有流量那就不要指定特定网卡直接用 tcpdump 抓任意接口或者抓具体端口sudo tcpdump -i any port 8080 -w /tmp/app.pcap抓完拉到本地用 Wireshark 打开再舒服不过。2. TCP 三次握手与四次挥手抓包拆解2.1 三次握手到底长什么样TCP 是面向连接的可靠传输协议建立连接要经历三次握手客户端先发 SYN服务端收到后回复 SYNACK客户端再回一个 ACK。理论上这是每个学过网络的人都背过的流程但实际报文里是什么样我建议你自己动手抓一次。最简单的验证方法是用 nc 或者在 Linux 下跑一个临时 TCP 服务终端一nc -l 127.0.0.1 8888终端二先用 tshark 抓回环接口sudo tshark -i lo -f tcp port 8888 -w /tmp/handshake.pcap然后终端三发起连接nc 127.0.0.1 8888连上之后 CtrlC 停掉抓包打开 /tmp/handshake.pcap。你会看到前三个包第一包客户端 127.0.0.1:随机端口 - 服务端 127.0.0.1:8888Flags 是 SYN。TCP 层的信息里Seq0 是相对序列号实际绝对序列号是一个随机大数Wireshark 默认显示相对序号是为了方便人看。看原始值可以右键 Preferences关掉 “Relative sequence numbers” 选项。第二包服务端回 SYNACK同时携带自己的 Seq0相对值和 Ack1表示期望收到客户端下一个序列号。第三包客户端发 ACKAck1连接建立。这里有个细节值得注意TCP 的 seq 表示当前报文第一个字节的序号ack 表示“我期望收到的下一个字节序号”。所以握手过程中SYN 本身虽然没有数据但它要消耗一个序号所以第二次握手的 ACK 值是 1而不是 0。我见过不少人在面试写这个以为是“客户端 seq0服务端 ack0”实际上抓包看一眼就明白ack 是 seq 1。在 Wireshark 里快速过滤三次握手也很方便显示过滤器写tcp.flags.syn 1 tcp.flags.ack 0这是只看 SYN 包也就是连接的发起方。如果要看 SYNACK要写成tcp.flags.syn 1 tcp.flags.ack 1这两个规则在排查大量网络中“连接是谁发起的”时非常实用。2.2 四次挥手的真实报文和 TIME_WAIT断开连接的抓包同样值得亲手做一次。在刚才 nc 建立的连接上直接在服务端按 CtrlC 结束 nc。Wireshark 里会看到后续这样的序列服务端先发 FIN客户端回 ACK稍后客户端再发 FIN服务端回 ACK。这就是经典的四次挥手。值得留意的点是中间顺序不一定完全固定。如果主动关闭一方发了 FIN对端可能还没发完数据所以先 ACK等自己的数据发完再发 FIN。这就是为什么 TCP 断开需要“四次”而不是“三次”因为 FIN 和 ACK 往往是分离的。如果两端同时都有数据要发完也可能看到 FINACK 合并的包实际传输中这些细节比教科书上更灵活。四次挥手之后还有个重要现象主动关闭的一方会进入 TIME_WAIT 状态一般在抓包里看到的最后一组 ACK 之后如果没有后续包那就是处于 TIME_WAIT 等待期。TIME_WAIT 持续 2MSLLinux 下默认 60 秒这是为了确保最后一次 ACK 能到达对端。这个状态对服务器开发影响很大比如你写一个测试客户端频繁短连接、重启程序可能遇到“Address already in use”因为端口还在 TIME_WAIT 里没释放。用 Wireshark 看到大片的 TIME_WAIT 连接你就能很快定位这类问题。后面常见问题部分我会讲怎么应对。2.3 回环接口抓包的微妙之处如果你在 lo 接口上抓包会发现每个 TCP 包都出现两次一次是发给虚拟回环设备一次是从回环设备收到。这是因为回环流量本质是“自己发给自己的”在 lo 上相当于一个数据包的收发两个方向都经过同一网卡。做本机客户端/服务端调试时别被这个现象吓到。过滤时你只需要在显示过滤器里再加条件比如关注从客户端端口发到服务端端口的包tcp.port 8888这样能快速腾出视线。另外回环接口的 MSS最大报文段通常是 16384 字节比以太网的 1460 大得多所以你在回环上看到的 TCP 分段策略与真实网络不同。这就提醒我一条经验回环抓包只能用来验证协议逻辑不能用来评估真实网络下的性能表现。要测吞吐、测延迟得走真实网卡或虚拟网桥。3. Linux 下 TCP 通信场景的实操抓包3.1 用 Python 快速搭一个测试环境很多情况下我们并不想用 nc 简单测连通性而是要验证自己写的 TCP 通信代码逻辑。Python 标准库的 socket 模块是最快搭出测试环境的方式。先写服务端监听端口接收数据并回显import socket server socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server.bind((0.0.0.0, 9000)) server.listen(5) print(server listening on 9000) conn, addr server.accept() print(client connected from, addr) data conn.recv(1024) print(received:, data.decode()) conn.sendall(back: data) conn.close() server.close()客户端import socket client socket.socket(socket.AF_INET, socket.SOCK_STREAM) client.connect((192.168.1.100, 9000)) client.sendall(bhello tcp) resp client.recv(1024) print(resp:, resp.decode()) client.close()这里的服务端地址要按你目标机器的实际 IP 改。跑起来之前在 Server 机器上先启动 Wireshark抓卡对应网卡过滤规则写tcp.port 9000然后跑客户端你就能看到从 SYN 到 FIN 的完整闭环。我自己调试这种代码时有个习惯socket 不要急着 close在客户端里 close 之前加一个 time.sleep(2)这样断开阶段的所有包都能抓到不会被程序退出得猝不及防。抓包最怕“哎呀刚按了停止结果关键包没抓到”抓包时间窗口留足是很重要的细节。3.2 从抓包看请求-响应延迟抓包不只是看协议状态更是定位延迟的利器。Wireshark 的每个包都有时间戳默认以秒为单位。如果你在分析一个“客户端发请求后服务端一直不回包”的问题做法是这样先看客户端发出的最后一个数据包时间再找服务端回的第一个数据包可能是 ACK 或响应数据时间两者相减就是服务端处理耗时。更精确的办法是用 Wireshark 的跟踪流功能右键任意一个包选择 Follow - TCP Stream它会把整个连接从建立到断开的所有双向数据以时间顺序列出来看起来和文本对话框一样请求和响应一目了然。如果服务端处理时间离谱比如超过几秒问题通常不在网络而在应用代码里。接下来点开统计菜单里的 Service Response Time可以看不同 TCP 连接上“请求到首个响应”的延迟分布能快速判断是不是个别连接慢。还有一种情况请求很快响应也很快但客户端感觉就是慢。这时候要看服务端是不是开启了 Nagle 算法导致小数据包被延迟发送。抓包里表现为客户端发一个请求后服务端很久才发数据中间间隔时间基本恒定。解决办法是服务端设置TCP_NODELAY在 Python 里就是server_socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)从抓包中看到的现象配合代码修改定位问题的效率是很高的。3.3 重传、乱序和窗口更新的识别TCP 的可靠传输是依赖确认和重传实现的但真到了实网环境丢包、乱序、网络拥塞是常态。Wireshark 通过一个叫 Expert Info 的机制自动标注这些异常。在工具栏的右下角如果看到黄色或者红色的小图标说明抓到的报文里有重传、乱序、零窗口这一类事件。常见的几个标注是TCP Retransmission发送端重传了自己已经发过但没收到 ACK 的报文段。看到大量重传基本可以断定链路有丢包。TCP Out-of-Order收到的包序号比预期的小说明数据乱序到达。TCP Dup ACK因为收到乱序或者缺失的包接收端重复确认上一个连续序号。Zero Window接收端缓冲区满了通告发送端暂停发送。这是典型的“应用层不读数据”导致的程序瓶颈比网络瓶颈更常见。我做 Linux 服务端性能分析时抓到大量 Zero Window基本不看网络直接看服务端进程是不是卡住了或者消费下游太慢。零窗口会导致发送端等待看起来像“卡死”实际上 TCP 在正常地做流控。如果想定量看整条连接的吞吐用 Statistics - TCP Stream Graphs - Throughput 会画出随时间变化的吞吐曲线。再配合 Time-Sequence Graph能看到每个包的序号随时间增长的情况。如果曲线是平的说明窗口受限或者带宽受限如果是锯齿状那是重传拖累了传输。4. 过滤语法与分析方法从海量报文里快速定位问题4.1 抓包过滤和显示过滤要分清楚Wireshark 里有两套过滤语法一个是抓包时生效的 Capture Filter一个是抓完后在界面上用的 Display Filter。这一点如果没搞清楚很容易把自己绕晕。Capture Filter 的语法更偏底层是 libpcap 格式。比如只抓某端口、某 IP 的包防止抓包文件过大tcp port 9000 host 192.168.1.10 tcp port 9000 or udp port 53Capture Filter 一旦写错可能什么都没抓下来所以日常排查里我更喜欢“全抓再过滤”。硬盘空间够的话直接把所有流量抓个几十秒到几分钟然后用 Display Filter 做各种维度的分析。Display Filter 功能远比 Capture Filter 强大支持复杂布尔表达式、字段匹配。我列几个高频使用的 Display Filter几乎每天都要用到目标过滤表达式只看某个 IP 的 TCP 包ip.addr 192.168.1.10 tcp只看某个端口tcp.port 9200只看客户端端口tcp.srcport 54321只看两个地址之间的连接ip.addr 192.168.1.10 ip.addr 192.168.1.20携带 SYN 标志位tcp.flags.syn 1SYNACKtcp.flags.syn 1 tcp.flags.ack 1携带 FIN 标志位tcp.flags.fin 1只看重传tcp.analysis.retransmission 1只看零窗口tcp.analysis.zero_window 1只看 HTTP 请求http.request只看 TLS 握手tls.handshake.type 1显示过滤是在文本框里输入输入过程中 Wireshark 会自动提示字段名语法高亮也会帮你纠错。如果整个框变红了多半是字段名写错了或者引号没闭合。把鼠标点到一个感兴趣的报文上左下角的协议树里的字段右键可以直接作为过滤器引用这个操作对新手来说比自己敲字段名靠谱得多。4.2 用工具菜单代替肉眼翻包很多人抓完包之后就一帧一帧翻虽然直观但效率太低。Wireshark 的统计菜单是隐藏的神器。Statistics - Conversations 会列出所有连接的五元组、包数和字节数双击某一行可以直接过滤到该连接这是排查“哪个连接最耗流量”最高效入口。Statistics - Protocol Hierarchy 能看到不同协议的占比。如果抓包结果里 TCP 占了 99% 而 UDP 只有 1%那对业务特征的理解就是一行数据的事。此外同一连接的数据要整体看用右键 Follow - TCP Stream 可以看到服务端和客户端的完整数据流。文本模式下会把两侧数据按方向排列这个视图非常适合调试自定义协议发送方发的东西错没错、接收方有没有回一目了然。如果协议是二进制的或者压缩过的文本模式会乱码可以切到 Hex Dump 模式看十六进制。在性能分析时Statistics - IO Graph 可以画时间维度的吞吐曲线。默认是一条总流量曲线你可以新增几条过滤器比如分别显示 SYN 包数量、重传包数量然后看它们是否和某个时间点的异常吻合。这套“曲线过滤”的组合拳比盯着一屏报文找规律高效得多。4.3 时间显示与时区问题Wireshark 默认的时间戳格式是“相对第一个包经过的秒数”。但排查问题时往往需要和服务器日志的时间对上这时要把时间格式切成绝对时间View - Time Display Format - Date and Time of Day。如果你要对比的是不同时区的日志点击 Time Shift 功能可以统一偏移比如想把抓包时间调整为北京时间就在 Time Shift 里设置 UTC 与目标时区的差值。具体做法菜单 View - Time Display Format 下有一个“Change Time Display Precision”和“Time Shift...”输入你想要偏移的小时数例如东八区是 8所有显示时间就会整体平移。抓包文件如果跨了几个小时这个功能极其实用。配合显示过滤器还能只显示某个时间窗口的报文frame.time 2025-01-01 10:00:00 frame.time 2025-01-01 10:05:00这样不用眼睁睁翻几百兆的文件。我自己排障的时候都是先把时间显示调成和服务端日志一致再用这个时间区间过滤先把可疑窗口的包范围圈起来再逐步收窄。5. 常见问题与排查技巧实录5.1 新手最常踩的 5 个坑我见过太多同事和网友在 Wireshark 抓包上卡住问题上天入地其实很多都是基础配置的坑。整理一份问题速查表照着自查比求助群友快得多现象最常见原因解决办法接口列表是空的无法抓包当前用户没有抓包权限加入 wireshark 用户组或给 dumpcap 设置 setcap抓了半天一个包都没有过滤器写错或者网卡选错先确认 ip addr 里的接口名再用-f临时放开所有流量测试明明访问某个 IP却抓不到数据走的是其他物理网卡或虚拟接口用tcpdump -i any或者在 Wireshark 里多选接口回环接口抓包看花眼lo 上同一数据包收、发都过一遍显示过滤加tcp.port或组合过滤抓到的包 checksum 全是错网卡开启了 checksum offload数据包在抓包点看到的校验和尚未被填充在 Wireshark 里忽略校验和错误或修改网卡 offload 设置应用层数据看不见全是一堆 TLS 密文通信走了 TLS/SSL 加密配置 SSLKEYLOGFILE 环境变量或用中间人方式解密抓包文件几百 MB打开卡死抓包范围没收住也没提前做环形缓冲用 Capture Filter 限制端口/IP或者用 tshark 按大小自动分割TLS 解密我要多说一句。在 Linux 下如果程序用 OpenSSL 或者 GnuTLS可以通过设置 SSLKEYLOGFILE 让客户端把会话密钥写出来Wireshark 里在 Preferences - Protocols - TLS 里配置这个密钥文件就能直接看到解密后的 HTTP 内容作为一种本地调试手段非常方便。生产环境不要乱开密钥泄露影响太大。5.2 命令行组合拳服务器上快速定位 TCP 问题有时候服务器上根本没有图形界面甚至 Wireshark 都不想装或者说图形库依赖太重。这时候 tshark 几乎是 Wireshark 的命令行替身能用大部分同样的过滤语法。快速抓包 10 秒并统计连接sudo timeout 10 tshark -i eth0 -f tcp port 8080 -w /tmp/8080.pcap tshark -r /tmp/8080.pcap -q -z conv,tcp第二条命令会打印出所有 TCP 会话的统计包括每个连接的包数和字节数这在没有图形界面的 Linux 服务器上非常刚需。如果还想知道哪个 IP 发了最多的 SYN 包可以这样tshark -r /tmp/8080.pcap -Y tcp.flags.syn1 -T fields -e ip.src | sort | uniq -c | sort -rn这一串命令把 pcap 文件里的 SYN 包按源 IP 计数如果某个 IP 的 SYN 数量明显异常很可能是在扫描端口或者恶意重连。做运维的朋友可以把这套命令记下来应对“半连接特别多”的排查场景非常有效。提到半连接Linux 自身有个命令可以实时看系统层面 TCP 状态统计ss -s ss -tan state syn-recvss 看到的 SYN-RECV 数量和 Wireshark 里抓到的 SYN 重传次数互相印证基本就能判断是不是遭遇了 SYN Flood 或者某个服务端的连接队列满了。这个配合起来排查效率非常高。5.3 我个人在长时间排障中沉淀的习惯抓包这件事工具本身其实很简单真正难的是明确“你到底要证明什么”。所以我每次打开 Wireshark 之前都会先在纸上写一句话我怀疑是网络丢包、还是两端程序逻辑不对、还是中间有个防火墙在干预目标不同抓包策略完全不同。如果是怀疑两端程序逻辑不对我会把抓包窗口放在进程两端各自抓一遍再做一次对比。比如客户端和服务端在同一台机器上回环包抓一份在跨机器场景下两端各抓一份比对两端看到的 seq 和 ack 是否一致。很多“程序bug其实是设备悄悄改包”的诡异问题就是靠这种两端对包的方式查出来的。如果怀疑防火墙干预先别急着看 iptables 规则直接抓包看有没有 TCP RST 包。RST 是连接被粗暴打断时的明确信号。当客户端收到 RST程序里通常报 Connection reset by peer服务端收到 RST可能报 Connection reset。看到 RST 之后再去查防火墙规则和中间设备策略路径清晰很多。另外还有一个细节抓包时长不要贪长。默认的 Wireshark 可以不限制抓包大小但实际生产环境流量大抓得越久文件越大分析越痛苦。我会用环形缓冲只保留最近几分钟sudo tshark -i eth0 -b duration:60 -b files:5 -w /tmp/ring.pcap意思是每个文件抓 60 秒最多保存 5 个文件滚动覆盖。这套配置在我处理线上问题时几乎是标配既保留了事发前中后的上下文又不会把磁盘填满。这个内容后续还可以扩展的方向很多比如把抓包流程集成进 CI 测试自动判断 TCP 连接建立的耗时或者用 Wireshark 的 Lua 脚本做自定义协议解析器把自己公司内部二进制协议直接解析成可读字段。真到了那一步你已经不是停留在“看包”的层次而是在用协议分析能力反哺开发和运维流程了。
返回列表