stm32 mqtt 如何判断心跳包发送成功_MQTT系列最终章Paho源码分析(三)心跳与重连机制...

本文深入探讨了STM32使用MQTT协议时的心跳包发送与接收机制,分析了Eclipse Paho库在心跳超时、异常处理及重连策略上的实现。介绍了心跳初始化、定时器的使用以及如何判断心跳包发送成功和处理连接断开的情况。此外,还提及了消息的恢复和重发机制。
摘要由CSDN通过智能技术生成

写在前面

        通过之前MQTT系列-Eclipse.Paho源码分析(二)-消息的发送与接收的介绍,相信仔细阅读过的小伙伴已经对Eclipse.Paho内部发送和订阅消息的流程有了一个较为清晰的认识,今天我们就把剩下的边角料扫一扫,也就是Eclipse.Paho作为客户端是如何进行容灾补偿和心跳的相关介绍。

心跳机制

首先了解一下在MQTT协议中心跳请求和响应是如何规定的。下面是官方文档中的描述:

bb2c0c1c6cc538392a01b69e2727f0b5.png

简单来说,就是在创建连接时发送的CONNECT控制报文中,在第九和第十字节会携带客户端与服务端的最大连接时长,如果超过这个时间,服务端和客户端会各自做一些相应的处理。但是这样会有一个问题,当客户端在超过最大连接时长的时间段内确实没有消息上送至服务器,此时服务器是无法判断因为客户端出现故障导致的还是确实没有收到消息导致的。所以MQTT协议中规定了PINGREQ和PINGRESP两种控制类型的报文用来处理上述情况,即如果客户端真的没有消息上送,你也要定时给我发送一个PINGREQ类型的报文告诉我你还活着,我服务器收到后会即使回送一个PINGRESP报文告诉客户端我收到了,这就是一条心跳消息。

下面我们来看看Eclipse.Paho的实现: 初始化心跳消息发送器 默认eclipse paho提供了两种MqttPingSender的实现:
  • TimerPingSender:使用了Java的原生定时工具Timer

  • ScheduledExecutorPingSender:基于线程池的定时任务调

之前说过我们在创建连接时首先会创建一个异步的客户端连接对象MqttAsyncClient, 在创建MqttAsyncClient时,会默认将心跳消息发送器TimerPingSender创建出来 ,也就是说系统默认使用的就是TimerPingSender,具体代码如下
public MqttAsyncClient(String serverURI, String clientId, MqttClientPersistence persistence) throws MqttException {
        // 构造方法中创建 TimerPingSender    this(serverURI, clientId, persistence, new TimerPingSender());  }
我们来分析一下这个类,这个类实现了MqttPingSender接口,接口中提供了5个方法
 public interface MqttPingSender {
      /**   * Initial method. Pass interal state of current client in.   * @param  comms The core of the client, which holds the state information for pending and in-flight messages.   */   // 初始化心跳发送器    void init(ClientComms comms);  /**   * Start ping sender. It will be called after connection is success.   */   // 心跳开始    void start();  /**   * Stop ping sender. It is called if there is any errors or connection shutdowns.   */   // 心跳终止    void stop();  /**   * Schedule next ping in certain delay.   * @param  delayInMilliseconds delay in milliseconds.   */   // 触发下一次心跳    void schedule(long delayInMilliseconds);}
  1. init() 心跳初始化

720bb5035294eefc568ec34c9864cb71.png

在创建连接对象时会同时创建ClientComms, 在ClientComms的构造方法中,会调用TimerPingSender的init()方法对心跳发送器进行初始化操作
  public void init(ClientComms comms) {
        if (comms == null) {
          throw new IllegalArgumentException("ClientComms cannot be null.");    }    // 包装ClientComms对象    this.comms = comms;  }
    2.start()第一次心跳触发

客户端与服务端建立连接后,服务端会响应一个CONNACK类型的报文,所以在消息接收线程中,如果判断是这种类型的报文,会创建Timer并开始心跳

protected void notifyReceivedAck(MqttAck ack) throws MqttException {
        if (token == null) {
          ...    } else if (ack instanceof MqttPubRec) {
          ...    } else if (ack instanceof MqttPubAck || ack instanceof MqttPubComp) {
          ...    } else if (ack instanceof MqttPingResp) {
          ...    } else if (ack instanceof MqttConnack) {
          int rc = ((MqttConnack) ack).getReturnCode();      if (rc == 0) {
            synchronized (queueLock) {
              if (cleanSession) {
                clearState();            // Add the connect token back in so that users can be              // notified when connect completes.            tokenStore.saveToken(token,ack);          }          inFlightPubRels = 0;          actualInFlight = 0;          restoreInflightMessages();          // 开启PingSender          connected();        }      }       ...   }

db039312d08d3dff28e7064531b096ed.png

在start()方法中,会创建一个PingTask并将定时任务开启,定时任务的执行时间为初始化设置的keepAlive

19e2786764923f8a3612e3a61a7916fb.png

在PingTask
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值