news 2026/9/18 18:17:28

Wireshark实战:MQTT报文抓包深度解析与常见问题排查

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Wireshark实战:MQTT报文抓包深度解析与常见问题排查

Wireshark实战里,MQTT这个协议属于典型的“看着简单,抓包才发现门道多”的类型。很多人用MQTTX或者EMQX能跑通收发,但一问到QoS级别怎么影响报文交互、遗嘱消息到底什么时候发、Keep Alive的超时机制在包上怎么体现,往往就卡住了。这篇我直接接着实战系列的风格,从搭建测试环境开始,把MQTT最核心的几类报文在Wireshark里的样子、过滤写法、交互时序全部拆开讲一遍,最后把我在实际抓包里遇到过的几个典型问题也一并列出来。

1. MQTT的协议设计思路与抓包前必须搞懂的几个关键点

1.1 为什么MQTT能这么轻,轻在哪里

MQTT的全称是Message Queuing Telemetry Transport,翻译过来就是消息队列遥测传输。很多人一看到“消息队列”四个字,就下意识拿它和RabbitMQ、Kafka做对比,实际上两者根本不是一回事。MQTT核心解决的问题是低带宽、高延迟、网络不稳定的环境下的设备通信,它不会像HTTP那样搞一堆Header,也不会像Kafka那样依赖批量吞吐和分区副本。

轻主要体现在两个层面。第一个层面是报文本身的体积小,一个CONNECT包除了固定协议头,核心的ClientID、Keep Alive、Clean Session标志都是紧凑的二进制编码,几乎没有冗余字段。第二个层面是交互模型简单,客户端和服务器通过一个长连接保持通信,发布和订阅被解耦成了两个独立的维度。我在调试STM32+4G模块接入MQTT服务器的时候,代码里发的第一条CONNECT包整个才三十几个字节,这在HTTP里面连请求行都写不完。

抓包前必须先理解MQTT的报文结构。MQTT控制报文由三部分组成:

  • 固定报头(Fixed Header):每个报文都有,包含报文类型、标志位、剩余长度。
  • 可变报头(Variable Header):部分报文才有,比如CONNECT里有协议名和协议级别,PUBLISH里有主题名。
  • 有效载荷(Payload):具体数据内容,比如PUBLISH里的消息体,SUBSCRIBE里的主题过滤器列表。

固定报头第一字节的高四位是报文类型,低四位是标志位,不同类型的报文对标志位有不同的要求。剩余长度使用的是变长编码方式,每个字节的低7位表示数据,最高位表示是否还有后续字节,所以理论上一字节可以表示0到127,超过这个范围就用多字节,最多4个字节。这一点在Wireshark里看得很直观——你点击一个PUBLISH报文的Remaining Length字段,它能直接给你算出实际长度,省得自己手工解码。

1.2 发布订阅模型比请求响应模型到底强在哪

我们平时最熟悉的HTTP是典型的请求响应模型,客户端主动发请求,服务器被动回响应,两者是强绑定的。但MQTT采用的是发布订阅模型,消息的发送方和接收方完全解耦。

解耦体现在三个维度:

  • 空间解耦:发布者和订阅者不需要知道对方的存在,不需要对方的IP或端口,只需要知道一个共同的主题。
  • 时间解耦:发布者和订阅者不需要同时在线,Broker会把消息缓存起来,订阅者上线后可以收到离线期间的消息(前提是持久会话+QoS>0)。
  • 同步解耦:发布操作是异步的,发布者发完消息就可以干别的,不会阻塞等待所有订阅者确认。

这三个解耦特性让MQTT特别适合设备数量大、网络不稳定的物联网场景。我在Wireshark里抓包时经常能看到的一个现象是:一个PUBLISH报文从设备发出后,Broker端马上会返回QoS确认报文(PUBACK或者PUBREC),但订阅者可能过了很久才上线,或者压根不在线。这就是解耦带来的好处——发布者和订阅者之间的生命周期完全独立。

1.3 抓包前需要建立的Wireshark过滤清单

MQTT的默认端口是1883,TLS加密是8883。在Wireshark里,最简单的过滤方式是直接输入mqtt或者tcp.port == 1883。但如果你的环境中存在大量其他TCP流量,建议直接用mqtt过滤协议,Wireshark会自动识别并解析。

