实践目标
- Android 客户端使用
- Java 客户端使用
Android 客户端
代码仓库 github.com/eclipse/pah…
工程改造
-
Gradle 及 Grade Plugin 版本
-
build.gradle
- compile 变更为 implementation
- 精简依赖
- 如果Gradle版本>=7.0, 修改工程仓库配置方式
-
主页面
-
采用 PahoExampleActivity.java 做为主页面
-
配置自己的MQTT信息
//服务器地址 final String serverUri="tcp://192.168.71.99:1883"; //客户端名称 String clientId = "ExampleAndroidClient"; //订阅主题名称 final String subscriptionTopic = "harvey"; //发布主题名称 final String publishTopic = "harvey"; //发布消息内容 final String publishMessage = "Message from MQTTHarvey's Android Client"; 复制代码 -
精简PahoExampleActivity.java布局
仅保留history_recycler_view和fab -
修改消息更新列表
private void addToHistory(String mainText){ System.out.println("LOG: " + mainText); mAdapter.add(mainText); mRecyclerView.post(new Runnable() { @Override public void run() { mAdapter.notifyDataSetChanged(); mRecyclerView.getLayoutManager().scrollToPosition(mAdapter.getItemCount()-1); Snackbar.make(findViewById(android.R.id.content), mainText, Snackbar.LENGTH_LONG) .setAction("Action", null).show(); } }); } 复制代码 -
Java 客户端
代码仓库github.com/eclipse/pah…
工程改造
-
创建MQTT Java工程
- 依赖
org.eclipse.paho.mqttv5源码 - 将mqttv5源码中的resource内容一并引入工程中
- 依赖
-
创建测试包名 com.harvey.mqtt,添加如下两个文件(ExampleConnect.java, MQTTConfigue.java)
package com.harvey.mqtt;
import org.eclipse.paho.mqttv5.client.*;
import org.eclipse.paho.mqttv5.client.logging.HarveyDebug;
import org.eclipse.paho.mqttv5.client.persist.MemoryPersistence;
import org.eclipse.paho.mqttv5.common.MqttException;
import org.eclipse.paho.mqttv5.common.MqttMessage;
import org.eclipse.paho.mqttv5.common.MqttSubscription;
import org.eclipse.paho.mqttv5.common.packet.MqttProperties;
import java.nio.charset.StandardCharsets;
public class ExampleConnect {
public static void main(String[] args) {
//1. 测试连接
// connect();
//2. 测试连接及断开
// connectAndDisconnect();
//3. 测试连接及Ping
// connectAndPing();
//4. 测试订阅及解除订阅
subscribeAndUnsubscribe();
}
/**
* 连接MQTT服务器
*/
private static void connect(){
connect("");
}
private static void connect(String id){
MemoryPersistence persistence = new MemoryPersistence();
try {
MqttConnectionOptions connOpts = new MqttConnectionOptions();
connOpts.setKeepAliveInterval(0);
MqttAsyncClient sampleClient = new MqttAsyncClient(MQTTConfigue.broker, MQTTConfigue.clientId+id, persistence);
IMqttToken token = sampleClient.connect(connOpts);
token.waitForCompletion();
} catch(MqttException me) {
System.out.println("reason "+me.getReasonCode());
System.out.println("msg "+me.getMessage());
System.out.println("loc "+me.getLocalizedMessage());
System.out.println("cause "+me.getCause());
System.out.println("excep "+me);
me.printStackTrace();
}
}
/**
* 连接 -> 断开
*/
private static void connectAndDisconnect(){
MemoryPersistence persistence = new MemoryPersistence();
try {
MqttConnectionOptions connOpts = new MqttConnectionOptions();
connOpts.setKeepAliveInterval(0);
MqttAsyncClient sampleClient = new MqttAsyncClient(MQTTConfigue.broker, MQTTConfigue.clientId, persistence);
IMqttToken token = sampleClient.connect(connOpts);
token.waitForCompletion();
sampleClient.disconnect();
token.waitForCompletion();
} catch(MqttException me) {
System.out.println("reason "+me.getReasonCode());
System.out.println("msg "+me.getMessage());
System.out.println("loc "+me.getLocalizedMessage());
System.out.println("cause "+me.getCause());
System.out.println("excep "+me);
me.printStackTrace();
}
}
/**
* 连接 -> ping
*/
private static void connectAndPing(){
MemoryPersistence persistence = new MemoryPersistence();
try {
MqttConnectionOptions connOpts = new MqttConnectionOptions();
connOpts.setKeepAliveInterval(10);
MqttAsyncClient sampleClient = new MqttAsyncClient(MQTTConfigue.broker, MQTTConfigue.clientId, persistence);
IMqttToken token = sampleClient.connect(connOpts);
token.waitForCompletion();
} catch(MqttException me) {
System.out.println("reason "+me.getReasonCode());
System.out.println("msg "+me.getMessage());
System.out.println("loc "+me.getLocalizedMessage());
System.out.println("cause "+me.getCause());
System.out.println("excep "+me);
me.printStackTrace();
}
}
/**
*
* 订阅主题 & 解除订阅
*
*/
private static void subscribeAndUnsubscribe(){
MemoryPersistence persistence = new MemoryPersistence();
try {
MqttConnectionOptions connOpts = new MqttConnectionOptions();
connOpts.setKeepAliveInterval(0);
MqttAsyncClient sampleClient = new MqttAsyncClient(MQTTConfigue.broker, MQTTConfigue.clientId, persistence);
IMqttToken token = sampleClient.connect(connOpts);
token.waitForCompletion();
MqttSubscription subscription2 = new MqttSubscription(MQTTConfigue.topic+"C");
MqttSubscription subscription = new MqttSubscription(MQTTConfigue.topic);
MqttProperties mqttProperties = new MqttProperties();
mqttProperties.setPayloadFormat(true);
sampleClient.subscribe(new MqttSubscription[]{subscription}, null, new MqttActionListener() {
@Override
public void onSuccess(IMqttToken asyncActionToken) {
try {
if(asyncActionToken.getTopics()[0].equals(MQTTConfigue.topic))
sampleClient.unsubscribe(MQTTConfigue.topic);
for (String topic : asyncActionToken.getTopics()){
if(topic.equals(MQTTConfigue.topic)){
sampleClient.unsubscribe(MQTTConfigue.topic);
} else if(topic.equals(MQTTConfigue.topic+"C")){
sampleClient.unsubscribe(MQTTConfigue.topic+"C");
}
}
} catch (MqttException e) {
e.printStackTrace();
}
}
@Override
public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
}
}, new IMqttMessageListener() {
@Override
public void messageArrived(String topic, MqttMessage message) throws Exception {
System.out.println(new String(message.getPayload(), StandardCharsets.UTF_8));
}
}, mqttProperties);
} catch(MqttException me) {
System.out.println("reason "+me.getReasonCode());
System.out.println("msg "+me.getMessage());
System.out.println("loc "+me.getLocalizedMessage());
System.out.println("cause "+me.getCause());
System.out.println("excep "+me);
me.printStackTrace();
}
}
}
- 在包 org.eclipse.paho.mqttv5.client.logging 下创建日志打印文件
package org.eclipse.paho.mqttv5.client.logging;
import java.text.SimpleDateFormat;
import java.util.Date;
public class HarveyDebug {
public static void d(String info){
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");//设置日期格式
String date = df.format(new Date());// new Date()为获取当前系统时间,也可使用当前时间戳
System.out.println(date + " " + info);
}
public static void d(){
System.out.println("");
}
}
package com.harvey.mqtt;
public class MQTTConfigue {
public static String topic = "harvey";
public static String content = "Message from MQTTHarvey'InteIIiJ IDEA ";
public static int qos = 2;
public static String broker = "tcp://127.0.0.1:1883";
public static String clientId = "InteIIiJ IDEA Client ";
}
- 修改原文件
public class MqttInputStream extends InputStream {
......
public MqttWireMessage readMqttWireMessage() throws IOException, MqttException {
final String methodName ="readMqttWireMessage";
MqttWireMessage message = null;
try {
// read header
if (remLen < 0) {
// Assume we can read the whole header at once.
// The header is very small so it's likely we
// are able to read it fully or not at all.
// This keeps the parser lean since we don't
// need to cope with a partial header.
// Should we lose synch with the stream,
// the keepalive mechanism would kick in
// closing the connection.
bais.reset();
byte first = in.readByte();
clientState.notifyReceivedBytes(1);
byte type = (byte) ((first >>> 4) & 0x0F);
if ((type < MqttWireMessage.MESSAGE_TYPE_CONNECT) ||
(type > MqttWireMessage.MESSAGE_TYPE_AUTH)) {
// Invalid MQTT message type...
throw ExceptionHelper.createMqttException(MqttClientException.REASON_CODE_INVALID_MESSAGE);
}
//TODO HARVEY
final String[] PACKET_NAMES = { "reserved", "CONNECT", "CONNACK", "PUBLISH", "PUBACK", "PUBREC",
"PUBREL", "PUBCOMP", "SUBSCRIBE", "SUBACK", "UNSUBSCRIBE", "UNSUBACK", "PINGREQ", "PINGRESP", "DISCONNECT",
"AUTH" };
HarveyDebug.d();
HarveyDebug.d("Receive 包类型:" + PACKET_NAMES[type]);
byte reserved = (byte) (first & 0x0F);
MqttWireMessage.validateReservedBits(type, reserved);
remLen = MqttDataTypes.readVariableByteInteger(in).getValue();
bais.write(first);
bais.write(MqttWireMessage.encodeVariableByteInteger((int)remLen));
packet = new byte[(int)(bais.size()+remLen)];
if(this.clientState.getIncomingMaximumPacketSize() != null &&
bais.size()+remLen > this.clientState.getIncomingMaximumPacketSize() ) {
// Incoming packet is too large
throw ExceptionHelper.createMqttException(MqttClientException.REASON_CODE_INCOMING_PACKET_TOO_LARGE);
}
packetLen = 0;
}
// read remaining packet
if (remLen >= 0) {
// the remaining packet can be read with timeouts
readFully();
// reset packet parsing state
remLen = -1;
byte[] header = bais.toByteArray();
System.arraycopy(header,0,packet,0, header.length);
message = MqttWireMessage.createWireMessage(packet);
//TODO HARVEY
byte [] hs = message.getHeader();
StringBuilder sb = new StringBuilder();
for(byte b : hs){
sb.append(Tools.toBinaryString(b&0xff)).append(" ");
}
HarveyDebug.d("Receive Message Header(二进制内容) : " + sb.toString());
HarveyDebug.d("Receive Message Header(字符串内容) : " + new String(hs, "UTF-8"));
byte [] ps = message.getPayload();
StringBuilder sb2 = new StringBuilder();
for(byte b : ps){
sb2.append(Tools.toBinaryString(b&0xff)).append(" ");
}
HarveyDebug.d("Receive Message payload(二进制内容) : " + sb2.toString());
HarveyDebug.d("Receive Message payload(字符串内容) : " + new String(ps, "UTF-8"));
// @TRACE 530= Received {0}
log.fine(CLASS_NAME, methodName, "530",new Object[] {message});
}
} catch (SocketTimeoutException e) {
// ignore socket read timeout
//TODO HARVEY
// System.out.println(e.getMessage());
}
return message;
}
......
}
package com.harvey.mqtt;
public class MQTTConfigue {
public static String topic = "harvey";
public static String content = "Message from MQTTHarvey'InteIIiJ IDEA ";
public static int qos = 2;
public static String broker = "tcp://127.0.0.1:1883";
public static String clientId = "InteIIiJ IDEA Client ";
}
public class MqttOutputStream extends OutputStream {
......
public void write(MqttWireMessage message) throws IOException, MqttException {
final String methodName = "write";
byte[] bytes = message.getHeader();
byte[] pl = message.getPayload();
if(this.clientState.getOutgoingMaximumPacketSize() != null &&
bytes.length+pl.length > this.clientState.getOutgoingMaximumPacketSize() ) {
// Outgoing packet is too large
throw ExceptionHelper.createMqttException(MqttClientException.REASON_CODE_OUTGOING_PACKET_TOO_LARGE);
}
//调试日志S HARVEY
final String[] PACKET_NAMES = { "reserved", "CONNECT", "CONNACK", "PUBLISH", "PUBACK", "PUBREC",
"PUBREL", "PUBCOMP", "SUBSCRIBE", "SUBACK", "UNSUBSCRIBE", "UNSUBACK", "PINGREQ", "PINGRESP", "DISCONNECT",
"AUTH" };
byte type = (byte) ((bytes[0] >>> 4) & 0x0F);
HarveyDebug.d();
HarveyDebug.d("Send 包类型:" + PACKET_NAMES[type]);
StringBuffer sb = new StringBuffer();
for(byte b : bytes){
sb.append(Tools.toBinaryString(b&0xff)).append(" ");
}
HarveyDebug.d("Send Message Header(二进制内容) : " + sb.toString());
HarveyDebug.d("Send Message Header(字符串内容) : " + new String(bytes, "UTF-8"));
//调试日志E
out.write(bytes,0,bytes.length);
clientState.notifySentBytes(bytes.length);
int offset = 0;
int chunckSize = 1024;
while (offset < pl.length) {
int length = Math.min(chunckSize, pl.length - offset);
out.write(pl, offset, length);
offset += chunckSize;
clientState.notifySentBytes(length);
}
StringBuilder sb2 = new StringBuilder();
for(byte b : pl){
sb2.append(Tools.toBinaryString(b&0xff)).append(" ");
}
HarveyDebug.d("Send Message payload(二进制内容) : " + sb2.toString());
HarveyDebug.d("Send Message payload(字符串内容) : 内容长度=" + pl.length + ",内容=" + new String(pl, "UTF-8"));
// @TRACE 529= sent {0}
log.fine(CLASS_NAME, methodName, "529", new Object[]{message});
}
......
}