Rust is a general-purpose, compiled programming language developed by Mozilla. The design principles of the language are: safety, concurrency, utility, and support for functional , concurrent , procedural, and object-oriented programming styles. Rust is blazingly fast and extremely memory efficient. With no runtime and no garbage collection, it can handle particularly performance-hungry services, run on embedded devices, and easily integrate with other languages. Rust's rich type system and ownership model guarantees memory safety and thread safety, allowing you to eliminate all kinds of errors at compile time.
MQTT is a lightweight , which can provide real-time and reliable message services for networked devices with very little code and bandwidth. It is widely used in the Internet of Things, mobile Internet, smart hardware, Internet of vehicles, electric power and other industries.
This article mainly introduces how to use the paho-mqtt client library in the Rust project to realize the functions of connecting, subscribing, unsubscribing, sending and receiving messages between the client and the MQTT server.
Project initialization
This project uses Rust 1.44.0 for development and testing, and uses the Cargo 1.44.0 package management tool for project management. Readers can use the following commands to view the current Rust version.
~ rustc --version
rustc 1.44.0 (49cae5576 2020-06-01)
Select MQTT client library
paho-mqtt is a well-functioning and widely used MQTT client in Rust. The latest 0.7.1
version supports MQTT v5, 3.1.1, 3.1, supports data transmission through standard TCP, SSL/TLS, WebSockets, and QoS supports 0, 1 , 2, etc.
Initialize the project
Execute the following command to create mqtt-example
a .
~ cargo new mqtt-example
Created binary (application) `mqtt-example` package
Edit the Cargo.toml
file dependencies
add the address of the paho-mqtt
library in , and specify the binary file corresponding to the subscription and publishing code files.
[dependencies]
paho-mqtt = { git = "https://github.com/eclipse/paho.mqtt.rust.git", branch = "master" }
[[bin]]
name = "sub"
path = "src/sub/main.rs"
[[bin]]
name = "pub"
path = "src/pub/main.rs"
Using Rust MQTT
Create client connection
This article will use the free MQTT server for the test connection, which is based on EMQ X's MQTT IoT cloud platform . The server access information is as follows:
- Broker: broker.emqx.io
- TCP Port: 1883
- Websocket Port: 8083
Configure MQTT Broker connection parameters
Configure the MQTT Broker connection address (including port), topic (here we configured two topics), and client id.
const DFLT_BROKER:&str = "tcp://broker.emqx.io:1883";
const DFLT_CLIENT:&str = "rust_publish";
const DFLT_TOPICS:&[&str] = &["rust/mqtt", "rust/test"];
Write the MQTT connection code
Write the MQTT connection code. In order to improve the user experience, you can pass in the connection address in the form of command line parameters when executing the binary file. Usually we need to create a client first and then connect that client to broker.emqx.io
.
let host = env::args().nth(1).unwrap_or_else(||
DFLT_BROKER.to_string()
);
// Define the set of options for the create.
// Use an ID for a persistent session.
let create_opts = mqtt::CreateOptionsBuilder::new()
.server_uri(host)
.client_id(DFLT_CLIENT.to_string())
.finalize();
// Create a client.
let cli = mqtt::Client::new(create_opts).unwrap_or_else(|err| {
println!("Error creating the client: {:?}", err);
process::exit(1);
});
// Define the set of options for the connection.
let conn_opts = mqtt::ConnectOptionsBuilder::new()
.keep_alive_interval(Duration::from_secs(20))
.clean_session(true)
.finalize();
// Connect and wait for it to complete or fail.
if let Err(e) = cli.connect(conn_opts) {
println!("Unable to connect:\n\t{:?}", e);
process::exit(1);
}
make an announcement
rust/mqtt
Here we publish a total of five messages , rust/test
which are published to the two topics , respectively, according to the parity of the loop .
for num in 0..5 {
let content = "Hello world! ".to_string() + &num.to_string();
let mut msg = mqtt::Message::new(DFLT_TOPICS[0], content.clone(), QOS);
if num % 2 == 0 {
println!("Publishing messages on the {:?} topic", DFLT_TOPICS[1]);
msg = mqtt::Message::new(DFLT_TOPICS[1], content.clone(), QOS);
} else {
println!("Publishing messages on the {:?} topic", DFLT_TOPICS[0]);
}
let tok = cli.publish(msg);
if let Err(e) = tok {
println!("Error sending message: {:?}", e);
break;
}
}
Subscribe to news
Before the client can connect, the consumer needs to be initialized first. Here we will loop through the message queue in the consumer, and print out the subscribed topic name and the content of the received message.
fn subscribe_topics(cli: &mqtt::Client) {
if let Err(e) = cli.subscribe_many(DFLT_TOPICS, DFLT_QOS) {
println!("Error subscribes topics: {:?}", e);
process::exit(1);
}
}
fn main() {
...
// Initialize the consumer before connecting.
let rx = cli.start_consuming();
...
// Subscribe topics.
subscribe_topics(&cli);
println!("Processing requests...");
for msg in rx.iter() {
if let Some(msg) = msg {
println!("{}", msg);
}
else if !cli.is_connected() {
if try_reconnect(&cli) {
println!("Resubscribe topics...");
subscribe_topics(&cli);
} else {
break;
}
}
}
...
}
full code
message release code
use std::{
env,
process,
time::Duration
};
extern crate paho_mqtt as mqtt;
const DFLT_BROKER:&str = "tcp://broker.emqx.io:1883";
const DFLT_CLIENT:&str = "rust_publish";
const DFLT_TOPICS:&[&str] = &["rust/mqtt", "rust/test"];
// Define the qos.
const QOS:i32 = 1;
fn main() {
let host = env::args().nth(1).unwrap_or_else(||
DFLT_BROKER.to_string()
);
// Define the set of options for the create.
// Use an ID for a persistent session.
let create_opts = mqtt::CreateOptionsBuilder::new()
.server_uri(host)
.client_id(DFLT_CLIENT.to_string())
.finalize();
// Create a client.
let cli = mqtt::Client::new(create_opts).unwrap_or_else(|err| {
println!("Error creating the client: {:?}", err);
process::exit(1);
});
// Define the set of options for the connection.
let conn_opts = mqtt::ConnectOptionsBuilder::new()
.keep_alive_interval(Duration::from_secs(20))
.clean_session(true)
.finalize();
// Connect and wait for it to complete or fail.
if let Err(e) = cli.connect(conn_opts) {
println!("Unable to connect:\n\t{:?}", e);
process::exit(1);
}
// Create a message and publish it.
// Publish message to 'test' and 'hello' topics.
for num in 0..5 {
let content = "Hello world! ".to_string() + &num.to_string();
let mut msg = mqtt::Message::new(DFLT_TOPICS[0], content.clone(), QOS);
if num % 2 == 0 {
println!("Publishing messages on the {:?} topic", DFLT_TOPICS[1]);
msg = mqtt::Message::new(DFLT_TOPICS[1], content.clone(), QOS);
} else {
println!("Publishing messages on the {:?} topic", DFLT_TOPICS[0]);
}
let tok = cli.publish(msg);
if let Err(e) = tok {
println!("Error sending message: {:?}", e);
break;
}
}
// Disconnect from the broker.
let tok = cli.disconnect(None);
println!("Disconnect from the broker");
tok.unwrap();
}
message subscription code
In order to improve the user experience, the message subscription is disconnected and reconnected, and the topic is re-subscribed after the connection is re-established.
use std::{
env,
process,
thread,
time::Duration
};
extern crate paho_mqtt as mqtt;
const DFLT_BROKER:&str = "tcp://broker.emqx.io:1883";
const DFLT_CLIENT:&str = "rust_subscribe";
const DFLT_TOPICS:&[&str] = &["rust/mqtt", "rust/test"];
// The qos list that match topics above.
const DFLT_QOS:&[i32] = &[0, 1];
// Reconnect to the broker when connection is lost.
fn try_reconnect(cli: &mqtt::Client) -> bool
{
println!("Connection lost. Waiting to retry connection");
for _ in 0..12 {
thread::sleep(Duration::from_millis(5000));
if cli.reconnect().is_ok() {
println!("Successfully reconnected");
return true;
}
}
println!("Unable to reconnect after several attempts.");
false
}
// Subscribes to multiple topics.
fn subscribe_topics(cli: &mqtt::Client) {
if let Err(e) = cli.subscribe_many(DFLT_TOPICS, DFLT_QOS) {
println!("Error subscribes topics: {:?}", e);
process::exit(1);
}
}
fn main() {
let host = env::args().nth(1).unwrap_or_else(||
DFLT_BROKER.to_string()
);
// Define the set of options for the create.
// Use an ID for a persistent session.
let create_opts = mqtt::CreateOptionsBuilder::new()
.server_uri(host)
.client_id(DFLT_CLIENT.to_string())
.finalize();
// Create a client.
let mut cli = mqtt::Client::new(create_opts).unwrap_or_else(|err| {
println!("Error creating the client: {:?}", err);
process::exit(1);
});
// Initialize the consumer before connecting.
let rx = cli.start_consuming();
// Define the set of options for the connection.
let lwt = mqtt::MessageBuilder::new()
.topic("test")
.payload("Consumer lost connection")
.finalize();
let conn_opts = mqtt::ConnectOptionsBuilder::new()
.keep_alive_interval(Duration::from_secs(20))
.clean_session(false)
.will_message(lwt)
.finalize();
// Connect and wait for it to complete or fail.
if let Err(e) = cli.connect(conn_opts) {
println!("Unable to connect:\n\t{:?}", e);
process::exit(1);
}
// Subscribe topics.
subscribe_topics(&cli);
println!("Processing requests...");
for msg in rx.iter() {
if let Some(msg) = msg {
println!("{}", msg);
}
else if !cli.is_connected() {
if try_reconnect(&cli) {
println!("Resubscribe topics...");
subscribe_topics(&cli);
} else {
break;
}
}
}
// If still connected, then disconnect now.
if cli.is_connected() {
println!("Disconnecting");
cli.unsubscribe_many(DFLT_TOPICS).unwrap();
cli.disconnect(None).unwrap();
}
println!("Exiting");
}
run and test
Compile the binary
Execute the following mqtt-example/target/debug
command to generate the corresponding binary files sub
for message subscription and publication in the directory.pub
cargo build
Execute the sub
binary and wait for the consumption release.
news release
Execute the pub
binary file, and you can see that messages are published rust/test
to rust/mqtt
these two topics respectively.
At the same time, you can see the published messages in the message subscription
So far, we have completed using the paho-mqtt client to connect to the public MQTT server , and implemented the connection between the test client and the MQTT server, message publishing and subscription.
Copyright statement: This article is original by EMQ , please indicate the source when reprinting.
Original link: https://www.emqx.io/cn/blog/how-to-use-mqtt-in-rust