Hi,大家好!今天我们来认识一下物联网通信中重要的通信协议MQTT。
MQTT(Message Queuing Telemetry Transport)是一种轻量级的消息协议,设计用于在低带宽、不稳定或高延迟的网络环境中传输消息。MQTT最初由IBM开发,后来成为OASIS标准,广泛用于物联网(IoT)应用和其他需要高效消息传递的场景。
MQTT是轻量级基于代理的发布/订阅的消息传输协议,它可以通过很少的代码和带宽和远程设备连接。例如通过卫星和代理连接,通过拨号和医疗保健提供者连接,以及在一些自动化或小型设备上,而且由于小巧,省电,协议开销小和能高效的向一和多个接收者传递信息,故同样适用于移动应用设备上。
以下是MQTT协议的一些关键概念和特性:
MQTT采用发布/订阅模型,消息的发送者称为发布者(Publisher),而消息的接收者称为订阅者(Subscriber)。发布者将消息发布到主题(Topic),而订阅者可以选择订阅特定主题以接收相关消息。
主题是MQTT中消息的分类标识,用于将消息发送到特定的目标。订阅者可以通过订阅特定主题来接收与该主题相关的消息。主题可以是层次结构的,例如:home/living-room/temperature
,其中home
是根主题,living-room
是home
的子主题,temperature
是living-room
的子主题。
MQTT支持不同的消息质量等级,用于确保消息传递的可靠性。共有三个等级:
发布者可以发送保留消息,这是一个持久的消息,当有新订阅者订阅与保留消息相匹配的主题时,将立即发送该消息。这对于传递重要信息或者初始化状态很有用。
连接到MQTT代理的客户端可以指定一个遗嘱消息。如果客户端非正常断开连接,代理将自动发布遗嘱消息到预定的主题。这可用于通知其他客户端某个设备的状态变化。
MQTT使用保持活动性机制来确保客户端与代理之间的连接保持活动。客户端会定期向代理发送保持活动性的消息,如果代理在指定的时间内未收到客户端的消息,将关闭连接。
MQTT的连接过程包括客户端向代理发送连接请求、代理响应并确认连接、客户端发送连接信息、代理确认连接信息。在这个过程中,客户端和代理之间会协商使用的MQTT版本、连接的用户名和密码、保持活动性时间等。
MQTT本身并没有内建的安全性机制,但可以通过TLS/SSL进行加密传输。此外,可以通过用户名和密码进行身份验证,以及通过访问控制列表(ACL)限制客户端的访问权限。
实现一个完整的MQTT协议是一个庞大而复杂的任务,因为MQTT涉及到多个方面,包括连接、发布/订阅、消息质量等级等。下面是一个简化的C++实现的示例,用于建立一个基本的MQTT客户端,演示连接到MQTT代理服务去发布/订阅消息。
该示例使用了 Eclipse Paho MQTT C++ 客户端库,该库提供了MQTT协议的C++实现。有兴趣的小获取可以去官网了解一下。
首先,确保你已经安装了 Paho MQTT C++ 客户端库。可以从Paho官网获取该库。
接下来,以下是一个简单的C++示例:
#include <iostream>
#include <mqtt/async_client.h>
class MQTTCallbacks : public virtual mqtt::callback, public virtual mqtt::iaction_listener {
void connectionLost(const std::string& cause) override {
std::cout << "Connection lost: " << cause << std::endl;
}
void deliveryComplete(mqtt::delivery_token_ptr tok) override {
std::cout << "Message delivered" << std::endl;
}
void onSuccess(const mqtt::token& tok) override {
std::cout << "Action successful" << std::endl;
}
void onFailure(const mqtt::token& tok) override {
std::cout << "Action failed" << std::endl;
}
};
int main() {
const std::string brokerAddress = "tcp://localhost:1883";
const std::string clientId = "cpp_mqtt_client";
const std::string topic = "test/topic";
mqtt::async_client client(brokerAddress, clientId);
MQTTCallbacks callbacks;
client.set_callback(callbacks);
mqtt::connect_options connOpts;
connOpts.set_keep_alive_interval(20);
connOpts.set_clean_session(true);
try {
std::cout << "Connecting to the broker..." << std::endl;
client.connect(connOpts)->wait();
std::cout << "Connected!" << std::endl;
std::string payload = "Hello, MQTT!";
mqtt::message_ptr pubmsg = mqtt::make_message(topic, payload);
pubmsg->set_qos(1);
client.publish(pubmsg)->wait();
std::cout << "Message published" << std::endl;
client.subscribe(topic, 1)->wait();
while (true) {
// Continue to keep the program running
std::this_thread::sleep_for(std::chrono::seconds(1));
}
client.unsubscribe(topic)->wait();
client.disconnect()->wait();
} catch (const mqtt::exception& exc) {
std::cerr << "Error: " << exc.what() << std::endl;
return 1;
}
return 0;
}
该示例假定了本地运行的MQTT代理地址为 tcp://localhost:1883
,客户端连接后发布了一条消息到主题 test/topic
,并订阅了该主题。实际开发中我们需要修改 brokerAddress
和其他参数。
需要说明的是这只是一个简单的示例,实际上MQTT的实现要更加复杂,需要处理连接丢失、重连、消息质量等级等情况。在实际应用中,建议使用现成的MQTT客户端库,以确保正确性和稳定性。
MQTT在实际开发中需要学习的内容有很多,而且很多细节需要注意。希望正在学习MQTT协议的小伙伴通过本节内容对MQTT协议有个初步的认识。
题外话:学习技术是一个慢慢沉淀的过程。不积跬步无以至千里,技术同样如此。希望小伙伴们在以后的技术路上坚持下去,并到达一片开阔的境地。