ARTICLE DETAIL

资讯详情

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

Wireshark实战:MQTT协议抓包分析与排障全指南

Wireshark实战:MQTT协议抓包分析与排障全指南 这期继续更Wireshark实战系列。前面聊过不少协议的抓包分析今天把MQTT单独拎出来写一篇原因很简单这玩意儿现在太常见了从嵌入式设备上报数据到服务端之间做消息流转再到各种IoT平台对接十有八九都会碰到它。我见过不少同事排查MQTT问题靠猜客户端日志打一打服务器日志翻一翻实在不行重启一下其实很多时候用Wireshark在链路上看一眼几秒钟就能定位问题。这篇我会从MQTT协议本身的设计逻辑讲起然后带你把环境搭起来再用Wireshark把一个完整的MQTT会话从头到尾拆一遍。还包括我实际排障过程中遇到的几个典型场景比如设备上线后疯狂重连、消息发了但订阅端就是收不到、TLS加密链路怎么抓包分析等等。适合刚接触MQTT的开发同学也适合那些已经用了一段时间、但一直靠“感觉”调试的人。看完你至少能回答这几个问题MQTT的QoS到底在干嘛、控制报文长什么样、用Wireshark怎么快速过滤和定位问题。1. MQTT到底是什么为什么需要抓包看它1.1 从一个“推送通知”的小类比说起MQTT全称是Message Queuing Telemetry Transport翻译过来是消息队列遥测传输。名字看着长但核心就是八个字轻量、发布、订阅。我习惯用一个“对讲机”的例子来解释。你手里拿着一个对讲机大家都在同一个频道上你按下说话键说一句“所有人到食堂集合”在频道里的所有人都能听到爱去不去是他们自己的事。你不需要知道具体是谁在听也不需要等每个人给你回一句“收到”。这就是发布订阅模型发布者只管发订阅者只管收双方完全解耦。如果换成传统的HTTP请求响应模型就变成你拿着电话本挨个给人打电话说“食堂集合”你得知道每个人的号码还得等对方接听效率低不说人一多你的电话根本打不过来。MQTT就是为这种“一对多、低带宽、不稳定网络”场景设计的。它跑在TCP之上默认端口1883报文头非常小几条消息在同一个连接里来回复用所以特别适合嵌入式设备、传感器网络、移动端推送这类环境。你要是在一个4G物联网卡上跑HTTP轮询流量费能教你做人换成MQTT一条连接一直挂着有消息才推省电省钱还省流量。1.2 MQTT核心机制速览QoS、保活、遗嘱、保留消息MQTT能解决的问题不仅仅是“推送”它在协议层面还设计了好几个实用机制这也是它和裸TCP长连接、或者自己造轮子的最大区别。先说QoS全称Quality of Service服务质量。MQTT把消息可靠性分成三个等级QoS 0发出去就完了不确认、不重发可能丢。QoS 1至少送达一次。发送方会收到一个确认收不到就重发但可能重复。QoS 2恰好送达一次。通过四次握手保证不丢不重代价是开销最大。这个分级设计非常实用。比如传感器上报室内温度偶尔丢一帧问题不大用QoS 0就行但如果是设备告警、控制指令这种关键消息就得用QoS 1甚至QoS 2不然指令丢了设备没动作代价就大了。再说保活机制。TCP连接自然断开其实很难被对端及时发现尤其是移动网络下信号飘忽不定连接可能已经断了但对端还傻等着。MQTT里有个Keep Alive字段客户端在建立连接时告诉服务端“我最多隔多久心跳一次”默认一般是60秒。如果超过1.5倍时间服务端没收到任何报文它就可以判断客户端挂了主动断开。这就是为什么你经常在抓包里看到PINGREQ和PINGRESP这一对报文本质就是心跳。还有遗嘱消息Will Message客户端在连接时声明一个遗嘱主题和遗嘱内容如果它后面是非正常掉线比如网络断开、崩溃服务端就会替它把遗嘱发出去告诉别人“这家伙挂了你们自己看着办”。这个机制在设备在线状态管理里特别有用。最后是保留消息Retained Message发布消息时带一个retain标志Broker会替你把这条消息存起来后续新的订阅者上线立刻就能收到这条最新的消息而不是只能收到订阅之后才发布的消息。这个特性很适合做“设备当前状态”这类场景。1.3 为什么现场调试一定离不开Wireshark协议设计得再好落到真实网络环境里也会出幺蛾子。客户端说发出去了服务端说没收到两边各执一词这时候你吵不过机器只能看抓包。Wireshark的价值在于它能让你站在网络流量的视角看整个交互过程而不是只听客户端和服务器的“一面之词”。而且Wireshark对MQTT的支持相当完善拿到报文后会自动帮你解析出报文类型、主题、消息ID、QoS级别、载荷内容等等免去了自己翻协议细节的功夫。你可以清楚地看到CONNECT报文发出去了没有Broker回CONNACK没有PUBLISH到达了没有消息被谁转发给了谁四舍五入就是协议分析界的监控探头链路上一放谁来谁走一目了然。我自己在帮别人排查MQTT问题时几乎不敢不先抓包。很多时候问题根本不在应用层而是TCP握手没完成、Keep Alive过期后断连、消息重复重传、或者报文太大被分片了这些光看日志是看不出来的必须看包。2. 先把环境搭起来Wireshark与MQTT工具链准备2.1 工具选型为什么是Wireshark Mosquitto MQTTX做实验和排障前环境得先搭好。我的常用组合是三件事Wireshark负责抓包分析Mosquitto作为本地BrokerMQTT服务器MQTTX作为图形化客户端。Wireshark不用多说了全平台开源抓包界的瑞士军刀。如果你还没装去官网下个稳定版就行安装时注意把Npcap或者WinPcap组件勾上不然抓不到包。顺带说一句Windows下如果Wireshark打不开或者抓不到任何包八成是Npcap驱动没装好重装一遍基本能解决。Mosquitto是Eclipse基金会下的开源MQTT Broker体积小、安装简单、跨平台本地做实验完全够用。装好之后一条命令就能启动一个Broker非常方便。生产环境当然有很多别的选择比如EMQX、EMQ X、VerneMQ、HiveMQ还有各种云厂商的IoT平台但它们的核心协议行为都是一样的本地用Mosquitto足够复现绝大多数问题。MQTTX是一个跨平台的MQTT客户端工具界面清爽支持填写连接参数、订阅主题、发布消息可视化操作还能同时建立多个客户端连接。调试的时候特别方便比nodered的mqtt节点灵活比命令行工具直观。如果你不用图形界面Mosquitto自带的mosquitto_pub和mosquitto_sub命令行工具也完全够用。这里插一句热词里有人提到的“MQTT虚拟串口软件”和“Node-RED实现OPC UA转MQTT”本质上它们也都是围绕MQTT工具链做的事情。前者是把串口数据和MQTT打通后者是工业现场把OPC UA的数据再转发成MQTT场景不一样但最终分析手段都是抓包看MQTT交互。2.2 本地复现环境搭建这里我以Windows环境为例Mac和Linux的步骤也大差不差。第一步安装Mosquitto。Windows下可以直接去官网下载安装包装完把安装目录加到系统PATH里。如果你装的是旧版本记得手动建一个配置文件夹比如C:\mosquitto并把默认配置文件复制过去新版一般会自动处理。第二步启动Broker。最简单的方式是直接执行mosquitto -v-v参数是打印详细日志建议加上这样你在Broker端也能看到谁连上了、订阅了啥、收到了啥。默认监听1883端口不做鉴权局域网内所有人都能连本地实验无所谓但生产环境千万别这么裸奔。第三步用MQTTX连接。打开MQTTX新建一个连接填Host: 127.0.0.1Port: 1883Client ID: 随便填比如test-client-01点击连接成功之后左边会出现一个绿色的已连接状态。这时候你可以在Wireshark里抓回路Loopback网卡的包因为Broker和客户端都在本机流量走的是回环接口。如果你用的是Mosquitto命令行工具也可以这样玩# 订阅test/topic主题 mosquitto_sub -h 127.0.0.1 -p 1883 -t test/topic -v # 发布消息到test/topic mosquitto_pub -h 127.0.0.1 -p 1883 -t test/topic -m hello mqtt会了这几个工具后面做实验就顺手多了。2.3 抓包前的几个细节设置环境搭好了抓包前有几个细节容易被忽略我提个醒。第一抓包网卡要选对。本机实验就抓Adapter for loopback traffic capture或直接选Loopback: lo别傻乎乎抓以太网卡半天看不到包。真机调试时抓设备所在网段的那个网卡比如Wi-Fi网卡或以太网网卡。第二可以先设置好过滤表达式。打开Wireshark在过滤栏输入mqtt或者更聚焦一点tcp.port 1883这样界面上只显示MQTT相关的流量不会被各种广播包干扰。两者区别在于mqtt是按应用层协议过滤Wireshark识别到是MQTT才展示tcp.port 1883是端口过滤不管协议是不是MQTT只要端口是1883就展示。我建议排查时两个都试试有时候端口对了但Wireshark没识别出是MQTT这时用端口过滤就能看到原始TCP数据方便进一步分析。第三抓包时长长的话记得用CtrlE暂停或设置文件自动切割避免内存被撑爆。Wireshark默认把所有包都存内存里长时间抓包内存占用吓人建议用环形缓冲区每条文件比如100MB、共20个文件自动滚动覆盖。3. 实战抓包MQTT完整会话逐包拆解环境就绪现在开始正式的抓包分析。这一节我会带你走一遍完整的MQTT会话从连接、订阅、发布到断开每个关键节点都看一下Wireshark里实际长什么样。3.1 过滤表达式与报文概览先启动抓包然后用MQTTX连上Broker订阅一个topic比如test/topic再发布一条消息最后断开连接。这时候停止抓包你会看到一堆TCP包和几个MQTT控制包。在Wireshark的过滤栏输入mqtt界面就会收缩到只有MQTT相关的包。如果mqtt过滤出来是空的而tcp.port 1883有数据说明Wireshark没把流量识别成MQTT往往是端口不是默认1883或者写的是tcp但报文字段不符合规范这时候要看原始报文和TCP payload的十六进制手动判断。先说一下MQTT控制报文的通用结构。过了一遍协议MQTT v3.1.1和v5.0的控制报文都由三部分组成固定头、可变头、载荷。固定头里第一个字节的高4位表示报文类型比如00011CONNECT客户端发起连接00102CONNACK服务端确认连接00113PUBLISH发布消息01004PUBACKQoS 1确认10008SUBSCRIBE订阅主题10019SUBACK订阅确认110012PINGREQ心跳请求110113PINGRESP心跳响应111014DISCONNECT断开连接知道这个你看到抓包也能快速判断一个包是什么操作不用每次都点开看。3.2 CONNECT与CONNACK设备上线过程CONNECT是MQTT客户端和服务端交互的第一步。TCP连接由客户端发起完成三次握手之后客户端的第一个MQTT报文就是CONNECT。在Wireshark里选中标记为MQTT的CONNECT包下面会展开很多字段Client ID客户端唯一标识服务端靠它区分不同设备。Keep Alive保活时间秒为单位我实验里设置的是60。Clean Session是否清理会话。如果为true服务端不保留该客户端的会话状态为false断线重连之后可能恢复之前的订阅和离线消息。需要说明的是MQTT v5.0改名叫Clean Start概念上有点差别这里不展开。Will Message遗嘱消息只有在连接时声明了才有展开能看到遗嘱主题和内容。User Name / Password用户名密码可选。Payload部分能看到协议名MQTT和协议版本4代表v3.1.15代表v5.0。协议版本在兼容性排查里很重要。我遇到过设备用MQTT v3.1连BrokerBroker却只认v3.1.1直接拒绝连接。后来一抓包CONNECT里的Protocol Level是3换成4就通了。如果连接正常Broker会回一个CONNACK包里面有一个Connect Acknowledge Flag字段还有一个Return Code0连接接受1不接受的协议版本2标识符被拒绝3服务器不可用4用户名或密码错误5未授权最容易踩的坑就是看到Return Code不是0那客户端压根没连上抓包里一眼就能看出来。3.3 SUBSCRIBE与SUBACK订阅主题连接建立之后客户端如果想接收某个主题的消息就要发送SUBSCRIBE报文。SUBSCRIBE里可以同时带多个主题过滤器每个过滤器可以指定不同的请求QoS。Wireshark里展开SUBSCRIBE能看到Message ID消息标识符客户端生成唯一的用于匹配SUBACK。Payload部分主题过滤器和请求的QoS。Broker处理完会回SUBACK里面包含返回码比如0表示QoS 0成功、1表示QoS 1成功、2表示QoS 2成功0x80表示失败。这里有个细节如果客户端订阅成功了那Broker才会把后续匹配主题的PUBLISH推给你。如果你发现订阅了好久啥都收不到先回头看看SUBACK的返回码是不是0x80八成是权限或者主题过滤器写错了。还有一件事要提一下主题过滤器的通配符。MQTT支持两级通配符匹配单层#匹配多层。比如订阅home//temp能收到home/room1/temp但收不到home/room1/humidity订阅home/#则能收到home/下面的所有层级。抓包能看到客户端实际订阅了啥这个对排查为什么“消息没推过来”很有帮助——有时候不是Broker没推是订阅的过滤器就没匹配上。3.4 PUBLISH与QoS交互消息发布的核心流程发布消息是最核心的环节也是最容易出现各种诡异问题的环节。一个PUBLISH包在Wireshark里展开的字段包括Topic Name主题比如test/topic。Message ID消息IDQoS 0时没有这个ID。QoS Level取值0/1/2。Retain Flag是否保留消息。Payload实际的消息内容Wireshark会以字符串或者十六进制形式显示。当QoS为0时客户端发出PUBLISH之后没有任何确认一条包完事丢了就丢了。当QoS为1时流程是客户端发PUBLISHBroker收到后会回一个PUBACK。这个流程看着简单但里面的坑在于重传机制。如果客户端发出PUBLISH之后迟迟等不到PUBACK它会按规则重发相同的PUBLISH此时你抓包会看到好几个相同Message ID的PUBLISH包连着出现。这就是网上常说的“消息重复”问题的根源之一如果应用层不自己做去重订阅端可能会收到同样的消息多次。当QoS为2时更复杂一些完整流程是四步握手发送方发PUBLISHQoS2带有消息ID接收方回PUBREC收到发布表示接收成功但还没提交给应用发送方回PUBREL发布释放表示可以完成这次交互了接收方回PUBCOMP发布完成双方才能真正释放消息ID在Wireshark里如果你看到一个 Message ID 同时出现PUBLISH、PUBREC、PUBREL、PUBCOMP这几个包这就是一个完整的QoS 2交互。实际使用中如果你发现某个消息ID长时间卡在PUBREC没有后续PUBREL说明客户端或Broker有一方的逻辑出问题了大概率是网络抖动导致状态机异常抓包能帮你精确判断卡在哪一步。再说Retain Flag。我见过有人往一个带retain的主题上发消息然后后面新设备一上线就收到一条非常古老的消息一脸懵。抓包一看PUBLISH里Retain Flag置1Broker把旧消息缓存住后来订阅者上线Broker立刻把这条消息推出来。排查思路很简单抓包看订阅者一开始收到的消息是不是Broker推送的保留消息而不是客户端新发布的就知道这是哪来的了。3.5 PINGREQ/PINGRESP与DISCONNECT保活与下线前面说过MQTT客户端和服务端之间有保活机制。Keep Alive时间内如果没有其他报文交换客户端就要发一个PINGREQ服务端回PINGRESP。抓包看到这一对包就说明连接还在正常维持心跳在按节奏走。如果抓包里出现了TCP的RST重置或者FIN正常关闭而之前已经没有PINGREQ/PINGRESP了通常说明连接已经异常可能是网络超时被某端杀掉。我之前排查过一个设备频繁掉线的案例设备端日志显示“连接断开”服务器端却说客户端主动断开了。抓包一看真相是设备因为网络原因收不到PINGRESP误以为Broker挂了自己主动发了DISCONNECT。后来调整了Keep Alive时间和重连策略问题就消失了。这个案例说明光看客户端或服务端日志都容易有盲区抓包才看到全过程。正常下线时客户端会发DISCONNECT报文服务端收到后回收会话资源。注意如果客户端直接拔网线、断电、杀进程Broker只能等Keep Alive超时才能发现。所以你经常看到设备已经掉线了平台侧要过几十秒才更新状态这就是在等超时。4. 真实场景排障设备消息丢失与重连风暴4.1 场景描述STM324G模组上云有个项目是用STM32单片机加一个移远4G模组通过MQTT把设备数据上报到云端IoT平台。设备端跑了MQTT客户端软件层面看着一切正常设备测试时也能偶尔连上发数据。结果部署到现场后问题来了设备经常上线后没多久就离线云平台那边总是“设备离线”告警而且用户反馈数据有丢失指令下发经常没反应。这种问题第一反应就是抓包。我在设备侧用一台笔记本接同一个4G路由的网口镜像流量在Wireshark上过滤mqtt把整个连接过程抓了个清清楚楚。4.2 抓包定位“消息丢失”的完整过程抓到包之后我按时间线一帧一帧看第一眼注意到的是TCP三次握手正常因为设备是主动外连Broker的IP和端口也正常。但往下翻CONNECT包里的Keep Alive被设置成了一个很短的秒数比如10秒。乍一看没什么但移动网络本身延迟高、信号不稳定10秒保活意味着设备把“判断自己掉线”的频率调得很高一旦网络稍有抖动超过阈值就立刻判定为离线主动重连。再往下看发现设备重连前并没有发DISCONNECT都是直接TCP RST然后重新建立连接。这种粗暴断连方式Broker侧会认为客户端异常掉线如果遗嘱消息设置了还会触发遗嘱发布。于是云平台那边就看到“设备离线-上线-离线-上线”反复横跳。关于“消息丢失”我抓到了更关键的一点设备上报数据用的QoS 0指令下发订阅的QoS也是0。在弱网环境下QoS 0的包丢了就丢了TCP层会重传但如果连接已经断了重传也没用。后来我建议把数据上报提高到QoS 1平台侧加去重指令下发这种关键操作至少QoS 1重要指令可以考虑QoS 2。这里顺便说一句有人说“QoS 1一定不丢消息”这是不对的。QoS 1保证的是“至少送达一次”但这要求连接是活的、网络是通的。如果连接已经断开消息在连接修复之前发出Broker那边如果没建立持久会话Clean Session为true消息一样会丢只是协议尽力重传而已。排查过程里我还顺手验证了一下设备能不能正常订阅到云平台下发的指令。抓包显示设备只在上线时订阅过一次之后重连时因为Clean Session设置成了false但云平台某些Broker并不保证恢复所有订阅或者会话在超时后被清理了结果就是设备以为自己还订阅着平台也以为设备还订阅着实际上两边早就不在一个频道上了。这类问题抓包能看得很清楚设备重连后的SUBSCRIBE有没有发出、SUBACK返回码是什么、平台下发指令时设备连接是否存在。4.3 TLS加密链路怎么抓包分析很多生产环境不会用明文1883端口而是用TLS加密的8883端口。这种链路在Wireshark里默认只能看到TLS握手和加密的Application Data看不到MQTT明文内容。如果你想看明文有两种正规做法第一种如果你有服务端的私钥和证书可以在Wireshark里配置TLS解密。操作路径是Edit - Preferences - Protocols - TLS在RSA keys list里填写服务端IP、端口、协议和私钥文件路径。这样Wireshark就能用私钥解密自动把TLS内容还原成MQTT明文。这里要说明一下TLS的密钥交换算法需要匹配旧版本用RSA密钥交换时这种静态私钥解密是可以的但新版本普遍用ECDHE这种前向保密算法服务端私钥无法解密流量。解决办法是配置Pre-Master Secret在客户端设置SSLKEYLOGFILE环境变量比如Windows下set SSLKEYLOGFILEC:\sslkey.log然后启动支持这种输出的客户端程序Wireshark里在TLS协议设置里指定这个密钥日志文件就能实时解密查看。这个思路适用于自研客户端和浏览器的场景十分实用。第二种没有密钥或者不方便解密的就只能从TLS握手细节、连接模式、发送频率、数据包大小这些间接信息做判断应用层内容就看不了了。所以我在设计系统时如果后续需要线上排障会专门在网关上做一个流量镜像端口或者直接在设备端临时用一个不加密的调试通道抓一把明文MQTT分析完再切回TLS。5. 常见问题速查与避坑心得5.1 常见问题速查表问题现象Wireshark排查要点常见根因解决思路客户端一直连不上看TCP是否完成三次握手防火墙拦截1883端口、Broker没启动先抓TCP确认握手再查Broker日志CONNECT发出后无CONNACK看确认包的返回码或是否有TCP RSTKeep Alive太短、鉴权失败、协议版本不匹配调整连接参数检查Broker配置设备反复上线离线看有没有DISCONNECT还是直接RST网络抖动、Keep Alive过短、重连策略没退避延长保活时间重连加指数退避消息发布了订阅端收不到看PUBLISH是否到达BrokerBroker有没有转发给订阅者主题通配符不匹配、QoS 0丢包、订阅失效抓包看转发方向确认主题和订阅关系消息收到了但重复看Message ID相同的PUBLISH出现几次、重传情况QoS 1重复投递、应用层未做去重应用层按消息ID去重设备下线但平台很久才感知看有没有PINGREQ/PINGRESPKeep Alive是否过长Keep Alive设置过长、遗嘱未配置调整保活利用遗嘱快速感知离线加密链路看不到明文看TLS握手、ClientHello里的SNI没有配置解密素材按4.3节配置私钥或预主密钥Wireshark里看不到MQTT包看tcp.port 1883是否有数据端口不对、Wireshark未识别先用端口过滤再人工查看TCP payload这个速查表基本覆盖了我日常遇到的大多数问题。实际排查时我建议大家遵循一个顺序先确认TCP层握手、断开、重传再确认MQTT层的连接和订阅最后才去分析消息内容。别一上来就死盯着Payload里的那几行数据很多时候问题根源在下面几层。5.2 我踩过的坑和几个小技巧最后分享几个实操心得都是拿真金白银的时间换来的。第一千万别忘抓包的时间点。分析MQTT问题抓包开始得太晚是大忌。很多关键交互发生在连接建立的几秒内你看日志发现问题时再去抓包早期报文早就没了。我的习惯是一旦怀疑MQTT异常立刻先开抓包最好能让抓包工具常驻监控设好循环覆盖等复现了再停下分析。尤其是设备随机掉线的场景不提前抓根本等不到问题发生。第二Wireshark的显示过滤器除了mqtt还有几个很常用的组合我也顺手记一下mqtt.qos 1 # 只看QoS 1的消息 mqtt.msgtype 3 # 只看PUBLISH报文msgtype对应报文类型 tcp.analysis.retransmission # 看TCP重传 mqtt mqtt.msgtype 8 # 过滤PINGREQ查看字段名可以在Wireshark里点击包看左下角的字段树里实际用的名称这样过滤表达式更准确。第三MQTT over WebSocket的抓包。现在有一部分场景比如浏览器前端用Vue3的mqtt库、或者Node-RED的网页端mqtt节点走的是WebSocket而非原生MQTT。这时候Wireshark默认不会在MQTT层解析你需要叠加过滤websocket来看或者先看TCP payload里的二进制内容手动分析。这类问题排查起来更费劲我建议前端代码里尽量保留调试日志或者干脆用MQTTX这种原生客户端先跑通再迁移到浏览器环境能大幅减少定位成本。第四关于Broker的选择本地实验用Mosquitto没问题但生产环境用EMQX这类带管理界面的Broker会香很多。它的Web管理后台能看到当前所有客户端连接、订阅关系、消息流转情况再配合Wireshark抓包排障效率翻倍。不过无论用哪个Broker抓包分析的基本功都是一样的。第五如果你要长时间抓包比如抓一晚看设备是否掉线千万别让Wireshark一直把所有包攒在内存里。实测下来超过几个G内存占用之后电脑会卡成PPT而且再大的包文件Wireshark打开也卡。用我之前说的环形缓冲设置文件大小100MB、数量20个这样即使抓一晚上也不会爆内存。抓完右键单击一个包选Follow - TCP Stream能看到一条连接上所有MQTT交互的完整对话这对长会话分析尤其有用。MQTT的坑看起来千奇百怪其实剥开看都是围绕连接保活、消息QoS、会话恢复这几件事做文章。抓包看一遍很多“灵异问题”其实都有明确的技术原因。你在排查时如果遇到过那种怎么都说不通的MQTT问题不妨把抓包打开看一眼CONNECT和PUBLISH里那几个字段八成会有新发现。
返回列表