3. 连接断开原因分类:本地主动断开、远端主动断开、链路超时断开、协议栈内部错误断开

做物联网开发这些年,我调试过最多的就是连接莫名其妙断掉的问题。你想想看,设备明明连得好好的,突然就断了,日志里就一行“Disconnected”,连个原因码都不给。这种时候,你只能靠经验去猜。

其实GATT连接断开的原因,归纳起来就四大类。我习惯把它们分成:本地主动断开、远端主动断开、链路超时断开、协议栈内部错误断开。搞懂这四类,你排查断连问题至少能少走一半弯路。

核心观点:90%的断连问题,根源都在前三类。协议栈内部错误虽然棘手,但出现频率其实不高。

GATT 连接断开原因分类 连接断开 本地主动断开 远端主动断开 链路超时断开 协议栈内部错误 应用层主动调用断开API 用户手动断开连接 对端设备主动断开 对端应用层断开 连接超时(LL超时) 物理链路断开 协议栈异常崩溃 资源耗尽/内存溢出 💡 排查断连问题,先判断属于哪一类 再针对性地看日志和抓包

3.1 本地主动断开

什么叫本地主动断开?说白了,就是你的设备自己决定要断开的。不是别人踢你,也不是信号不好,就是代码里调了断开API。

我见过很多新手,一上来就怀疑协议栈有问题。其实先看看代码里有没有主动调用 disconnect() 或者 gatt_disconnect() 之类的函数。嗯,这一步往往能省下半天调试时间。

常见的场景有这些:

  • 应用层主动断开:比如用户按了断开按钮,或者设备进入低功耗模式前主动释放连接。
  • 业务逻辑触发:数据交互完成,或者检测到对端设备不合法,主动断开。
  • 重连机制:有些设备发现连接质量不好,会主动断开再重新连接。

我的习惯:在调用断开API的地方,加一个自定义的原因码日志。比如 disconnect(REASON_USER_ACTION),这样排查时一眼就能看出来是主动断的。

3.2 远端主动断开

这个就更有意思了。不是你断的,是对面那个设备断的。你想想看,手机APP那边点了断开,或者对端设备出了什么状况,连接就断了。

我在项目中遇到过最典型的情况:手机APP在后台被系统杀掉了,然后连接就断了。当时查了半天日志,发现是远端主动断开,原因码是 0x13(远程用户终止连接)。

远端主动断开的常见原因:

  • 对端应用层主动断开:APP退出、用户操作、业务完成。
  • 对端协议栈异常:对端设备内部错误,主动发起了断开流程。
  • 对端进入低功耗:有些设备在休眠前会主动断开所有连接。

注意:远端主动断开时,你的设备会收到一个 disconnect 事件,里面带有一个原因码。这个原因码非常关键,一定要把它打印出来。我曾经就因为没打印原因码,多花了两天时间排查。

3.3 链路超时断开

这个是最让人头疼的。不是谁主动断的,是连接自己“超时”了。蓝牙协议里有个链路层超时机制,如果一段时间内收不到对端的任何包,就会认为连接已经丢失,自动断开。

为什么会超时?说白了,就是信号不好,或者干扰太严重。你想想看,两个设备隔得太远,中间还有墙,数据包发过去对方收不到,对方回应的包你也收不到。几次下来,链路层就判定连接超时了。

我建议你重点关注这几个参数:

参数 说明 典型值
连接间隔 两个连接事件之间的时间 7.5ms ~ 4s
从机延迟 从设备可以跳过的连接事件数 0 ~ 499
超时时间 链路层等待超时的时间 100ms ~ 32s

这三个参数是联动的。连接间隔越大,从机延迟越高,超时时间就得相应调大。否则很容易出现误判超时。

避坑指南:我曾经在一个项目中,把连接间隔设成了100ms,超时时间只设了200ms。结果设备稍微离远一点就断连。后来把超时时间调到1s,问题就解决了。记住,超时时间至少要大于连接间隔的3倍。

3.4 协议栈内部错误断开

这类问题最诡异。日志里可能就一行 HCI Error,或者干脆什么日志都没有。原因往往是协议栈内部出了状况,比如内存溢出、缓冲区满、任务调度异常等等。

我遇到过最离谱的一次:设备运行了三天三夜,突然断连了。查了所有日志,没有任何异常。最后发现是协议栈的一个定时器任务因为优先级太低,被其他任务饿死了。嗯,这种问题真的很难复现。

常见的协议栈内部错误:

  • 内存不足:协议栈内部缓冲区耗尽,无法维持连接。
  • 任务死锁:协议栈内部任务互相等待,导致连接管理线程卡死。
  • 硬件异常:蓝牙控制器出现硬件错误,比如射频模块异常。
  • 固件Bug:协议栈本身有未修复的缺陷。

重要提醒:遇到协议栈内部错误,先别急着怀疑硬件。我建议你先检查一下协议栈的版本,看看有没有已知的Bug修复。另外,把协议栈的调试日志全开,有时候能抓到关键信息。

3.5 如何快速定位断开原因

说了这么多,到底怎么快速定位?我个人的排查顺序是这样的:

  1. 看日志:有没有主动调用断开API?有没有收到断开事件?原因码是多少?
  2. 抓空口包:用抓包工具看是谁发的断开请求。这个最直观。
  3. 检查参数:连接间隔、超时时间设置是否合理?
  4. 排除干扰:换个环境测试,看看是不是信号问题。
  5. 升级协议栈:如果以上都排除了,试试升级协议栈版本。

我的经验:80%的断连问题,通过第一步“看日志”就能定位。所以一定要把日志系统做好,尤其是断开原因码和事件类型,必须打印出来。

好了,这四种断开原因,你心里有数了吧?下次遇到断连问题,先对号入座,看看属于哪一类。方向对了,排查起来就快多了。