基于MQTT协议的 org.eclipse.paho.client.mqttv3 源码学习(二)

本文深入解析了MQTT协议的工作原理,包括消息缓存机制、QoS级别处理方式以及客户端和服务端交互过程。详细介绍了消息如何在不同QoS级别下进行缓存与处理,并解释了消息在客户端和服务端之间的传递流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

http://blog.youkuaiyun.com/chenbifeng/article/details/25067761

们从通过MqttClientPersistent 这个接口读取缓存信息;
Qos 2 的PUBLISH消息和 PUBREL 存储到outboundqos2 中,Qos 1的消息存到 outboundqos1 中。消息通过messgeId 作为可以来缓存,
messgeId的范围是1-65535,所以当缓存的值超做这个,消息就会替换掉。
每次发送的时候,客户端读取 pendingMessages,pendingFlows这两个vertor中的数据,初始的消息存pendingMessages,PUBREL消息存储
到 pendingFlows中,消息发送完成后移除,但是outboundQoS2 outboundQoS1等队列中的消息会保留直到接收线程中收到消息回应。
如果Qos 1 移除持久化数据和 outboundqos1 数据
如果Qos 为2 的 PUBREC 则返回 PUBREL响应,更新持久化数据与outboundqos1中消息状态为 PUBREL
如果Qos 2 的PUBCOM 则移除持久化中数据和 outboundqos2中消息。
具体流程可以对比查看 ClientStatesend函数

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public void send(MqttWireMessage message, MqttToken token) throws MqttException {  
  2.         final String methodName = "send";  
  3.         if (message.isMessageIdRequired() && (message.getMessageId() == 0)) {  
  4.             message.setMessageId(getNextMessageId());  
  5.         }  
  6.         if (token != null ) {  
  7.             try {  
  8.                 token.internalTok.setMessageID(message.getMessageId());  
  9.             } catch (Exception e) {  
  10.             }  
  11.         }  
  12.               
  13.         if (message instanceof MqttPublish) {  
  14.             synchronized (queueLock) {  
  15.                 if (actualInFlight >= this.maxInflight) {  
  16.                     //@TRACE 613= sending {0} msgs at max inflight window  
  17.                     log.fine(className, methodName, "613"new Object[]{new Integer(actualInFlight)});  
  18.   
  19.                     throw new MqttException(MqttException.REASON_CODE_MAX_INFLIGHT);  
  20.                 }  
  21.                   
  22.                 MqttMessage innerMessage = ((MqttPublish) message).getMessage();  
  23.                 //@TRACE 628=pending publish key={0} qos={1} message={2}  
  24.                 log.fine(className,methodName,"628"new Object[]{new Integer(message.getMessageId()), new Integer(innerMessage.getQos()), message});  
  25.   
  26.                 switch(innerMessage.getQos()) {  
  27.                     case 2:  
  28.                         outboundQoS2.put(new Integer(message.getMessageId()), message);  
  29.                         persistence.put(getSendPersistenceKey(message), (MqttPublish) message);  
  30.                         break;  
  31.                     case 1:  
  32.                         outboundQoS1.put(new Integer(message.getMessageId()), message);  
  33.                         persistence.put(getSendPersistenceKey(message), (MqttPublish) message);  
  34.                         break;  
  35.                 }  
  36.                 tokenStore.saveToken(token, message);  
  37.                 pendingMessages.addElement(message);  
  38.                 queueLock.notifyAll();  
  39.             }  
  40.         } else {  
  41.             //@TRACE 615=pending send key={0} message {1}  
  42.             log.fine(className,methodName,"615"new Object[]{new Integer(message.getMessageId()), message});  
  43.               
  44.             if (message instanceof MqttConnect) {  
  45.                 synchronized (queueLock) {  
  46.                     // Add the connect action at the head of the pending queue ensuring it jumps  
  47.                     // ahead of any of other pending actions.  
  48.                     tokenStore.saveToken(token, message);  
  49.                     pendingFlows.insertElementAt(message,0);  
  50.                     queueLock.notifyAll();  
  51.                 }  
  52.             } else {  
  53.                 if (message instanceof MqttPingReq) {  
  54.                     this.pingCommand = message;  
  55.                 }  
  56.                 else if (message instanceof MqttPubRel) {  
  57.                     outboundQoS2.put(new Integer(message.getMessageId()), message);  
  58.                     persistence.put(getSendConfirmPersistenceKey(message), (MqttPubRel) message);  
  59.                 }  
  60.                 else if (message instanceof MqttPubComp)  {  
  61.                     persistence.remove(getReceivedPersistenceKey(message));  
  62.                 }  
  63.                   
  64.                 synchronized (queueLock) {  
  65.                     if ( !(message instanceof MqttAck )) {  
  66.                         tokenStore.saveToken(token, message);  
  67.                     }  
  68.                     pendingFlows.addElement(message);  
  69.                     queueLock.notifyAll();  
  70.                 }  
  71.             }  
  72.         }  
  73.     }  
