mqtt-sn网关设置的空闲超时时间无效,并且无法查看客户端的心跳值(一切都为0)

环境

  • EMQX 版本:v5.8.1
  • 操作系统版本:macOS 15.1

重现此问题的步骤

  1. 打开mqtt-sn网关
  2. 使用 mqtt-sn-tools 订阅随便一个主题并建立起一个客户端连接成功
  3. 当客户端keep alive心跳设置为60秒,超过60秒钟后并没有收到网关的PINGRESP包
  4. 就算设置空闲超时时间到更高的数值也没用
  5. 无法正确显示客户端的心跳

预期行为

mqtt-sn网关遵从设置的空闲超时时间值,并且正确客户端显示客户端的心跳值

实际行为

可以发一下 emqx 侧的全部 debug 日志么。

可以说就只有这句的报错receive_unknown_packet_in_idle_state

方便打开 debug 么 dashboard -》 managment =》 log

log.zip (9.6 KB)

谢谢,看起来是个 bug(而且不简单)。会在下周一开例会排期来排查哈。

1 个赞

请问有任何进展吗

已经排期了,应该安排在 5.9.0 的。

1 个赞

感谢反馈,这里的几个问题:

  1. 空闲超时(IdleTimeout):是用来控制网关在Socket创建后【多长时间未收到第一个报文】就是否链接资源。它和这里的心跳逻辑没有关系的。

  2. 页面 Keepalive 始终显示为0,这个是 BUG。

  3. 当 mqtt-sn-sub 的心跳设置为 60s 后,客户端报类似以下日志(我本地重现的):

     2025-01-08 10:54:46 ERROR Keep alive error: timed out while waiting for a PUBLISH from gateway.
    

    服务端报(从帖子的 log.zip 文件里找到的):

    2024-11-14T10:57:31.471993+08:00 [warning] msg: receive_unknown_packet_in_idle_state, peername: 192.168.107.1:44473, packet: {mqtt_sn_message,22,<<>>}
    

通过观察服务器的日志可以发现 mqtt-sn-sub 在创建连接和发送心跳时使用不动的源端口

2024-11-14T10:56:31.425170+08:00 [debug] msg: RECV_packet, peername: 192.168.107.1:50898, packet: SN_CONNECT(W0, C1, ProtocolId=1, Duration=60, ClientId=macbook)

连接使用的源端口是 50898,发心跳用的 44473 从而导致了服务器无法识别心跳报文。

问题3解决办法:

  1. 固定客户端消息发送端口。这在简单网络下有用,例如客户端直连服务器,但在在复杂的网络下,例如经过中间的网络设备,NAT 转换等,服务端拿到的客户端端口也是由中间的网络设备维护的,也是可能会变化的。一旦 NAT 映射关系被回收,那么上行的心跳会错误,下行的报文也无法抵达了。

  2. 将心跳时间改短,例如 10s。如果业务上,这个客户端需要实时关注消息,那应该把心跳设定的更短,以维持在复杂网络上的网络链路保活

1 个赞

我好像找到了问题所在,这种问题只会出现在docker部署emqx的情况下:

docker compose部署emqx

在任务管理器中查看得到mqtt-sn-tool的mqtt-sn-sub真实占用的端口是65344。

docker network的网关是192.168.97.1

在emqx容器的log中查看客户端的地址发现被网关的地址隐藏了,如图192.168.97.1:26180

超过60秒后,在客户端发送PINGREQ心跳的时候端口又会被改变一次成192.168.97.1:35768

本地部署emqx

超过60秒后客户端口63161依然不会改变

结论

问题不在mqtt-sn-tools改变发心跳端口号,而是在docker容器上?我该如何配置呢:thinking:

如果业务要求心跳必须超过60s以节省带宽怎么办呢?还有任何的解决方案吗?