不过有一个坑要注意:如果你抓的是回环接口(通常是用Mosquitto在本地跑Broker,客户端也用本地客户端连接的情况),Wireshark默认是能抓到回环流量的,但需要确保你选择了正确的接口。Windows下通常叫“Npcap Loopback Adapter”,Linux下是lo。很多人在本地测试时用mqtt过滤半天一条包都看不到,就是因为接口选错了。

另外,如果你使用了TLS加密的MQTT连接(8883端口),Wireshark是识别不出MQTT协议层的,只能看到TCP。这时候需要配置SSLKEYLOGFILE或者将Broker端配置成关闭TLS的模式,才能看到明文报文。我自己的习惯是:先抓明文1883的包理解协议交互,再开TLS对照看加密后的差异。

2. 完整抓包环境搭建:Mosquitto + MQTTX + Wireshark三板斧

2.1 为什么选Mosquitto而不是EMQX

EMQX功能强大,Web管理界面好看,支持规则引擎和插件,但我个人在实际抓包分析时更推荐Mosquitto,原因很简单:它轻量、无Web界面干扰、日志简洁、配置直观,而且行为更贴近一个标准的MQTT Broker实现。你用EMQX做生产环境没问题,但做协议分析时,Mosquitto能让你把注意力集中在报文交互本身,而不是被管理后台分散。

还有一个重要原因:Mosquitto对MQTT 5.0的支持很完整,配置项里可以设置allow_anonymouspassword_filepersistence等,这些参数在验证Wireshark不同报文行为时都非常有用。比如我想验证“遗嘱消息在客户端非正常断开时才会发布”,在Mosquitto里直接改配置就能模拟出各种断开场景。

如果你已经装了EMQX也没关系,抓包层面的分析逻辑完全一样,只是我个人习惯而已。

2.2 本地Broker的安装与配置

以Ubuntu/Debian系统为例,安装Mosquitto只需要两行命令:

sudo apt update sudo apt install -y mosquitto mosquitto-clients

安装完成后,确认服务是否在运行:

sudo systemctl status mosquitto

如果提示没有启动,手动启动一下:

sudo systemctl start mosquitto

默认配置下,Mosquitto监听1883端口,且允许匿名连接。如果你需要验证用户名密码机制,需要手动编辑配置文件/etc/mosquitto/mosquitto.conf,添加以下内容:

allow_anonymous false password_file /etc/mosquitto/passwd

生成密码文件的命令是:

sudo mosquitto_passwd -c /etc/mosquitto/passwd testuser

这种方式在验证“用户名密码错误导致连接拒绝”的场景时很好用,Wireshark里能看到Broker返回的CONNACK报文携带错误码5。

Windows下安装则更简单,去官网下载安装包,一路Next装完。Windows服务默认也是自动启动的,日志路径在C:\Program Files\mosquitto\mosquitto.log(具体看安装目录)。

2.3 客户端选型:MQTTX和mosquitto_pub/sub各自适用的场景

MQTTX是一款跨平台的图形化客户端,支持MQTT 3.1.1和5.0,界面友好,连接参数配置项齐全,非常适合手动控制连接行为来配合抓包。我最喜欢它的一个功能是:可以单独发送一条指定Topic、指定QoS、指定Retain标志的消息,这在分析PUBLISH报文各种组合标志位时的意义很大。

比如我想验证“Retain标志置1的PUBLISH报文长什么样”,用MQTTX勾选Retain再发一条,Wireshark里立刻就能看到固定报头中Retain位的变化。这种精细控制是Socket工具或者telnet做不到的。

mosquitto_pub和mosquitto_sub则是命令行工具,适合用脚本批量控制场景。比如用循环连续发100条消息,观察TCP层有没有粘包/拆包现象。命令格式如下:

mosquitto_sub -h 127.0.0.1 -p 1883 -t "test/topic" -v mosquitto_pub -h 127.0.0.1 -p 1883 -t "test/topic" -m "hello mqtt" -q 1

-v参数会让sub端打印主题名,便于区分不同Topic的消息。-q参数指定QoS级别,这个在抓包时非常重要,不同QoS级别下的完整报文交互完全不同。

2.4 Wireshark抓包预配置

打开Wireshark,选择回环接口(如果你Broker和客户端都在本机)或者实际网卡,在过滤栏输入mqtt,然后抓包。这里有个细节:Wireshark对MQTT协议的支持版本,有些老版本解析MQTT 5.0报文会有偏差。实测下来,Wireshark 3.6及以上版本对MQTT 5.0支持得比较完整,能正确解析属性(Properties)部分;如果用的是2.x老版本,建议升级,否则可读性差很多。

