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 ...
随机推荐
- P4388 付公主的矩形(gcd+欧拉函数)
P4388 付公主的矩形 前置芝士 \(gcd\)与欧拉函数 要求对其应用于性质比较熟,否则建议左转百度 思路 有\(n×m\)的矩阵,题目要求对角线经过的格子有\(N\)个, 设函数\(f(x,y) ...
- Tornado--基于H5图片的上传
日记 好久没有分享过东西,一直在学习状态,学的并不好很多东西都没有,也写了很多demo,后续整理出来在分享,就不分享了,不为什么因为今天周六,好不容易双休,大早上的一个人醒来,刷刷知乎,听音乐.分享一 ...
- spring项目命名
groupId 一般分为多个段,最简单的分两段,第一段为域,第二段为公司名称.域又分为org.com.cn等等许多, 举个apache公司的tomcat项目例子:这个项目的groupId是org.ap ...
- curl 监控web
[root@rhel6 ~]# curl -I -s -w "%{http_code}\n" -o /dev/null http://127.0.0.1 [root@rhel6 ~ ...
- java入门了解15
1.批处理文件(bat) 简单的说,批处理的作用就是自动的连续执行多条命令 .编写bat处理文件可以使用记事本的方式: 常见批处理文件的命令: echo 表示显示此命令后的字符 tiltle 设置窗口 ...
- angularjs $http提交数据探索
前两天在搞自己的项目,前端js框架用的是angularjs框架:网站整的差不多的时候出事了:那就是当我用$http.post()方法向服务器提交一些数据的时候:后台总是接收不到数据: 于是采用了其他方 ...
- 关于ATML信号定义的理解-1
1.XML中的类型标签: <xs:complexType>复合类型和<xs:simpleTyle>简单类型是数据结构类型,包含了各种类型的属性.可以被子类型继承,继承方式为&l ...
- stl_multimap.h
stl_multimap.h // Filename: stl_multimap.h // Comment By: 凝霜 // E-mail: mdl2009@vip.qq.com // Blog: ...
- bzoj3595 方伯伯的oj
有$n$个数,一开始是$1~n$,有$m$次操作 1.把编号为$x$的人编号改为$y$,保证$y$没出现过 2.把编号为$x$的人提到第一名 3.把编号为$x$的人怼到最后一名 4.查询排名为$x$的 ...
- NOIP2018爆炸记
又是一年\(NOIP\),可能是梦结束的地方? 之所以咕了这么久是得先确定自己不会退役,因为分太低了. 和去年一样在学校门前照了相,然后上车走了.高三回来考的只剩下\(p2oileen\)学姐了.新一 ...