Paho是什么?
Paho是Eclipse IoT开源项目的一个子项目。Paho项目提供了开源可靠的开放标准消息协议的实现,目标是为机器到机器和物联网提供新的、存在的和新兴的应用程序。
Paho是一个开源的MQTT客户端SDK,准确来说是一组,它包含各种语言的实现版本,比如C、Java、Python、javascript、golang等,其中paho.mqtt.java是Java语言的实现版本,这个就是本文的主角。
Paho怎么用?
Paho的API设计的非常清晰简洁。MQTT的发布者和订阅者都是客户端,所以首先要创建一个客户端,然后连接到代理服务器,接下来就可以订阅或者发布消息了。
导入paho包,以maven工程为例子,在pom.xml文件加入下面的依赖
<dependency>
<groupId>org.eclipse.paho</groupId>
<artifactId>org.eclipse.paho.client.mqttv3</artifactId>
<version>1.2.5</version>
</dependency>
创建客户端
// 代理服务器地址(服务端)
String broker = "tcp://127.0.0.1:1883";
String clientId = "mqtt-client-1";
MemoryPersistence persistence = new MemoryPersistence();
MqttClient client = new MqttClient(broker, clientId, persistence);
client.setCallback(new MqttCallback() {
public void connectionLost(Throwable throwable) {
}
public void messageArrived(String s, MqttMessage mqttMessage) throws Exception {
System.out.println("receive message:" + s + " " + new String(mqttMessage.getPayload()));
}
public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {
}
});
连接代理服务器
MqttConnectOptions options = new MqttConnectOptions();
options.setUserName("admin");
options.setPassword("123456".toCharArray());
client.connect(options);
发布消息
String content = "hello mqtt";
MqttMessage mqttMessage = new MqttMessage();
mqttMessage.setPayload(content.getBytes());
client.publish("jack", mqttMessage);
订阅消息
String topic = "testtopic";
client.subscribe(topic);
当有'testtopic'主题的消息到达代理服务器后,代理服务器会转发消息给本客户端,最终表现为回调MqttCallback.messageArrived(String topic, MqttMessage message)方法。
Paho设计原理
通过socket实现的通讯,会涉及到从InputStream读取数据和向OutputStream写入数据,实现这样的双向通讯需要使用到多线程技术,还需要考虑CPU资源的合理化利用、生产者消费者等问题。
Paho用了四个线程来实现这个通讯过程,读和写分别使用两个线程。读和写各使用一个消息队列来存储收发的消息,用于消息缓存和中转,同时使用Java的Object的wait()/notify()来阻塞/唤醒线程,解决生产者消费者问题和CPU资源浪费问题。
协议实现主要集中在org.eclipse.paho.client.mqttv3.internal.wire
包,协议一共定义了15种类型的消息,使用面向对象编程,经过抽象封装,MqttWireMessage
是消息的基类。
1=Invalid protocol version
2=Invalid client ID
3=Broker unavailable
4=Bad user name or password
5=Not authorized to connect
Paho打印的信息支持多语言。多语言的Message是使用Java的ResourceBundle实现的。
API。库提供给外部使用的接口。
从源代码角度看Paho
源代码的解读,关注网络通讯模块,也就是对MqttClient.connect()做一下展开。
org\eclipse\paho\client\mqttv3\MqttClient.java
public void connect(MqttConnectOptions options) throws MqttSecurityException, MqttException {
aClient.connect(options, null, null).waitForCompletion(getTimeToWait());
}
然后类之间一路调用到ClientComms类,这个类是一个工具类,用来处理客户端和服务端的通讯。
org\eclipse\paho\client\mqttv3\internal\ClientComms.java
public void connect(MqttConnectOptions options, MqttToken token) throws MqttException {
final String methodName = "connect";
synchronized (conLock) {
if (isDisconnected() && !closePending) {
... ...
ConnectBG conbg = new ConnectBG(this, token, connect, executorService);
// 启动ConnectBG线程,即执行ConnectBG.run()方法
conbg.start();
}
... ...
}
}
private class ConnectBG implements Runnable {
public void run() {
try {
... ...
// 网络通讯的关键流程就在这里
NetworkModule networkModule = networkModules[networkModuleIndex];
// 1 创建socket并连接server
networkModule.start();
receiver = new CommsReceiver(clientComms, clientState, tokenStore, networkModule.getInputStream());
// 2 启动消息接收线程,不断从socket的inputstream读取server发来的数据
receiver.start("MQTT Rec: "+getClient().getClientId(), executorService);
sender = new CommsSender(clientComms, clientState, tokenStore, networkModule.getOutputStream());
// 3 启动消息发送线程,不断从vector读取用户publis的消息,写入到socket的outputstream
sender.start("MQTT Snd: "+getClient().getClientId(), executorService);
// 4 启动callback线程,不断从vector读取消息接收线程写入的数据,回调用户代码
callback.start("MQTT Call: "+getClient().getClientId(), executorService);
internalSend(conPacket, conToken);
}
... ...
}
}
小结
Paho是一个很小的开源项目,java文件只有96个,目标就是实现MqTT协议并封装接口提供给用户使用。项目很小,所以门槛就比较低,都能看的懂。功能虽然简单,但是很多共性的问题都会处理到,比如经典的生产者消费者问题、同步异步问题、多线程问题等,阅读源码的时候会有一定启发。另外,项目是eclipse大厂出品,可以学习大厂的代码设计。