另外建议开启Wireshark的“Follow TCP Stream”功能。当你鼠标右键点击任意一条MQTT报文,选择“Follow -> TCP Stream”,能看到这一整条TCP连接上的全部MQTT报文序列。这个视角在分析一次完整的连接建立、订阅、发布、心跳、断开的生命周期时特别有用,比一条条报文翻效率高得多。

抓包过程中还要注意:如果MQTT消息的数据量很大,Wireshark的实时刷屏会拖慢界面。可以在抓包前设置一个抓包停止条件,比如抓1000个包自动停止,或者用-a duration:60方式在命令行模式下抓60秒。命令行抓包的写法:

tshark -i lo -f "tcp port 1883" -w mqtt_test.pcapng -a duration:60

这样可以避免打开图形界面占用过多内存,适合长时间抓包场景。停止后把pcapng文件用Wireshark打开来分析,体验比实时抓包好很多。

3. 抓包实战:核心报文交互的Wireshark深度解析

3.1 CONNECT/CONNACK:握手过程到底交换了什么

先用MQTTX连接本机Broker,同时Wireshark抓包,连接成功后停止抓包,会话里至少应该有两条报文:客户端发出的CONNECT和服务器回应的CONNACK。

CONNECT报文的固定报头第一个字节是0x10。0x10这个数字的二进制是0001 0000,高四位0001表示报文类型是1(CONNECT),低四位0000是CONNECT要求必须为零的标志位。可变报头里的第一个字段是协议名,正常情况下是“MQTT”四个ASCII字符(0x00 0x04 0x4D 0x51 0x54 0x54),Wireshark会直接显示成字符串。协议级别字段,MQTT 3.1.1是0x04,MQTT 5.0是0x05。

连接标志位这一个字节信息量很大。Clean Session(或者MQTT 5.0里的Clean Start)位置1,表示客户端要求一个全新的会话,Broker端不保存任何历史状态。如果置0,Broker需要恢复之前该ClientID的会话状态。Will Flag位置1,说明这条连接带遗嘱消息,遗嘱的Topic和Payload在CONNECT报文后续部分直接携带,这让CONNECT包在抓包时看起来比想象中要长。

Keep Alive字段占两个字节,单位是秒。设成60表示Broker要在60秒内至少收到一次客户端的心跳(PINGREQ),否则判定连接超时。实际抓包时你会发现,MQTT的心跳机制和TCP的Keep-Alive是两码事,MQTT的心跳是应用层主动发PINGREQ,TCP Keep-Alive是内核层面的探测包,两者不要搞混。

CONNACK报文的固定报头第一字节是0x20,连接确认标志位(Session Present)在可变报头里。最关键的是返回码字段:

返回码含义
0连接已被接受
1服务器拒绝,不支持的协议版本
2客户端标识符合法,但服务器拒绝连接
3服务器不可用
4服务器拒绝,用户名或密码错误
5服务器拒绝,未授权

如果CONNACK返回码不是0,说明握手失败。我在实际环境中遇到最多的就是返回码5——Broker配置了allow_anonymous false,但客户端没有传用户名密码。这个错误在Wireshark里一眼就能看出来,不需要看服务器日志。

3.2 PUBLISH报文与QoS 0/1/2的完整交互差异

发布消息时的核心报文是PUBLISH,固定报头第一字节是0x30加上各种标志位。低四位中,DUP标志表示是否重发,QoS标志占两位,Retain标志表示是否保留消息。

  • QoS 0:第一字节是0x30,没有报文标识符(Packet Identifier),可变报头里只有主题名和负载。接收方收到即处理,不回复任何确认,这是最轻量的模式。
  • QoS 1:第一字节是0x32,带报文标识符,Broker处理完成后回复PUBACK确认。如果客户端在一定时间内没收到PUBACK,会重发这条PUBLISH,DUP标志置1。
  • QoS 2:第一字节是0x34,带报文标识符,但交互过程变成四次握手:PUBLISH -> PUBREC -> PUBREL -> PUBCOMP。这保证了消息恰好被送达一次,代价是报文交互数量翻倍。