接下来我们在来查看下CommsReceiver 接收端的代码

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public void run() {  
  2.         final String methodName = "run";  
  3.         MqttToken token = null;  
  4.   
  5.         while (running && (in != null)) {  //无限循环  
  6.             try {  
  7.                 // @TRACE 852=network read message  
  8.                 log.fine(className, methodName, "852");  
  9. //阻塞式读取消息  
  10.                 MqttWireMessage message = in.readMqttWireMessage();  
  11.                 log("Receiver 852 message:" + message.toString());  
  12.                 if (message instanceof MqttAck) {  
  13.                     token = tokenStore.getToken(message);  
  14.                     if (token != null) {  
  15.                         synchronized (token) {  
  16.                             // Ensure the notify processing is done under a lock  
  17.                             // on the token  
  18.                             // This ensures that the send processing can  
  19.                             // complete before the  
  20.                             // receive processing starts! ( request and ack and  
  21.                             // ack processing  
  22.                             // can occur before request processing is complete  
  23.                             // if not!  
  24.                             //通知回复确认消息  
  25.                             clientState.notifyReceivedAck((MqttAck) message);  
  26.                         }  
  27.                     } else {  
  28.                         // It its an ack and there is no token then something is  
  29.                         // not right.  
  30.                         // An ack should always have a token assoicated with it.  
  31.                         throw new MqttException(MqttException.REASON_CODE_UNEXPECTED_ERROR);  
  32.                     }  
  33.                 } else {  
  34.                     //通知有新的消息达到,我们进入此查看  
  35.                     // A new message has arrived  
  36.                     clientState.notifyReceivedMsg(message);  
  37.                 }  
  38.             } catch (MqttException ex) {  
  39.                 // @TRACE 856=Stopping, MQttException  
  40.                 log.fine(className, methodName, "856"null, ex);  
  41.   
  42.                 log("Receiver 856:exception->" + ex.toString());  
  43.                 running = false;  
  44.                 // Token maybe null but that is handled in shutdown  
  45.                 clientComms.shutdownConnection(token, ex);  
  46.             } catch (IOException ioe) {  
  47.                 // @TRACE 853=Stopping due to IOException  
  48.                 log.fine(className, methodName, "853");  
  49.   
  50.                 log("Receiver 853:exception->" + ioe.getLocalizedMessage());  
  51.                 log("Receiver 853:exception->" + ioe.toString());  
  52.                 running = false;  
  53.                 // An EOFException could be raised if the broker processes the  
  54.                 // DISCONNECT and ends the socket before we complete. As such,  
  55.                 // only shutdown the connection if we're not already shutting  
  56.                 // down.  
  57.                 if (!clientComms.isDisconnecting()) {  
  58.                     clientComms.shutdownConnection(token, new MqttException(  
  59.                             MqttException.REASON_CODE_CONNECTION_LOST, ioe));  
  60.                 } // else {  
  61.             }  
  62.         }  
  63.   
  64.         // @TRACE 854=<  
  65.         log.fine(className, methodName, "854");  
  66.     }  
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. 点击进入  clientState.notifyReceivedMsg(message)  
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. protected void notifyReceivedMsg(MqttWireMessage message) throws MqttException {  
  2.         final String methodName = "notifyReceivedMsg";  
  3.         this.lastInboundActivity = System.currentTimeMillis();  
  4.   
  5.         // @TRACE 651=received key={0} message={1}  
  6.         log.fine(className, methodName, "651"new Object[] {  
  7.                 new Integer(message.getMessageId()), message });  
  8.           
  9.         if (!quiescing) {  
  10.             if (message instanceof MqttPublish) {  
  11.                 MqttPublish send = (MqttPublish) message;  
  12.                 switch (send.getMessage().getQos()) {  
  13.                 case 0:  
  14.                 case 1:  
  15.                     if (callback != null) {  
  16.                         callback.messageArrived(send);  
  17.                     }  
  18.                     break;  
  19.                 case 2:  
  20.                     persistence.put(getReceivedPersistenceKey(message),  
  21.                             (MqttPublish) message);  
  22.                     inboundQoS2.put(new Integer(send.getMessageId()), send);  
  23.                     this.send(new MqttPubRec(send), null);  
  24.                 }  
  25.             } else if (message instanceof MqttPubRel) {  
  26.                 MqttPublish sendMsg = (MqttPublish) inboundQoS2  
  27.                         .get(new Integer(message.getMessageId()));  
  28.                 if (sendMsg != null) {  
  29.                     if (callback != null) {  
  30.                         callback.messageArrived(sendMsg);  
  31.                     }  
  32.                 } else {  
  33.                     // Original publish has already been delivered.  
  34.                     MqttPubComp pubComp = new MqttPubComp(message  
  35.                             .getMessageId());  
  36.                     this.send(pubComp, null);  
  37.                 }  
  38.             }  
  39.         }  
  40.     }  
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. 这里可以看出,如果是Qos 1的消息或者Qos 2 MqttPubRel,我们直接回调告诉消息已到达,点击进入 callback.messageArrived(sendMsg);     
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public void messageArrived(MqttPublish sendMessage) {  
  2.         final String methodName = "messageArrived";  
  3.         if (mqttCallback != null) {  
  4.             // If we already have enough messages queued up in memory, wait  
  5.             // until some more queue space becomes available. This helps  
  6.             // the client protect itself from getting flooded by messages  
  7.             // from the server.  
  8.             synchronized (spaceAvailable) {  
  9.                 if (!quiescing && messageQueue.size() >= INBOUND_QUEUE_SIZE) {  
  10.                     try {  
  11.                         // @TRACE 709=wait for spaceAvailable  
  12.                         log.fine(className, methodName, "709");  
  13.                         spaceAvailable.wait();  
  14.                     } catch (InterruptedException ex) {  
  15.                     }  
  16.                 }  
  17.             }  
  18.             if (!quiescing) {  
  19.                 messageQueue.addElement(sendMessage);  
  20.                 // Notify the CommsCallback thread that there's work to do...  
  21.                 synchronized (workAvailable) {  
  22.                     // @TRACE 710=new msg avail, notify workAvailable  
  23.                     log.fine(className, methodName, "710");  
  24.                     workAvailable.notifyAll();  
  25.                 }  
  26.             }  
  27.         }  
  28.     }  
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. 所做的操作就是将数据插入到 消息队列中,然后唤醒 workAvailable 这个锁,在 CommsCallback类中所有这个锁对应的地方,可以查看到  
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public void run() {  
  2.     final String methodName = "run";  
  3.     while (running) {  
  4.         try {  
  5.             // If no work is currently available, then wait until there is  
  6.             // some...  
  7.             try {  
  8.                 synchronized (workAvailable) {  
  9.                     if (running & messageQueue.isEmpty() && completeQueue.isEmpty()) {  
  10.                         // @TRACE 704=wait for workAvailable  
  11.                         log.fine(className, methodName, "704");  
  12.                         workAvailable.wait();  
  13.                     }  
  14.                 }  
  15.             } catch (InterruptedException e) {  
  16.             }  
  17.   
  18.             if (running) {  
  19.                 // Check for deliveryComplete callbacks...  
  20.                 if (!completeQueue.isEmpty()) {  
  21.                     // First call the delivery arrived callback if needed  
  22.                     MqttToken token = (MqttToken) completeQueue.elementAt(0);  
  23.                     handleActionComplete(token);  
  24.                     completeQueue.removeElementAt(0);  
  25.                 }  
  26.   
  27.                 // Check for messageArrived callbacks...  
  28.                 if (!messageQueue.isEmpty()) {  
  29.                     // Note, there is a window on connect where a publish  
  30.                     // could arrive before we've  
  31.                     // finished the connect logic.  
  32.                     MqttPublish message = (MqttPublish) messageQueue.elementAt(0);  
  33.   
  34.                     handleMessage(message);  
  35.                     messageQueue.removeElementAt(0);  
  36.                 }  
  37.             }  
  38.   
  39.             if (quiescing) {  
  40.                 clientState.checkQuiesceLock();  
  41.             }  
  42.   
  43.             synchronized (spaceAvailable) {  
  44.                 // Notify the spaceAvailable lock, to say that there's now  
  45.                 // some space on the queue...  
  46.   
  47.                 // @TRACE 706=notify spaceAvailable  
  48.                 log.fine(className, methodName, "706");  
  49.                 spaceAvailable.notifyAll();  
  50.             }  
  51.         } catch (Throwable ex) {  
  52.             // Users code could throw an Error or Exception e.g. in the case  
  53.             // of class NoClassDefFoundError  
  54.             // @TRACE 714=callback threw exception  
  55.             log.fine(className, methodName, "714"null, ex);  
  56.             running = false;  
  57.             clientComms.shutdownConnection(nullnew MqttException(ex));  
  58.         }  
  59.     }  
  60. }  


程序通过handleActionComplete(token);handleMessage(message);通知用户发布一个消息完成和 有新的订阅消息到达。

三、总结
ClientState 类中, pendingMessages容器存放MqttPubish消息,而pendingFlows消息则存放 MqttPubRel,MqttConnect,MqttPingReq,MqttAck等
Send 方法将消息放入到容器中,同时唤醒等待发送的线程。
 
CommsSender 这个发送线程 通过 ClientStatele类get() 方法等待 pendingMessages和pendingFlows 这个这两个队列中放入消息,
如果有消息放入,且同时被唤醒,那么就执行消息发送操作。
 
CommsSender 接收线程中阻塞式获取消息根据不通的消息类型已经Qos level,通过CommsCallback及ClientStatele中notifyReceivedMsg 来执行相应的操作。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值