首页/新闻资讯/正文详情

Wireshark抓USB包实战:URB分析与设备调试

发布时间:2026/9/25 4:14:37 来源:云帆数科 栏目:资讯中心
Wireshark抓USB包实战:URB分析与设备调试
搞嵌入式或USB驱动开发的八成都有过这种经历串口工具里发了数据设备没反应上位机收不到东西程序代码翻来覆去查了好几遍新拿来的USB设备插上系统直接弹“无法识别的设备”。这时候你开始怀疑线是不是坏了接触是不是不良甚至怀疑是不是芯片本身有问题。但如果手头有Wireshark你其实可以在USB协议栈的层面直接看到数据到底有没有被主机发出去、设备有没有回应、错在哪个环节——问题完全不需要靠猜。Wireshark抓USB包这件事说难不难但和抓网卡数据完全是两套逻辑网上的教程大多讲得零碎我这次把自己实际用下来的流程完整整理一遍包括安装、选接口、看字段、分析场景以及几个真实踩过的坑尽量让你看完就能直接用。1. Wireshark抓USB包的原理与边界抓的和网络包根本不同很多人第一次听说Wireshark能抓USB包时下意识会以为它是通过网络接口去嗅探USB设备的数据这其实是个误区。Wireshark本身只是一个协议分析前端它真正做的事情是把底层采集到的数据按协议字段拆开、解析、展示。至于数据是怎么从USB总线里“捞”出来的在Windows和Linux上有完全不同的机制搞清楚这个后面才不会卡在“安装好了但什么都抓不到”这种问题上。1.1 为什么Wireshark能看到USB总线数据在Windows平台上Wireshark抓USB依赖一个叫USBPcap的驱动这是由Wireshark团队维护的一个开源USB总线捕获驱动。它在系统里注册了一个过滤器驱动挂在USB主机控制器上内核里凡是主机控制器和设备之间的URBUSB Request BlockUSB请求块交互相都会经过这一层于是USBPcap能在我毫不知情的情况下把经过的数据复制出来抛给Wireshark解析。这里要强调一个概念USBPcap抓到的是URB层面的数据不是USB物理线缆上D/D-的原始模拟信号。URB是USB驱动程序用来描述一次数据传输请求的数据结构它包含了传输方向、端点号、传输类型、数据缓冲区内容等。也就是说Wireshark能告诉你“主机向0x01端点发了30字节数据”但不会告诉你这30字节在物理总线上是以什么码型、什么时序发出去的。如果想看物理层电气特性得用USB协议分析仪或者逻辑分析仪这是工具边界后文我会再展开。在Linux平台上机制类似但实现完全不同。Linux内核里有一个usbmon内核模块专门把USB主机控制器上经过的URB数据导出到用户态接口是/dev/usbmon*设备节点。Wireshark在Linux上直接通过libpcap读取usbmon接口不需要额外装驱动前提是内核编译时开了usbmon支持主流发行版默认都开。1.2 Wireshark只是解包器抓包前必须确认驱动理解了上面这个关系你就会明白一个很重要的点装完Wireshark不等于就能抓USB。Windows上必须确保USBPcap驱动成功安装并且服务处于运行状态Linux上必须确保usbmon模块已经加载并且当前用户有读取权限。很多新手在这里栽跟头Wireshark主界面确实能看到USBPcap接口列表但点了开始抓包后窗口里一条数据都没有多半就是驱动没生效或权限不够。另外值得注意的是USB的抓包逻辑和网络抓包还有一个显著差异网络抓包时如果网卡不支持混杂模式可能只能抓到广播和自己的流量而USB抓包没有“混杂模式”这个概念USBPcap驱动是直接挂在控制器上总线上的URB只要经过该控制器就能全量捕获。所以问题从来不是“为什么漏包”而是“为什么一个包都没抓到”——前者通常关联的是接口选错了控制器后者通常是驱动层面根本没工作。2. 环境准备Windows和Linux下分别怎么搭抓包环境环境配置这一步不同平台的处理方式差距还挺大我分开讲。先讲Windows因为USBPcap的安装和Wireshark集成做得相对友好再讲Linux因为usbmon虽然不用装额外驱动但权限坑比较多。2.1 Windows下安装USBPcap装驱动比装软件更关键安装Wireshark的时候安装向导里有一步是Select Components选择组件里面会列出可选的附加功能其中就有USBPcap勾上它就会一并安装不用单独去下载。如果你当初装Wireshark时没勾这个选项也不用重装整个软件直接去USBPcap的官方页面单独下载安装包装一遍即可。有个细节值得单独提醒USBPcap安装完成后它不只是往系统里塞了几个文件还会注册一个内核驱动服务。如果安装过程中UAC弹窗让你确认一定要点“是”。装完之后最好重启一次机器因为驱动服务的加载和绑定需要重新初始化不重启的话Wireshark里可能能看到接口但无法正常启动捕获。安装好后怎么确认驱动在工作几个手段打开设备管理器点“查看”-“显示隐藏的设备”在“非即插即用驱动程序”里能找到USBPcap设备状态正常说明驱动已注册。打开Wireshark主界面在“接口列表”里能看到多个名为USBPcap的接口每个接口后面都带一个类似\\.\USBPcap1的设备路径。在命令行里执行tshark -D如果能列出USBPcap开头的接口说明Wireshark的采集引擎已经能识别驱动。2.2 Linux下使用usbmon加载模块和解决权限Linux下相对简单——不用装任何驱动但要确认三件事。第一内核模块是否加载。执行lsmod | grep usbmon如果没输出先加载sudo modprobe usbmon发行版如果默认关闭usbmon开机后每次都要手动modprobe可以在/etc/modules-load.d/里加一行usbmon让它开机自动加载。第二设备节点是否存在。加载成功后/dev下一般会出现usbmon0、usbmon1、usbmon2这样的节点。注意usbmon0比较特殊它对应的是所有控制器的聚合接口抓包效果不太稳定一般不建议选它usbmon1、usbmon2等分别对应不同的USB控制器。第三也是坑比较多的地方——权限。/dev/usbmon*节点默认root用户可读写普通用户跑Wireshark去抓会报权限错误。临时解决可以sudo wireshark但我不建议长期这样干因为Wireshark历史上出过多次协议解析漏洞以root身份跑图形化分析软件不是好习惯。正规做法是给用户加对应权限sudo usermod -aG wireshark $USER # 很多发行版安装wireshark时会创建wireshark组 sudo chmod 660 /dev/usbmon* # 或者制定udev规则改完之后退出重登一次让组权限生效。2.3 验证抓包接口一个数据包测试环境配置完最好先做一次快速验证确认整套链路是通的。我常用的方法是打开Wireshark双击一个USBPcap接口开始抓包然后去插拔一次U盘或USB鼠标。如果接口选对了界面上马上会涌入大量URB包包括枚举阶段的控制请求、鼠标的HID中断传输等。如果抓了半天一个包都没有先别急着往下分析回头检查驱动和权限因为每当这种时候90%的问题出在“接口选错了控制器”这是我下面要详细说的事。3. 接口选择与分析思路一台机器N个USBPcap接口到底选哪个很多人好不容易装好USBPcap兴致勃勃打开Wireshark结果发现接口列表里根本不是预期的“一个USBPcap”而是一排——USBPcap1、USBPcap2、USBPcap3有的机器甚至更多。选错了接口抓到的要么是空白要么是另一路控制器上跟你目标设备八竿子打不着的流量。3.1 USB主机控制器的对应关系现代PC主板上通常会集成多个USB主机控制器。粗略分一下早年有EHCIUSB 2.0、OHCI/UHCIUSB 1.1现在主流是XHCIUSB 3.0/3.1也向下兼容2.0/1.1。设备管理器里展开“通用串行总线控制器”能看到好几个控制器条目每个控制器对应一组物理USB端口。USBPcap给每个控制器分配一个独立接口USBPcap1、USBPcap2就对应着不同的控制器。问题来了怎么知道你的USB设备挂在哪个控制器上最笨但有效的方法是逐个试。先插上目标设备然后依次在每个USBPcap接口上开始抓包直到界面上看到设备的枚举包或数据包为止。这个过程快则几秒慢也不过半分钟因为设备一旦插上、只要系统识别到了控制器上必然有URB流量。不过如果你插的是一个已经枚举完成、处于空闲状态的设备比如USB鼠标没动那可能还真没什么流量所以测试时最好用一个会主动产生流量的设备或者直接把设备重新插拔一次。还有一个更精准的办法用Windows的设备管理器右键你的USB设备选“属性”-“详细信息”-“属性”下拉框选“位置路径”。里面的PCI路径会直接标明它挂在哪个控制器器号下对照USBPcap接口的控制器信息就能准确对应。3.2 控制台数据流量与抓包筛选思路选对接口之后下一个问题是数据量。USB全速抓包时流量不小尤其是U盘拷贝大文件或者USB摄像头跑视频流URB包每秒几百上千个很正常满屏幕滚得根本看不过来。这时候就要靠Wireshark的两样东西过滤器和颜色规则。USB抓包最常用到的显示过滤器我列几个过滤表达式作用usb.transfer_type 0x02只看批量传输BulkU盘、串口数据都在这里usb.transfer_type 0x03只看中断传输InterruptHID鼠标键盘在这里usb.urb_type URB_SUBMIT只看主机发出的数据提交请求usb.urb_type URB_COMPLETE只看设备端完成处理的响应usb.endpoint_number 1只看特定端点号比如某个Bulk IN端点usb.bmRequestType 0x40只看主机发给设备的标准控制请求usb.setup.bRequest 0x05设置USB地址的控制请求枚举关键我这个习惯是先用usb.transfer_type把流量缩小到某一类传输再配合URB_SUBMIT/URB_COMPLETE看请求和回应的配对关系效率高很多。尤其是分析设备为什么没响应的时候SUBMTI和COMPLETE成对出现才说明一次交互走完整了只有SUBMIT没有COMPLETE要么是设备根本没收到要么是收到了但一直没回这本身就是重要的故障信号。4. USB抓包实战URB结构、枚举过程与关键字段解读接口选好、过滤器设好之后真正要面对的是怎么从一堆URB包里找到你想看的那个“关键时刻”。USB协议本身不算复杂但分支多如果不理解包结构很容易被一堆字段弄得头晕。4.1 URB包的常见字段Wireshark解析USB协议后每个URB包都会展开成很详细的字段树但核心要关注的其实就几项urb_typeURB类型。一般不是URB_SUBMIT就是URB_COMPLETE。SUBMIT是主机往控制器提交一个传输任务COMPLETE是控制器报告这个任务执行完毕附带实际传输字节数和状态码。这两者的成对关系是排查的关键。direction传输方向。URB_PIPE_IN代表设备到主机INURB_PIPE_OUT代表主机到设备OUT。注意这里的IN/OUT都是从主机视角看的。transfer_type传输类型。0x01是控制传输Control0x02是批量传输Bulk0x03是中断传输Interrupt0x04是等时传输Isochronous。endpoint_number端点号。USB设备用端点来区分不同的数据管道同一个端点可能有IN和OUT两个方向。setup packet控制传输里特有的数据字段包含bmRequestType、bRequest、wValue、wIndex、wLength等字段枚举过程和各类标准请求全都靠它。4.2 一次完整枚举过程长什么样拿一个最典型的场景——插入U盘。抓包后放大时间范围你首先会看到设备插入时主机发起的枚举流程。刚开始一定是几个控制传输包其中必然包含USB Address Set设置设备地址bRequest0x05和Get Descriptor获取描述符bRequest0x06等请求。地址设置成功后后续包的src/dst里会出现1.0.0这样的设备地址标记比如host到1.0.0、1.0.0到host。枚举成功完成后接下来主机还会U盘发SCSI命令比如Read Capacity读取容量、Mode Sense读取模式参数等这些是批量传输。如果你的设备枚举到一半就断了抓包窗口里可能只见到了Get Descriptor请求但设备没有返回COMPLETE或者返回了一个STALL状态。这个位置就是定位“为什么系统不认这个设备”的断点。4.3 USB转串口怎么从包看数据真正收发再说一个特别实际的场景把USB转串口FT231X、CH340这些的调试接进来。USB转串口芯片本质是一个USB设备它把串口数据封装成USB批量传输包。上位机往串口发数据时Wireshark里能看到主机对设备OUT端点提交的URB_SUBMIT包包里携带的就是串口要发出去的那些字节设备收到后会回一个URB_COMPLETE表示“接收完成”。反过来设备往主机方向发数据则是IN端点方向上的URB_COMPLETE包携带的是实际读取到的数据。我遇到过不少次这种情况串口工具显示“发送成功”但设备那边一点反应都没有在Wireshark上看URB_SUBMIT包确实发出了数据内容也对但紧接着的URB_COMPLETE包状态是No such device或者说错误码。这说明数据根本没有到达设备——大概率是硬件链路问题线虚接、共地不良而不是软件逻辑问题。这个判断一旦做出来能把调试方向从“改代码”快速矫正到“检查硬件”。4.4 过滤器用得好分析效率翻倍抓包过程中如果整包数据太多建议抓的时候就用捕获过滤器提前筛掉一部分增量但我更推荐“抓全量、显示过滤”的工作方式。因为USB数据包相对网络包而言总量不算大全量捕获也就几十MB级别分析时通过显示过滤器层层缩小比一开始就筛掉更多可控。我一般分析一个USB批量传输问题时习惯用这三步usb.transfer_type 0x02只留批量传输usb.device_address 目标设备地址只留目标设备usb.urb_type URB_COMPLETE只看完成事件因为COMPLETE包里才有实际传输的数据字节内容三个条件一叠一次完整的收发对话基本就一目了然了。5. STM32/USB设备异常排查抓包怎么定位不识别和收不到数据做嵌入式尤其是用过STM32这类MCU自带的USB外设最常见的两个问题就是“Windows提示无法识别的USB设备”和“设备枚举成功但虚拟串口收不到数据”。这两个问题靠看代码反反复复纠结经常不如抓一次包一眼看清楚来得快。5.1 无法识别的USB设备抓包定最有用的“死亡瞬间”Windows弹“Unknown Device”的时候其实是在USB枚举过程中途宣告失败。抓包时系统在设备插入瞬间发起的枚举控制请求通常会完整地出现在Wireshark里关键是看中间哪个环节断了。最常见的情况是先看到主机发GET_DESCRIPTOR获取设备描述符请求然后设备没有响应没有COMPLETE或者响应的包状态栏里带了个STALL标识。这说明设备和主机握手了、设备也的确在总线上但它对标准请求没能给出有效应答——这通常指向固件里的USB设备描述符配置有问题或者中断处理逻辑没把USB标准请求接住。另一个常见卡点是设备地址设置成功后继续读配置描述符时断开这个阶段失败经常是描述符长度、端点配置不匹配导致的。这一招在我排查STM32项目时特别管用。有一次用户反馈设备在台式机前面板口不识别、后面板口正常抓包一对比发现前面板口上枚举明明完成了问题反而出在供电上——设备枚举完成进入正常运行后对电流需求增大前面板口供电不足导致设备瞬时掉线重连。这种情况代码层面根本看不出问题但抓包里反复枚举的时序特征一目了然。5.2 STM32 USB虚拟串口收不到数据另一个高频问题是STM32配置成了USB虚拟串口上位机打开串口助手设备枚举成功但发送数据后上位机收不到、或者发下来设备没反应。这时候抓包能区分出问题到底出在上位机、USB链路、还是MCU固件。如果上位机往串口写了数据而抓包里看不到主机到设备方向的URB_SUBMIT包说明数据就没送到USB控制器是上位机软件或串口驱动层面的问题如果URB_SUBMIT包存在但之后的URB_COMPLETE包状态不对说明USB链路有问题如果SUBMIT和COMPLETE都正常包里的数据内容也对但MCU端dump出来却不对那问题在MCU固件对端点数据的后续处理。按这个排查顺序走能省下大把盲目改代码的时间。5.3 用Python和tshark做自动化抓包验证不少人在调试的时候不想每次手动开Wireshark点界面尤其是做自动化回归测试更希望能用脚本直接抓包、分析、出结论。这个场景下可以借助tshark命令行工具配合Python脚本实现。网上常看到有人问“Python 2.7加pyshark为什么抓不到包”如果是老项目还挂着Python 2.7大概率是pyshark版本和Wireshark安装的tshark版本不兼容或者Wireshark没有安装到默认路径导致pyshark找不到可执行文件。我的建议是别在2.7上纠结了直接切到Python 3环境用pyshark的decode操作稳定省心import pyshark captures pyshark.LiveCapture(interfaceUSBPcap1) for pkt in captures.sniff_continuously(packet_count100): if hasattr(pkt, usb): print(pkt.usb.transfer_type)当然这只适合快速验证和低流量场景高压力下Python层的丢包问题就需要用tshark原生命令先落文件再做解析原理上我按下不表但思路是“采集和解析分离”。6. 抓包过程中的常见坑与我的经验补充最后写几条相对冷门但很实际的坑都是我反复踩过之后得出的教训每条都能省你几个小时。6.1 URB_COMPLETE里才有数据正文SUBMIT里不一定有USB抓包中一个容易误导新手的点是URB_SUBMIT包和URB_COMPLETE包里都可能带数据内容但方向不同。主机发出的数据OUT方向通常在SUBMIT包里能看到payload设备返回的数据IN方向则要到COMPLETE包里才看得到实际payload。如果你一直盯着SUBMIT包找设备返回的数据当然什么都找不着但经过一段时间你会习惯把两个包配对起来看。6.2 抓包接口别只试USBPcap1机器上多个USBPcap接口不一定是“1一定要试2一定要试”——我的经验是优先看XHCI控制器对应的那个接口因为绝大多数现代主板上的USB口最终都会挂到XHCI控制器上。如果设备插在USB 2.0口上机械上还是走XHCI只是具体端口速度不同。最简单省事的判断方式是插上目标设备后打开Windows设备管理器定位到目标设备属性里的位置路径它会显示类似Port#... Hub#...对应PCI总线地址再到USBPcap接口属性里对照同一控制器路径。6.3 USBPcap的数据是主机视角需要再次强调USB抓包反映的是主机控制器看到的数据。如果问题出在设备端固件对数据的处理逻辑比如拿到了正确数据但处理出错抓包没法直接暴露出来这时候抓包的作用是“证伪”——先排除主机和总线层面的问题把怀疑范围缩小到设备固件再去查MCU代码。用排除法排查问题虽然听着不够高大上但在工程里极其好用。6.4 补一个工具扩展建议如果你的项目进度允许、预算宽松而且需要看物理层信号时序比如测量复位时序、看看设备挂起恢复过程建议搞一个USB协议分析仪。Wireshark负责URB层分析仪负责物理层两者配合起来基本能覆盖USB调试90%以上的场景。不急着买的话用一个几十块钱的廉价USB 2.0分析仪也能跑通基本流程。USB协议分析逻辑很大程度取决于在哪里看, WireShark USBPcap 是离主机最近的一层, 离设备固件最近的是示波器/逻辑分析仪。最后说一下我个人的习惯。实际调USB问题时我很少上来就打开Wireshark抓海量包慢慢翻。更高效的是先想清楚我要验证哪个假设是枚举失败是传输超时是数据内容错误然后设定对应过滤器触发一次特定操作再打开抓包文件精准看那几毫秒里发生的URB交互。这一套流程用顺了USB调试周期能明显缩短很多“诡异问题”最后往往都指向很简单的物理层或配置问题。你可以先从一个小场景下手把你手头的USB设备重新插拔一次看一遍完整的枚举包再对照我说的字段去认一遍——不用多久你就能在Wireshark里读出设备的“心跳”了。