从Wireshark看QoS 1的交互过程,你会在抓包结果里看到一对PUBLISH和PUBACK报文,它们的Packet Identifier是相同的。QoS 2则复杂一些,一条消息要经历四轮交互,中间差点看不出是同一批消息的关联。Wireshark实际上不提供自动关联同一Packet Identifier的一组报文,需要靠自己的眼睛和过滤规则来追踪。

我用mqtt && mqtt.msgtype == 3过滤出所有PUBLISH报文,再找出它们的Packet Identifier,从Wireshark的Packet Details面板里找到Message Identifier字段。然后再用mqtt && mqtt.msgtype == 4过滤出所有PUBACK,对比两者的Message Identifier是否一致。这个手工对照的过程虽然繁琐,但对理解MQTT QoS机制却非常有帮助。

当消息体比较大时,Wireshark里还会出现一个现象:单条MQTT消息跨越了多个TCP分段。比如一条2KB的PUBLISH消息,在Wireshark的报文列表里显示为多行TCP协议,只有最后一行的解码标签里才是MQTT。这时候需要用Wireshark的“Assemble fragmented TCP messages”功能,或者直接点开协议解析,它会在Info列提示[TCP segment of a reassembled PDU],点进去能看到组包后的MQTT内容。

我踩过一次坑:用脚本往MQTT里发了一条10KB的数据,Wireshark过滤mqtt死活看不到这条消息,看了半天才发现消息被拆在了好几个TCP包里,Wireshark默认的显示过滤对“被TCP分片的MQTT报文”不会在首行标记为MQTT。解决办法是把过滤条件改成mqtt || tcp.port == 1883,再通过“Follow TCP Stream”查看完整内容。

3.3 SUBSCRIBE/SUBACK:订阅时的过滤器是如何编码的

SUBSCRIBE报文固定头第一字节是0x82,这里的0x8是报文类型8,低四位中的0x2是必须设置的保留位。可变报头里有一个Packet Identifier,然后是一系列主题过滤器。

每个主题过滤器由主题字符串+QoS级别组成。比如订阅test/topic且QoS为1,那有效载荷就是:主题长度(2字节)+主题ASCII + QoS级别(1字节)。

Wireshark对SUBSCRIBE报文的解析非常友好,它会把订阅的Topic和QoS等级直接展开在Packet Details面板里,不需要人工解码。与之对应的响应是SUBACK报文,固定头第一字节是0x90,里面携带返回码数组。返回码0表示成功且最大QoS为0,1表示成功且最大QoS为1,2表示成功且最大QoS为2,0x80表示失败。

这里有个容易忽略的细节:SUBSCRIBE报文里的QoS表示“这个订阅允许的最大消息QoS上限”,而Broker实际下发的消息QoS是取“发布时的QoS”和“订阅时的QoS”中较小的那个。比如发布者用QoS 2发布,订阅者用QoS 0订阅,那么订阅者实际收到的是QoS 0消息。在Wireshark里看这个过程,你会看到Broker发出的PUBLISH报文的QoS标志是0,而不是2。刚开始抓包时我以为Broker出BUG了,后来才明白这是QoS降级机制。

3.4 PINGREQ/PINGRESP和DISCONNECT:连接保活与自我结束

MQTT的Keep Alive机制是靠PINGREQ和PINGRESP这对报文实现的。Keep Alive时间在CONNECT包里设置,Broker计算这个时间的一半作为“半程心跳”参考,如果超过Keep Alive时间没收到任何控制报文,就断开连接。

抓包时,如果你设置Keep Alive为60秒,你会看到客户端每30到45秒左右发一条PINGREQ,Broker秒回PINGRESP。Wireshark对这两个报文的解析非常简单,PINGREQ固定头是0xC0,PINGRESP是0xD0,两者都没有可变报头,也没有有效载荷。

注意在实际抓包时,Wireshark并不计算“距离上一包已经过了多少时间”这样的统计字段,你需要自己看Time列的时间差。我在排查设备频繁离线问题时,就是通过Time列观察到PINGREQ的间隔不规律,有的间隔甚至超过60秒,最终定位到是蜂窝网络信号差导致的上行数据无法及时送达。

DISCONNECT报文固定头是0xE0,由客户端发送给Broker,表示正常断开连接。这里关键区别在于:正常断开时携带遗嘱消息的会话(Clean Session=0且Will Flag=1),Broker不会把遗嘱消息发布出去。只有非正常断开——比如网线断开、TCP连接超时、进程崩溃——Broker才会发布遗嘱消息。这个逻辑在抓包时需要专门构造场景去验证。

