对MQTT协议的理解和Android端的应用

2,793 阅读3分钟

理解

  1. MQTT协议是应用层的协议,基于TCP协议(所以也是要建立全双工连接的)
  2. MQTT协议是publish/subscribe模式,不同于HTTP协议的client/server模式。。所以我的一个小小的理解就是:HTTP是一对一的数据通信,MQTT是一对多的通信(一个消息发布可能有很多订阅者接收),所以MQTT更适合物联网这种【app一键控制多个设备同时响应】的场景。
  3. 维基百科介绍:

MQTT 协议定义了两种网络实体:消息代理(message broker)与客户端(client)。其中,消息代理用于接收来自客户端的消息并转发至目标客户端。MQTT 客户端可以是任何运行有 MQTT 库并通过网络连接至消息代理的设备,例如微型控制器或大型服务器。

信息的传输是通过主题(topic)管理的。发布者有需要分发的数据时,其向连接的消息代理发送携带有数据的控制消息。代理会向订阅此主题的客户端分发此数据。发布者不需要知道订阅者的数据和具体位置;同样,订阅者不需要配置发布者的相关信息。

应用场景

在一个建筑里,通过手机app控制不同楼层、区域的灯、门、空调等设备的开关。

client的开发

比如app功能页面的开发。

也比如每个设备收到指令的动作,但那些就不是我做的部分了。

  1. 在网上找一个实现了MQTT协议的库 名字 | 开发者 | 开发语言 | 类型 | 初次发布日期 | 最新发布版本 | 最新发布日期 | 许可证 | | -- | --- | ---- | -- | ------ | ------ | ------ | --- Paho MQTT | Eclipse基金会 | C语言C++JavaJavascriptPythonGo | 客户端 | 2014-05-02 | 1.4.1[44] | 2019-02-25 | Eclipse公共许可证 1.0, Eclipse发行许可证 1.0 (BSD) |

  2. 添加依赖、注册服务

implementation 'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.2.5'
implementation 'org.eclipse.paho:org.eclipse.paho.android.service:1.1.1'
<service android:name="org.eclipse.paho.android.service.MqttService" /> <!--MqttService 加在AndroidManifest.xml的application节点里-->
  1. 连接message broker
private MqttAndroidClient mqttAndroidClient;
private MqttConnectOptions mMqttConnectOptions;

//broker地址(协议+ip地址+端口号)
private String HOST = "tcp://xx.com:xxx";
//根据业务场景确定,可为空
private final String CLIENTID = "";
//用户名
private final String USERNAME = "xxx";
//密码
private final String PASSWORD = "xxx";
//传输质量
private final int QOS = 2;
//是否在连接意外断开后保留最后一条publish
boolean retained = false;
public void connect() {
    mqttAndroidClient = new MqttAndroidClient(context, HOST, CLIENTID);
    mqttAndroidClient.setCallback(mqttCallback);
    mMqttConnectOptions = new MqttConnectOptions();
    mMqttConnectOptions.setCleanSession(true);
    mMqttConnectOptions.setConnectionTimeout(10); //设置超时时间,单位:秒
    mMqttConnectOptions.setKeepAliveInterval(20); //设置心跳包发送间隔,单位:秒
    mMqttConnectOptions.setUserName(USERNAME);
    mMqttConnectOptions.setPassword(PASSWORD.toCharArray());
    
    if (!mqttAndroidClient.isConnected() && isConnectIsNomarl()) {
        try {
            mqttAndroidClient.connect(mMqttConnectOptions, null, new IMqttActionListener() {
                @Override
                public void onSuccess(IMqttToken token) {
                    LogUtils.i(TAG, "连接成功");
                    //然后做一些publish、subscribe之类的业务逻辑
                }

                @Override
                public void onFailure(IMqttToken token, Throwable e) {
                    LogUtils.e(TAG, e);
                }
            });
        } catch (MqttException e) {
            LogUtils.e(TAG, e);
        }
    }
}
/**
 * 判断网络是否连接
 */
private boolean isConnectIsNomarl() {
    ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo info = connectivityManager.getActiveNetworkInfo();
    if (info != null && info.isAvailable()) {
        String name = info.getTypeName();
        LogUtils.i(TAG, "当前网络:" + name);
        return true;
    } else {
        LogUtils.i(TAG, "没有可用网络");
        return false;
    }
}
  1. 发布和订阅
//发布
private void publish(String topic, String message) {
    try {
        mqttAndroidClient.publish(topic, message.getBytes(), QOS, retained);
    } catch (MqttException e) {
        LogUtils.e(e);
    }
}
//订阅
private void subscribe(String topic) {
    try {
        mqttAndroidClient.subscribe(topic, QOS);
    } catch (MqttException e) {
        LogUtils.e(e);
    }
}
  1. 收到订阅topic消息时的回调处理
private final MqttCallback mqttCallback = new MqttCallback() {

    @Override
    public void messageArrived(String topic, MqttMessage message) throws Exception {
        String msg = new String(message.getPayload(), "GB2312");
        //根据topic和msg处理
    }

    @Override
    public void deliveryComplete(IMqttDeliveryToken token) {
        LogUtils.i(TAG, "发送完毕 ");
    }

    @Override
    public void connectionLost(Throwable e) {
        LogUtils.i(TAG, "连接断开 ");
    }
};
  1. 断开连接
public void disconnect() {
    if (mqttAndroidClient != null && mqttAndroidClient.isConnected()) {
        try {
            mqttAndroidClient.disconnect();
        } catch (MqttException e) {
            LogUtils.e(e);
        }
        mqttAndroidClient.unregisterResources();
    }
}

项目里是在app的功能页面 create 的时候连接,destroy 的时候断开连接。

broker

项目里的broker是设备控制团队提供的,我只要做个app页面就可以。不过也可以自己本地起一个broker服务做测试:

  1. 参考这篇博客下载ActiveMQ,ActiveMQ把broker的转发等功能都实现好了
  2. 解压进入bin目录,在命令行执行./activemq.bat start启动,ctrl+c关闭
  3. 启动日志会打印出broker服务监听的端口号
INFO | Listening for connections at: mqtt://myname:1883?maximumConnections=1000&wireFormat.maxFrameSize=268435456
INFO | Connector mqtt started
  1. 用上文代码连接broker,其中
HOST = "tcp://本机电脑ip:1883";
USERNAME = "admin";
PASSWORD = "admin";