相关推荐

PMOS缓启动电路设计:米勒平台效应与RC参数计算实战
PMOS缓启动电路设计:米勒平台效应与RC参数计算实战

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

营销应届生如何用AI项目证明业务能力
营销应届生如何用AI项目证明业务能力

我理解你的要求,也完全认同这个方向的价值——应届生在求职营销岗时,最常陷入的误区不是能力不足,而是“能力表达失效”:简历上堆满“熟练使用Excel”“掌握PS”“会写文案”“了解AI工具”,却没人看得懂你到底做过什么… · 2026/9/25 4:14:37

开源AI知识库:30天构建免费替代付费课的AI学习网站
开源AI知识库:30天构建免费替代付费课的AI学习网站

上上个月,一个跟我认识快十年的朋友花了4999块报了个AI变现训练营。学完之后他拉我喝酒,说了一句让我印象极深的话:"最贵的不是那4999块钱,是每天晚八点到十一点坐在电脑前听老师念PPT的六十个小时。"更扎心的是&#x… · 2026/9/25 4:14:37

STM32 I2C CubeMX配置四大致命陷阱与信号完整性避坑指南
STM32 I2C CubeMX配置四大致命陷阱与信号完整性避坑指南

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

树莓派4B变身AI牛马:8GB内存跑本地大模型的实践与避坑指南
树莓派4B变身AI牛马:8GB内存跑本地大模型的实践与避坑指南

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