4. 高级场景:遗嘱消息、Retain与会话恢复的抓包验证

4.1 遗嘱消息的完整触发与观察流程

遗嘱消息是MQTT里很多人容易搞混的一个特性。我的经验是,只有把它放在抓包场景里完整跑一遍,才能彻底理解。

具体验证步骤:

  1. 在MQTTX中新建两个连接。连接A用于发布遗嘱消息,连接B用于订阅遗嘱主题。

  2. 先在连接B中订阅遗嘱Topic,比如will/test,确保后续遗嘱发布时能被捕获。

  3. 在连接A的配置中勾选Will选项,设置遗嘱Topic为will/test,遗嘱Payload为“client A offline”,QoS选1。

  4. 连接A建立连接后,在Wireshark里能看到CONNECT报文的可变报头和Payload部分比普通连接多出了Will Topic和Will Message字段。这是遗嘱消息的“注册”阶段。

  5. 关键操作:断开连接的方式很重要。如果点击MQTTX的“断开”按钮,会正常发送DISCONNECT报文,此时Broker不会发布遗嘱消息。要触发遗嘱生效,必须把连接A的网络直接掐断。最简单的方法是在Wireshark里选择对应的TCP连接,然后用tcp.rst或者直接关闭MQTTX进程。

  6. 掐断连接后,观察连接B是否收到遗嘱消息。

从抓包角度看,当连接A被强制断开后,TCP层会看到RST或者多次重传直至超时。稍后Broker会向连接B发送一条PUBLISH报文,Topic是will/test,Payload是预设内容。这条PUBLISH报文就是遗嘱消息。

我在这个验证过程中踩过的最大的坑,是用正常断开方式去触发遗嘱消息,结果怎么都等不到遗嘱。后来才明白,正常断开时客户端会发DISCONNECT,Broker根据MQTT协议规范会丢弃遗嘱消息。协议文档里写得很清楚:遗嘱消息只有在连接被异常中断时才会发布。

4.2 Retain标志:Broker缓存与重发机制

Retain标志位决定Broker是否保留这条消息的最新值。当你用Retain标志发布一条消息后,新订阅该Topic的客户端会立刻收到这条消息,而不是等发布者再次发布。

抓包验证的方式很简单:

  1. 在MQTTX中发布一条消息到retain/test,勾选Retain选项。

  2. 断开连接A,再让一个新客户端连接并订阅retain/test

  3. 观察Wireshark,你会发现订阅后Broker立刻向这个新客户端推送了一条PUBLISH报文,Payload正是之前发布的内容。

实际场景中这个特性非常有用。比如设备状态上报,设备挂掉之前发一条Status=offline且Retain的数据,这样任何客户端订阅这个Topic时都能立刻知道当前状态,而不需要等设备重新上线。

注意,Retain标志和遗嘱消息叠加使用会产生一个很常见的模式:设备连接时设置遗嘱消息为“离线状态”,并规定Retain为1,这样一旦设备异常断开,Broker会发布并保留“离线状态”,任何新订阅者都能立刻看到。这个组合在设备状态监控场景里几乎是标配,但它对Broker存储也带来额外开销。

4.3 持久会话与离线消息的报文差别

支持离线消息需要两个条件:连接时Clean Session设置为0(或MQTT 5.0的Clean Start为0),且消息QoS大于0。这两者缺一不可。

抓包观察时,你会发现一个有意思的现象:同一台Broker上,Clean Session=1的连接断开后,再次连接时CONNACK报文的Session Present标志为0,表示服务端没有恢复任何会话状态。而Clean Session=0的连接断开后重新连接,CONNACK的Session Present可能为1,表示服务端恢复了一个持久会话。

离线消息的补发在Wireshark里看得更加直观:客户端上线后,Broker会主动推送一批PUBLISH报文,Payload是离线期间的消息,并且这些消息的QoS级别和发布时一致。我见过有人误以为离线消息只有在重新订阅时才能收到,其实只要会话还在,Broker会根据订阅关系自动补发。

有一点值得注意:持久会话对Broker内存的占用是持续性的,如果ClientID固定但用户不再上线,这些会话会一直占用服务器资源。生产环境要设置合理的会话过期时间。

