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函数
- public void send(MqttWireMessage message, MqttToken token) throws MqttException {
- final String methodName = "send";
- if (message.isMessageIdRequired() && (message.getMessageId() == 0)) {
- message.setMessageId(getNextMessageId());
- }
- if (token != null ) {
- try {
- token.internalTok.setMessageID(message.getMessageId());
- } catch (Exception e) {
- }
- }
- if (message instanceof MqttPublish) {
- synchronized (queueLock) {
- if (actualInFlight >= this.maxInflight) {
- //@TRACE 613= sending {0} msgs at max inflight window
- log.fine(className, methodName, "613", new Object[]{new Integer(actualInFlight)});
- throw new MqttException(MqttException.REASON_CODE_MAX_INFLIGHT);
- }
- MqttMessage innerMessage = ((MqttPublish) message).getMessage();
- //@TRACE 628=pending publish key={0} qos={1} message={2}
- log.fine(className,methodName,"628", new Object[]{new Integer(message.getMessageId()), new Integer(innerMessage.getQos()), message});
- switch(innerMessage.getQos()) {
- case 2:
- outboundQoS2.put(new Integer(message.getMessageId()), message);
- persistence.put(getSendPersistenceKey(message), (MqttPublish) message);
- break;
- case 1:
- outboundQoS1.put(new Integer(message.getMessageId()), message);
- persistence.put(getSendPersistenceKey(message), (MqttPublish) message);
- break;
- }
- tokenStore.saveToken(token, message);
- pendingMessages.addElement(message);
- queueLock.notifyAll();
- }
- } else {
- //@TRACE 615=pending send key={0} message {1}
- log.fine(className,methodName,"615", new Object[]{new Integer(message.getMessageId()), message});
- if (message instanceof MqttConnect) {
- synchronized (queueLock) {
- // Add the connect action at the head of the pending queue ensuring it jumps
- // ahead of any of other pending actions.
- tokenStore.saveToken(token, message);
- pendingFlows.insertElementAt(message,0);
- queueLock.notifyAll();
- }
- } else {
- if (message instanceof MqttPingReq) {
- this.pingCommand = message;
- }
- else if (message instanceof MqttPubRel) {
- outboundQoS2.put(new Integer(message.getMessageId()), message);
- persistence.put(getSendConfirmPersistenceKey(message), (MqttPubRel) message);
- }
- else if (message instanceof MqttPubComp) {
- persistence.remove(getReceivedPersistenceKey(message));
- }
- synchronized (queueLock) {
- if ( !(message instanceof MqttAck )) {
- tokenStore.saveToken(token, message);
- }
- pendingFlows.addElement(message);
- queueLock.notifyAll();
- }
- }
- }
- }
- public void run() {
- final String methodName = "run";
- MqttToken token = null;
- while (running && (in != null)) { //无限循环
- try {
- // @TRACE 852=network read message
- log.fine(className, methodName, "852");
- //阻塞式读取消息
- MqttWireMessage message = in.readMqttWireMessage();
- log("Receiver 852 message:" + message.toString());
- if (message instanceof MqttAck) {
- token = tokenStore.getToken(message);
- if (token != null) {
- synchronized (token) {
- // Ensure the notify processing is done under a lock
- // on the token
- // This ensures that the send processing can
- // complete before the
- // receive processing starts! ( request and ack and
- // ack processing
- // can occur before request processing is complete
- // if not!
- //通知回复确认消息
- clientState.notifyReceivedAck((MqttAck) message);
- }
- } else {
- // It its an ack and there is no token then something is
- // not right.
- // An ack should always have a token assoicated with it.
- throw new MqttException(MqttException.REASON_CODE_UNEXPECTED_ERROR);
- }
- } else {
- //通知有新的消息达到,我们进入此查看
- // A new message has arrived
- clientState.notifyReceivedMsg(message);
- }
- } catch (MqttException ex) {
- // @TRACE 856=Stopping, MQttException
- log.fine(className, methodName, "856", null, ex);
- log("Receiver 856:exception->" + ex.toString());
- running = false;
- // Token maybe null but that is handled in shutdown
- clientComms.shutdownConnection(token, ex);
- } catch (IOException ioe) {
- // @TRACE 853=Stopping due to IOException
- log.fine(className, methodName, "853");
- log("Receiver 853:exception->" + ioe.getLocalizedMessage());
- log("Receiver 853:exception->" + ioe.toString());
- running = false;
- // An EOFException could be raised if the broker processes the
- // DISCONNECT and ends the socket before we complete. As such,
- // only shutdown the connection if we're not already shutting
- // down.
- if (!clientComms.isDisconnecting()) {
- clientComms.shutdownConnection(token, new MqttException(
- MqttException.REASON_CODE_CONNECTION_LOST, ioe));
- } // else {
- }
- }
- // @TRACE 854=<
- log.fine(className, methodName, "854");
- }
- 点击进入 clientState.notifyReceivedMsg(message)
- protected void notifyReceivedMsg(MqttWireMessage message) throws MqttException {
- final String methodName = "notifyReceivedMsg";
- this.lastInboundActivity = System.currentTimeMillis();
- // @TRACE 651=received key={0} message={1}
- log.fine(className, methodName, "651", new Object[] {
- new Integer(message.getMessageId()), message });
- if (!quiescing) {
- if (message instanceof MqttPublish) {
- MqttPublish send = (MqttPublish) message;
- switch (send.getMessage().getQos()) {
- case 0:
- case 1:
- if (callback != null) {
- callback.messageArrived(send);
- }
- break;
- case 2:
- persistence.put(getReceivedPersistenceKey(message),
- (MqttPublish) message);
- inboundQoS2.put(new Integer(send.getMessageId()), send);
- this.send(new MqttPubRec(send), null);
- }
- } else if (message instanceof MqttPubRel) {
- MqttPublish sendMsg = (MqttPublish) inboundQoS2
- .get(new Integer(message.getMessageId()));
- if (sendMsg != null) {
- if (callback != null) {
- callback.messageArrived(sendMsg);
- }
- } else {
- // Original publish has already been delivered.
- MqttPubComp pubComp = new MqttPubComp(message
- .getMessageId());
- this.send(pubComp, null);
- }
- }
- }
- }
- 这里可以看出,如果是Qos 1的消息或者Qos 2 MqttPubRel,我们直接回调告诉消息已到达,点击进入 callback.messageArrived(sendMsg);
- public void messageArrived(MqttPublish sendMessage) {
- final String methodName = "messageArrived";
- if (mqttCallback != null) {
- // If we already have enough messages queued up in memory, wait
- // until some more queue space becomes available. This helps
- // the client protect itself from getting flooded by messages
- // from the server.
- synchronized (spaceAvailable) {
- if (!quiescing && messageQueue.size() >= INBOUND_QUEUE_SIZE) {
- try {
- // @TRACE 709=wait for spaceAvailable
- log.fine(className, methodName, "709");
- spaceAvailable.wait();
- } catch (InterruptedException ex) {
- }
- }
- }
- if (!quiescing) {
- messageQueue.addElement(sendMessage);
- // Notify the CommsCallback thread that there's work to do...
- synchronized (workAvailable) {
- // @TRACE 710=new msg avail, notify workAvailable
- log.fine(className, methodName, "710");
- workAvailable.notifyAll();
- }
- }
- }
- }
- 所做的操作就是将数据插入到 消息队列中,然后唤醒 workAvailable 这个锁,在 CommsCallback类中所有这个锁对应的地方,可以查看到
- public void run() {
- final String methodName = "run";
- while (running) {
- try {
- // If no work is currently available, then wait until there is
- // some...
- try {
- synchronized (workAvailable) {
- if (running & messageQueue.isEmpty() && completeQueue.isEmpty()) {
- // @TRACE 704=wait for workAvailable
- log.fine(className, methodName, "704");
- workAvailable.wait();
- }
- }
- } catch (InterruptedException e) {
- }
- if (running) {
- // Check for deliveryComplete callbacks...
- if (!completeQueue.isEmpty()) {
- // First call the delivery arrived callback if needed
- MqttToken token = (MqttToken) completeQueue.elementAt(0);
- handleActionComplete(token);
- completeQueue.removeElementAt(0);
- }
- // Check for messageArrived callbacks...
- if (!messageQueue.isEmpty()) {
- // Note, there is a window on connect where a publish
- // could arrive before we've
- // finished the connect logic.
- MqttPublish message = (MqttPublish) messageQueue.elementAt(0);
- handleMessage(message);
- messageQueue.removeElementAt(0);
- }
- }
- if (quiescing) {
- clientState.checkQuiesceLock();
- }
- synchronized (spaceAvailable) {
- // Notify the spaceAvailable lock, to say that there's now
- // some space on the queue...
- // @TRACE 706=notify spaceAvailable
- log.fine(className, methodName, "706");
- spaceAvailable.notifyAll();
- }
- } catch (Throwable ex) {
- // Users code could throw an Error or Exception e.g. in the case
- // of class NoClassDefFoundError
- // @TRACE 714=callback threw exception
- log.fine(className, methodName, "714", null, ex);
- running = false;
- clientComms.shutdownConnection(null, new MqttException(ex));
- }
- }
- }
三、总结
ClientState 类中, pendingMessages容器存放MqttPubish消息,而pendingFlows消息则存放 MqttPubRel,MqttConnect,MqttPingReq,MqttAck等
Send 方法将消息放入到容器中,同时唤醒等待发送的线程。
CommsSender 这个发送线程 通过 ClientStatele类get() 方法等待 pendingMessages和pendingFlows 这个这两个队列中放入消息,
如果有消息放入,且同时被唤醒,那么就执行消息发送操作。
CommsSender 接收线程中阻塞式获取消息根据不通的消息类型已经Qos level,通过CommsCallback及ClientStatele中notifyReceivedMsg 来执行相应的操作。