MQTT协议实现Eclipse Paho学习总结二
一、概述
前一篇博客(MQTT协议实现Eclipse Paho学习总结一) 写了一些MQTT协议相关的一些概述和其实现Eclipse Paho的报文类别,同时对心跳包进行了分析。这篇文章,在不涉及MQTT逻辑实现的基础之上分析一下Eclipse Paho中Socket通信的实现,这里我们主要阐述其采用Java同步技术将同步的Socket通信异步化的过程。
二、上菜
2.1 CommsSender
- public void run() {
- final String methodName = "run";
- MqttWireMessage message = null;
- while (running && (out != null)) {//超级无限循环
- try {
- message = clientState.get();//主要看这里获取message时进行了阻塞,即clientState.get()方法没有获得消息的时候,代码一直处理阻塞状态,不会一直无限循环!
- if (message != null) {
- //@TRACE 802=network send key={0} msg={1}
- log.fine(className,methodName,"802", new Object[] {message.getKey(),message});
- if (message instanceof MqttAck) {
- out.write(message);
- out.flush();
- } else {
- MqttToken token = tokenStore.getToken(message);
- // While quiescing the tokenstore can be cleared so need
- // to check for null for the case where clear occurs
- // while trying to send a message.
- if (token != null) {
- synchronized (token) {//使用了同步,防止一次性多个写操作。
- out.write(message);
- out.flush();
- clientState.notifySent(message);//通知已经发送了一个消息
- }
- }
- }
- } else { // null message
- //@TRACE 803=get message returned null, stopping}
- log.fine(className,methodName,"803");
- running = false;
- }
- } catch (MqttException me) {
- handleRunException(message, me);
- } catch (Exception ex) {
- handleRunException(message, ex);
- }
- } // end while
- //@TRACE 805=<
- log.fine(className, methodName,"805");
- }
- synchronized (queueLock) {
- while (result == null) {
- if (pendingMessages.isEmpty() && pendingFlows.isEmpty()) {
- try {
- long ttw = getTimeUntilPing();
- //@TRACE 644=nothing to send, wait for {0} ms
- log.fine(className,methodName, "644", new Object[] {new Long(ttw)});
- queueLock.wait(getTimeUntilPing());//如果pendingMessages队列和pendingFlows队列为空,则放弃queueLock锁,等待,而这个等待时间是有限的,如果长时间没有发送消息,同时等待的时间超过了心跳包发送的时间,那么就往下执行,根据实际情况发送心跳包或者消息。
- } catch (InterruptedException 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();//通知get方法,我已经有消息放入队列了!!!
- }
总 的过程如下:send方法将消息放入到pendingMessages队列和pendingFlows当中同时发送消息唤醒等待中的线程,get等待 pendingMessages队列和pendingFlows中的消息,同时等待唤醒,如果有消息放入,同时被唤醒,那么就执行发送消息的操作。这个过 程是不是跟操作系统当中的生产者-消费者的关系一样呢!!!
2.2 CommsReceiver
- 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();// 这里,因为socket.getInputStream()一直在阻塞,如果没有消息是读不到message的,因此在这里的while循环也没有无限制 的运行下去,只有在有消息的时候才往下走。socket默认是阻塞的,就是在读的时候如果读不到资源就会一直等待,直到超时(如果设置了超时时间的话), 如果服务端和客户端都在读的话而没有写的话就会一直阻塞。你可以使用SocketChannel,设置socket的通道,使其变成非阻塞的。
- 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);
- 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");
- 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);方法,部分代码如下:
- if (message instanceof MqttPublish) {
- MqttPublish send = (MqttPublish) message;
- switch (send.getMessage().getQos()) {
- case 0:
- case 1:
- if (callback != null) {
- callback.messageArrived(send);
- }
- break;
我们点击进入callback.messageArrived(send);方法,
- 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();
- }
- }
- }
- }
在 这里,同样使用了生产者-消费者模式,在run方法里,我们可以看到其调用了handleMessage,在这个方法里面调用了 mqttCallback.messageArrived(destName, publishMessage.getMessage());接口回调。
MQTT协议实现Eclipse Paho学习总结二的更多相关文章
- MQTT协议实现Eclipse Paho学习总结
MQTT协议实现Eclipse Paho学习总结 摘自:https://www.cnblogs.com/yfliufei/p/4383852.html 2015-04-01 14:57 by 辣椒酱, ...
- MQTT协议及推送服务(二)
MQTT简介 MQTT全称叫做Message Queuing Telemetry Transport,意为消息队列遥测传输,是IBM开发的一个即时通讯协议.由于其维护一个长连接以轻量级低消耗著称,所以 ...
- [3] MQTT,mosquitto,Eclipse Paho---怎样使用 Eclipse Paho MQTT工具来发送订阅MQTT消息?
在上两节,笔者主要介绍了 MQTT,mosquitto,Eclipse Paho的基本概念已经怎样安装mosquitto. 在这个章节我们就来看看怎样用 Eclipse Paho MQTT工具来发送接 ...
- mqtt协议实现 java服务端推送功能(二)java demo测试
上一篇写了安装mosQuitto和测试,但是用cmd命令很麻烦,有没有一个可视化软件呢? 有,需要在google浏览器下载一个叫MQTTLens的插件 打开MQTTLens后界面如下: 打开conne ...
- 基于RabbitMQ的MQTT协议及应用
MQTT的开源代码地址先贴在这里:https://github.com/mqtt/mqtt.github.io/wiki/servers MQTT定义: MQTT(Message Queuing Te ...
- 采用MQTT协议实现android消息推送(2)MQTT服务端与客户端软件对比、android客户端示列表
1.服务端软件对比 https://github.com/mqtt/mqtt.github.io/wiki/servers 名称(点名进官网) 特性 简介 收费 支持的客户端语言 IBM MQ 完整的 ...
- MQTT协议通俗讲解
参考 Reference v3.1.1 英文原版 中文翻译版 其他资源 网站 MQTT官方主页 Eclipse Paho 项目主页 测试工具 MQTT Spy(基于JDK) Chrome插件 MQTT ...
- MQTT 协议学习:000-有关概念入门
背景 从本章开始,在没有特殊说明的情况下,文章中的MQTT版本均为 3.1.1. MQTT 协议是物联网中常见的协议之一,"轻量级物联网消息推送协议",MQTT同HTTP属于第七层 ...
- MQTT 协议学习: 总结 与 各种定义的速查表
背景 经过几天的学习与实操,对于MQTT(主要针对 v3.1.1版本)的学习告一段落,为了方便日后的查阅 本文链接:<MQTT 协议学习: 总结 与 各种定义的速查表> 章节整理 MQTT ...
随机推荐
- 第二章 python中重要的数据结构(下)
二.元组(tuple):不可变序列 跟list一样,也是一种序列,唯一不同的是,元组元素不能被修改,通常用(, ,)表示元组,也可以不加括号. #创建元组 >>> 1,2,3 (1, ...
- 小学生都能看懂的数位dp
前言 数位dp其实很久前就知道了,也做过几道和其他算法混在一起的题目,其实通过手玩是能做的 但毕竟是种算法,还是系统学下比较好(节省手玩时间) 模板题 P2602 [ZJOI2010]数字计数 化简题 ...
- shell单例-处理方案
shell单例:当某一个shell脚本需要重复执行时(shell定时任务 etc),为了避免多个相同任务之间交叉,造成数据的混乱或者错误,需要脚本单例执行. 就是前一个进程执行时,后一个进程需要阻塞等 ...
- linux常用端口
linux常用端口:已有的比较好的博客! 本人常用端口备份: 50070 hdfs hdfs集群监控端口 8088 yarn yarn集群监控端口 8983 solr solr服务查询端口60010 ...
- grep egrep
grep: Global search REgular expression and Print out the line. 作用: 文本搜索工具,根据用户指定的“模式”对目标文本逐行进行匹配检查:打 ...
- poj 1028 Web Navigation 【模拟题】
题目地址:http://poj.org/problem?id=1028 测试样例: Sample Input VISIT http://acm.ashland.edu/ VISIT http://ac ...
- 【JavaScript学习整理】DOM对象(location history screen navigator)
DOM: 描述网页各个组成部分之间的关系. parentNode: 父节点 childNode: 子节点 firstChild: 第一个子节点 lastChild: 最后一个子节点 nextSibli ...
- ML二(决策树学习)
决策树学习 Decision Tree Learning 1 基本概念 属性(attribute):树上的每个结点说明了对实例的某个属性的测试,该结点的每一个后继分支对应该属性的一个可能值. 熵(en ...
- Java -- 表达式类型的自动提升
1. 提升规则: a. 所有byte型,short型和char型将被提升到int型. b. 整个算数表达式的数据类型自动提升到与表达式中最高等级操作数同样的类型. 例1: short val = 5; ...
- 翻译Lanlet2
Here is more information on the basic primitives that make up a Lanelet2 map. Read here for a primer ...