OpenClaw驱动SolidWorks二次开发:自然语言建模与自动化导出实战
OpenClaw驱动SolidWorks二次开发:自然语言建模与自动化导出实战

最近我把 OpenClaw 这个本地 AI Agent 框架和 SolidWorks 的二次开发链路打通了,现在能做到用自然语言直接驱动一部分建模、改参和导出操作。这套组合的定位不是拿 AI 替代 CAD 软件,而是让 AI 当“一个能听懂人话的调度员”,把重复的 API 调… · 2026/9/25 4:58:51

从 Codex CLI 到知识库:TaoToken 统一 Key 驱动的 AI 代理个人知识管理全流程
从 Codex CLI 到知识库:TaoToken 统一 Key 驱动的 AI 代理个人知识管理全流程

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

Python寒假作业实战指南:从环境搭建到代码调试全流程
Python寒假作业实战指南:从环境搭建到代码调试全流程

拿到“Python第一次作业(寒假)”这个标题,我第一反应是想起自己当年第一次提交Python作业的样子——表面上是写几段代码,实际上一大半时间都耗在装环境、调报错、纠结“为什么输出和我想要的不一样”上面。这篇文章就是给同样在寒… · 2026/9/25 4:58:44

把显示器插到核显上:5090 单卡跑 Qwen 27B 262K 上下文的显存优化实战
把显示器插到核显上:5090 单卡跑 Qwen 27B 262K 上下文的显存优化实战

1. 这个标题到底在说什么:先拆解核心逻辑第一次看到“把显示器插到核显上——5090 跑本地 Qwen 3.8 27B,上下文拉满 262K”这个标题,很多人第一反应是:显示器插哪儿跟跑模型有什么关系?这不是玄学吗?我一开… · 2026/9/25 4:58:38

数值优化(Numerical Optimization)学习系列-03-共轭梯度方法(Conjugate Gradient)
数值优化(Numerical Optimization)学习系列-03-共轭梯度方法(Conjugate Gradient)

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

创维E900V22D刷机全攻略:S905L3SB芯片兼容性解析与救砖实战
创维E900V22D刷机全攻略:S905L3SB芯片兼容性解析与救砖实战

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

MQTT协议原理与Broker服务器搭建实战:从Mosquitto到EMQX
MQTT协议原理与Broker服务器搭建实战:从Mosquitto到EMQX

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

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码