MQTT协议分析与应用

MQTT(Message Queuing Telemetry Transport)是一种轻量级的消息传输协议,基于发布 / 订阅(Publish/Subscribe)模式,专为资源受限的设备和低带宽、高延迟或不可靠的网络设计,广泛应用于物联网(IoT)、工业监控、移动应用等领域。

核心概念

客户端(Client):使用 MQTT 协议与 MQTT 代理进行通信的设备或应用程序。客户端可以是发布者(Publisher),负责发布消息到特定主题;也可以是订阅者(Subscriber),订阅感兴趣的主题以接收消息。

代理(Broker):作为 MQTT 网络的核心,负责接收客户端发布的消息,并将消息转发给订阅了相应主题的客户端。

主题(Topic):是消息的分类标识,客户端通过主题来区分不同类型的消息。主题采用分层结构,使用斜杠(/)分隔不同层级,例如 home/livingroom/temperature。

服务质量(QoS):定义了消息传输的可靠性级别,有三个等级:

QoS 0:最多一次,消息可能会丢失,不保证送达。

QoS 1:至少一次,消息会被发送,但可能会重复。

QoS 2:恰好一次,保证消息只被送达一次,实现相对复杂,开销较大。

工作流程

连接:客户端向代理发送连接请求(CONNECT),包含客户端 ID、用户名、密码等信息。代理验证通过后,返回连接确认(CONNACK)。

订阅:客户端向代理发送订阅请求(SUBSCRIBE),指定要订阅的主题和 QoS 级别。代理接收请求后,返回订阅确认(SUBACK)。

发布:客户端向代理发送发布消息(PUBLISH),包含主题、QoS 级别和消息内容。代理根据主题将消息转发给订阅了该主题的客户端。

取消订阅:客户端向代理发送取消订阅请求(UNSUBSCRIBE),指定要取消订阅的主题。代理接收请求后,返回取消订阅确认(UNSUBACK)。

断开连接:客户端向代理发送断开连接请求(DISCONNECT),结束与代理的连接。

示例代码如下:

kotlin 复制代码
//在build.gradle中添加依赖
dependencies {
	implementation ("org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.2.5")
}

//在类文件中定义函数
fun checkMqtt() {
    // MQTT 代理地址
    val brokerUrl = "tcp://broker.hivemq.com:1883" // 公共测试 Broker
    // 客户端 ID
    val clientId = "KotlinClient"
    // 持久化方式,使用 MemoryPersistence 作为消息持久化方式,意味着消息将临时存储在内存中。
    val persistence = MemoryPersistence()

    try {
        // 创建 MQTT 客户端实例,指定代理地址、客户端 ID 和持久化方式。
        val mqttClient = MqttClient(brokerUrl, clientId, persistence)
        // 创建 MQTT 连接选项
        // 创建连接选项对象,设置 isCleanSession 为 true,表示建立一个干净的会话,不保留之前的订阅和未处理的消息
        val connOpts = MqttConnectOptions()
        connOpts.isCleanSession = true

        // 连接到 MQTT 代理
        println("Connecting to broker: $brokerUrl")
        // 调用 connect 方法连接到指定的 MQTT 代理
        mqttClient.connect(connOpts)
        println("Connected")

        // 订阅主题
        // 定义要订阅的主题为 "test/topic",调用 subscribe 方法订阅该主题
        val topic = "test/topic"
        mqttClient.subscribe(topic)
        // 第二个参数 1 代表的是 MQTT 协议里的服务质量(Quality of Service,QoS)级别。MQTT 协议提供了三种不同的 QoS 级别,用于规定消息传输的可靠性程度。
        //mqttClient.subscribe(topic, 1) { topic, message ->
        //    println("Received message on topic $topic: ${String(message.payload)}")
        //}
        println("Subscribed to topic: $topic")

        // 设置消息回调
        // 使用 setCallback 方法设置一个匿名内部类作为消息回调处理对象,实现了 MqttCallback 接口的三个方法
        mqttClient.setCallback(object : MqttCallback {
            override fun connectionLost(cause: Throwable?) {
                println("connectionLost: ${cause?.message}")
            }

            override fun messageArrived(topic: String?, message: MqttMessage?) {
                println("messageArrived...received message on topic '$topic': ${message?.toString()}")
            }

            override fun deliveryComplete(token: IMqttDeliveryToken?) {
                println("deliveryComplete...")
            }
        })

        // 发布消息
        // 定义要发布的消息内容为 "Hello, MQTT!",创建 MqttMessage 对象并将消息内容转换为字节数组
        val message = "Hello, MQTT!"
        val mqttMessage = MqttMessage(message.toByteArray())
        // 设置消息的 QoS 级别为 1(至少一次送达)
        mqttMessage.qos = 1
        // 调用 publish 方法将消息发布到指定主题
        mqttClient.publish(topic, mqttMessage)
        println("Message published: $message")

        // 等待一段时间,确保消息接收
        Thread.sleep(5000)

        // 断开连接
        mqttClient.disconnect()
        println("Disconnected")
    } catch (e: MqttException) {
        println("MqttException: ${e.message}")
    }
}

上面代码的输出如下:

csharp 复制代码
Connecting to broker: tcp://broker.hivemq.com:1883
Connected
Subscribed to topic: test/topic
messageArrived...received message on topic 'test/topic': Hello from MQTTnet v4.1.2
messageArrived...received message on topic 'test/topic': Hello, MQTT! from kotlin
Message published: Hello, MQTT! from kotlin
deliveryComplete...
Disconnected

在上面的输出中,出现两次messageArrived是因为:

第一次messageArrived:当代码订阅test/topic主题后,可能有其他客户端在该主题上发布了消息Hello from MQTTnet v4.1.2,所以你的客户端收到了这条消息,触发了messageArrived回调。由于代码中没有对收到的消息进行额外处理,所以不清楚具体是哪个客户端发送的以及发送的时间。

第二次messageArrived:是因为代码本身向test/topic主题发布了消息Hello, MQTT! from kotlin,发布后,代理服务器会将该消息发送给所有订阅了test/topic主题的客户端,包括当前客户端自己,所以会再次触发messageArrived回调。

相关推荐
androidwork3 小时前
Kotlin与Android Studio开发环境配置指南
开发语言·kotlin·android studio
stevenzqzq3 小时前
kotlin 01flow-StateFlow 完整教程
android·开发语言·kotlin·flow
androidwork3 小时前
使用Kotlin Flow实现Android应用的响应式编程
android·开发语言·kotlin
stevenzqzq3 小时前
kotlin 数据类
android·开发语言·kotlin
大G哥14 小时前
Kotlin Lambda语法错误修复
android·java·开发语言·kotlin
androidwork18 小时前
Kotlin Android工程Mock数据方法总结
android·开发语言·kotlin
悠哉清闲21 小时前
kotlin一个函数返回多个值
kotlin
每次的天空1 天前
Android学习总结之kotlin协程面试篇
android·学习·kotlin
MyhEhud1 天前
Kotlin zip 函数的作用和使用场景
开发语言·windows·kotlin
androidwork1 天前
Kotlin Coroutine与Retrofit网络层构建指南
开发语言·kotlin·retrofit