5. 实战中常见的Wireshark抓包问题与Wireshark排查思路

5.1 为什么抓不到任何MQTT报文

这是我在答疑群里被问到最多的问题。排查看三个地方:

  • 抓包接口选错了。如果你Broker和客户端都在本机,必须选回环接口,不是以太网或WLAN。检查方法:确认Broker日志里的IP是127.0.0.1还是局域网IP。
  • 过滤条件写错了。注意MQTT协议名在Wireshark里是小写mqtt,不是大小写不敏感。另外如果是WS-MQTT(走WebSocket的MQTT),Wireshark需要解析WebSocket之后才能解码出MQTT,这时候单写mqtt可能看不到。
  • 报文被TLS加密了。如果你客户端连接的是8883端口,Wireshark无法识别出MQTT协议,只会显示TLSv1.2。可以先抓明文1883的包验证问题,再去处理TLS解密的问题。

另外,在Windows下还有一个常见坑:Wireshark安装时没有勾选安装Npcap,或者Npcap版本太旧,导致无法抓回环流量。重装Npcap后,回环接口才会出现在接口列表里。

5.2 Wireshark明明显示TCP包,为什么不是MQTT

这个问题的常见诱因是TCP粘包和分包。

MQTT建立在TCP之上,TCP是流式协议。当大量MQTT消息在短时间内连续发出时,底层TCP很可能将多条MQTT消息合并到同一个TCP段中发送,这就是粘包。这时候Wireshark解析第一条MQTT消息后,后续数据会在同一个TCP段里继续解析,你会看到同一个TCP段下面带多个MQTT报文。

反过来,如果MQTT消息很大(比如超过MSS),底层TCP会把一条MQTT消息拆成多个TCP分段发送,Wireshark在首行不会识别成MQTT协议,只会显示TCP。这时候你要么启用TCP重组(默认是开启的),要么就手动拼接分段内容来看。

我的习惯是:在Wireshark的Preferences -> Protocols -> TCP里确认“Allow subdissector to reassemble TCP streams”这个选项是开启的。如果某些分析场景必须关闭重组,那就要接受报文的边缘情况。

5.3 QoS 2交互过程中出现重复报文,是不是Broker的BUG

QoS 2设计目标就是防止消息重复投递,但你抓包时可能发现,在PUBLISH、PUBREC、PUBREL、PUBCOMP的交互过程中,有时候同一Packet Identifier消息会重复发送。这在协议设计里是允许的。

举例来说:客户端发送PUBLISH后,如果一直没有收到Broker的PUBREC,客户端会重发PUBLISH且DUP标志置1,Packet Identifier与首次发送时一致。如果此时Broker已经成功收到并回复PUBREC(但回复包在网络上丢了),Broker收到重发的PUBLISH时会判断Packet Identifier,然后直接回复PUBREC而不会再次存储消息。

在Wireshark里定位这类重复报文比较繁琐,因为MQTT的报文标识符不是全局唯一的,不同连接间可以复用。所以分析时一定要绑定到具体的TCP流里看,不能只看一个Packet Identifier就判断是重复包。

我的经验是:用Wireshark的“Follow TCP Stream”把完整四步协议过程拉出来,再对着协议规范逐条对应。如果不方便,可以在过滤栏输入mqtt && mqtt.packetid == N,配合点击具体流,逐个排查。

5.4 大消息被截断,只显示520字节或2090字节

Wireshark默认在抓包完成前不会直接告诉你,但当你点击一条大的PUBLISH报文后,Packet Details面板里能看到这个包的实际数据长度。如果你只看到“520 bytes”或“2090 bytes”这样的截断数据,多半是抓包时的快照长度(Snaplen)设置过小。

Wireshark默认Snaplen是262144字节,一般情况不会截断。但如果你手动调小了快照长度,或者使用tcpdump时指定了-s 128之类的小快照长度,就会出现截断情况。解决方法是重新抓包,抓包时把Snaplen设为0(表示不截断),或者直接使用默认值。

另外,“Wireshark为什么只显示520字节数据”还有一种情况:消息本身不大,但Wireshark的Decode As功能没有自动识别成MQTT,导致解析器把MQTT数据当作未知数据展示。可以右键点击该报文,选择“Decode As”,然后将TCP端口1883对应的协议改成MQTT,重新解析。

5.5 Keep Alive超时导致设备掉线,怎么从抓包上定位

当设备莫名其妙掉线时,抓包是最有效的排查手段。步骤:

  1. 抓到设备掉的时段的流量,过滤mqtt || tcp.port == 1883

  2. 看近一分钟内有没有PINGREQ发出。正常情况下,每个Keep Alive周期内至少会有一条PINGREQ和一条PINGRESP。

  3. 如果客户端发出PINGREQ,但Wireshark里看不到PINGRESP,说明Broker没有响应或者响应超时。这时候要看TCP层有没有重传。

  4. 如果连PINGREQ都没发出,说明客户端的应用层卡死或者定时器异常,问题大概率在固件代码里,不在网络上。

我在STM32+4G模块的场景中踩过几次坑,最后定位到是模块的TCP连接被基站侧回收了,但固件不知道,还保持着MQTT会话状态。这时固件发的PINGREQ实际上在空口层就丢了,Wireshark里看只有一个单独的PINGREQ,没有对端响应,随后就是TCP重传风暴。这种情况下靠MQTT的心跳机制已经来不及了,必须依赖MQTT协议层面的网络诊断为更底层的断线检测。

6. 过滤技巧与高效分析的三个实用经验

用到最后再分享三个Wireshark实操技巧,这几条是我在真实项目里反复用到的。

第一是学会用显示过滤表达式快速定位特定行为。想只看CONNECT和CONNACK报文,输入mqtt.msgtype == 1 || mqtt.msgtype == 2。账号密码错误导致连接失败,先过滤mqtt.msgtype == 2,再看返回码字段。想看所有发布消息,过滤mqtt.msgtype == 3;想追踪某个Topic,过滤mqtt.topic == "test/topic"

第二是善用Wireshark的“Follow TCP Stream”按流分析功能。MQTT虽然报文种类多,但只要跟着TCP流一条条看,协议交互逻辑会非常清晰。我在排查一个“订阅后收不到消息”的问题时,发现订阅成功(SUBACK返回码为0),但Broker就是不向客户端发PUBLISH。后来跟了一遍TCP流才发现,这个客户端在旧连接上订阅的是一个Topic,实际上消息发到另一个Topic了,纯粹是订阅关系配错了。

第三是搭建测试环境来复现特定场景。Wireshark抓包解决不了所有问题,但它能帮你把问题范围缩小。我现在的做法是:先根据现象构造一个最小化环境(本地Mosquitto + MQTTX),然后在抓包里复现,逐一排除。真正常见的MQTT问题,十有八九是QoS设置不对、Topic不匹配、Clean Session理解错误,这些在抓包面前都藏不住。

Wireshark在MQTT调试中的角色更像是一个“眼见为实”的帮手。纸上谈兵式的协议分析远远不够,只有亲手抓到CONNECT、SUBSCRIBE、PUBLISH、PUBACK、PINGREQ这些报文,才能对MQTT的行为建立一个直觉认知。遇到设备消息丢了、订阅收不到、掉线频繁这类问题时,条件反射地抓起包看一眼,通常比漫天瞎猜定位得更快。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/18 18:13:14

MMC七电平整流器MATLAB建模与故障分析

1. MMC七电平整流器模型概述模块化多电平换流器(Modular Multilevel Converter, MMC)作为高压直流输电(HVDC)领域的核心技术,近年来在电力电子领域获得了广泛应用。七电平拓扑结构因其在输出波形质量和器件数量之间的良…

作者头像 李华
网站建设 2026/9/18 18:11:19

10分钟跑通实时数字人直播:LiveTalking完整快速指南

10分钟跑通实时数字人直播:LiveTalking完整快速指南 【免费下载链接】metahuman-stream Real time interactive streaming digital human 项目地址: https://gitcode.com/GitHub_Trending/me/metahuman-stream 直播间的数字人为什么总慢半拍?话没…

作者头像 李华
网站建设 2026/9/18 18:10:48

Word 用 Bibtex4Word 插入 BibTeX 参考文献

凌晨一点半,编辑部退回来的邮件里通常只有一句干巴巴的话:参考文献格式不符合本刊要求。文档是用 Word 写的,文献库是十几年攒下来的 .bib,Zotero 导进导出折腾了三轮,样式依旧对不上——这是我这些年帮人收拾论文格式…

作者头像 李华
网站建设 2026/9/18 18:09:36

基于机器学习的网络异常流量检测:从特征工程到阈值校准

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华