陈拓 2021/04/15-2021/04/15
1. 概述
在《MQTT服务器Mosquitto 2.x编译安装配置》
https://zhuanlan.zhihu.com/p/365103802
https://blog.csdn.net/chentuo2000/article/details/115731687
一文中我们下载了最新的mosquitto源码进行了编译安装。
在下载的源码中有几个C语言的例子可以参考:
这些例子在github上也可以找到:
https://github.com/eclipse/mosquitto/tree/master/examples
下面我们看例子subscribe。
2. 查看例子subscribe
- 查看temperature_conversion目录
ls -l mosquitto-2.0.9/examples/subscribe/
- 查看C源文件basic-1.c
cat mosquitto-2.0.9/examples/subscribe/basic-1.c
下面对注释进行了翻译。
/*
* This example shows how to write a client that subscribes to a topic and does
* not do anything other than handle the messages that are received.
这个例子示范了怎样写一个客户端订阅一个主题,对收到的消息不做任何其他处理。
*/
#include <mosquitto.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
/* Callback called when the client receives a CONNACK message from the broker. */
/* 当客户端从代理接收到CONNACK消息时调用回调。 */
void on_connect(struct mosquitto *mosq, void *obj, int reason_code)
{
int rc;
/* Print out the connection result. mosquitto_connack_string() produces an
* appropriate string for MQTT v3.x clients, the equivalent for MQTT v5.0
* clients is mosquitto_reason_string().
打印连接结果。mosquitto_connack_string()为mqttv3.x客户端生成适当的字符串,与
mqttv5.0客户端等效的是mosquitto_reason_string()。
*/
printf("on_connect: %s\n", mosquitto_connack_string(reason_code));
if(reason_code != 0){
/* If the connection fails for any reason, we don't want to keep on
* retrying in this example, so disconnect. Without this, the client
* will attempt to reconnect.
如果由于任何原因连接失败,在本例中我们不想继续重试,所以断开连接。否则,客户端将尝
试重新连接。
*/
mosquitto_disconnect(mosq);
}
/* Making subscriptions in the on_connect() callback means that if the
* connection drops and is automatically resumed by the client, then the
* subscriptions will be recreated when the client reconnects.
在on_connect()回调中进行订阅意味着如果连接断开并由客户端自动恢复,则在客户端重新连接时将重
新创建订阅。
*/
rc = mosquitto_subscribe(mosq, NULL, "example/temperature", 1);
if(rc != MOSQ_ERR_SUCCESS){
fprintf(stderr, "Error subscribing: %s\n", mosquitto_strerror(rc));
/* We might as well disconnect if we were unable to subscribe */
/* 如果无法订阅,我们应该断开连接。 */
mosquitto_disconnect(mosq);
}
}
/* Callback called when the broker sends a SUBACK in response to a SUBSCRIBE. */
/* 当代理在响应订阅发送SUBACK时调用回调。 */
void on_subscribe(struct mosquitto *mosq, void *obj, int mid, int qos_count, const int *granted_qos)
{
int i;
bool have_subscription = false;
/* In this example we only subscribe to a single topic at once, but a
* SUBSCRIBE can contain many topics at once, so this is one way to check
* them all.
在本例中,我们一次只订阅一个主题,但是订阅可以一次包含多个主题,因此这是检查所有主题的一种方
法。
*/
for(i=0; i<qos_count; i++){
printf("on_subscribe: %d:granted qos = %d\n", i, granted_qos[i]);
if(granted_qos[i] <= 2){
have_subscription = true;
}
}
if(have_subscription == false){
/* The broker rejected all of our subscriptions, we know we only sent
* the one SUBSCRIBE, so there is no point remaining connected.
代理拒绝了我们所有的订阅,我们知道我们只发送了一个订阅,所以没有保持连接的点。
*/
fprintf(stderr, "Error: All subscriptions rejected.\n");
mosquitto_disconnect(mosq);
}
}
/* Callback called when the client receives a message. */
/* 当客户端收到消息时调用回调。 */
void on_message(struct mosquitto *mosq, void *obj, const struct mosquitto_message *msg)
{
/* This blindly prints the payload, but the payload can be anything so take care.
这会盲目地打印有效负载,但是有效负载可以是任何东西,所以请小心。
*/
printf("%s %d %s\n", msg->topic, msg->qos, (char *)msg->payload);
}
int main(int argc, char *argv[])
{
struct mosquitto *mosq;
int rc;
/* Required before calling other mosquitto functions */
/* 调用其他mosquitto函数之前需要初始化 */
mosquitto_lib_init();
/* Create a new client instance.
* id = NULL -> ask the broker to generate a client id for us
* clean session = true -> the broker should remove old sessions when we connect
* obj = NULL -> we aren't passing any of our private data for callbacks
创建新的客户端实例。
id = NULL -> ask 代理为我们生成一个客户id
clean session = true -> 当我们连接时,代理将删除旧会话
obj = NULL -> 我们没有为回调传递任何私有数据
*/
mosq = mosquitto_new(NULL, true, NULL);
if(mosq == NULL){
fprintf(stderr, "Error: Out of memory.\n");
return 1;
}
/* Configure callbacks. This should be done before connecting ideally. */
/* 配置回调,这应该在连接之前完成。 */
mosquitto_connect_callback_set(mosq, on_connect);
mosquitto_subscribe_callback_set(mosq, on_subscribe);
mosquitto_message_callback_set(mosq, on_message);
/* Connect to test.mosquitto.org on port 1883, with a keepalive of 60 seconds.
* This call makes the socket connection only, it does not complete the MQTT
* CONNECT/CONNACK flow, you should use mosquitto_loop_start() or
* mosquitto_loop_forever() for processing net traffic.
将端口1883连接test.mosquitto.org,心跳时间间隔60秒。
此调用仅使套接字连接,它不完成MQTT CONNECT/CONNACK流,应该使用
mosquitto_loop_star()或mosquitto_loop_forever()来处理网络流量。
*/
rc = mosquitto_connect(mosq, "test.mosquitto.org", 1883, 60);
if(rc != MOSQ_ERR_SUCCESS){
mosquitto_destroy(mosq);
fprintf(stderr, "Error: %s\n", mosquitto_strerror(rc));
return 1;
}
/* Run the network loop in a blocking call. The only thing we do in this
* example is to print incoming messages, so a blocking call here is fine.
*
* This call will continue forever, carrying automatic reconnections if
* necessary, until the user calls mosquitto_disconnect().
在阻塞调用中运行网络循环。在这个例子中,我们所做的唯一一件事就是打印传入的消息,所以这里的阻
塞调用是可以的。
如果需要此调用将一直持续,直到用户调用mosquitto\u disconnect()。
*/
mosquitto_loop_forever(mosq, -1, 1);
mosquitto_lib_cleanup();
return 0;
}
说明:
这个例子示范了怎样写一个客户端订阅一个主题,对收到的消息不做任何其他处理。
3. 编译例子subscribe
3.1 头文件和库文件
- 头文件
ls -l /usr/local/include/mosquitto.h
- 库文件
ls -l /usr/local/lib/libmosquitto.so.1
有关API的详细说明见:
https://mosquitto.org/api/files/mosquitto-h.html#mosquitto_connect
3.2 修改例子
- 设置Username和password
在rc = mosquitto_connect(mosq, "test.mosquitto.org", 1883, 60);
前面添加一句,用来设置Username和password:
mosquitto_username_pw_set(mosq, "ct", "1qaz2wsx");
- 设置我的服务器
将rc = mosquitto_connect(mosq, "test.mosquitto.org", 1883, 60);
中的test.mosquitto.org改成raspberrypi。
- 编辑basic-1.c
进入例子目录:
cd mosquitto-2.0.9/examples/subscribe/
nano basic-1.c
3.3 编译
- 编译
gcc -o basic-1 basic-1.c -lmosquitto
3.4 测试
- 运行basic-1
./basic-1
从rc = mosquitto_subscribe(mosq, NULL, "example/temperature", 1);可知,订阅的消息主题为example/temperature。
- 再开一个窗口发布消息
mosquitto_pub -p 1883 -u ct -P xxxxxxxx -t example/temperature -m "26.6"
- 订阅测试窗口收到消息
- 用MQTT.fx远程测试
详细说明见《树莓派MQTT服务远程测试MQTT.fx》
https://zhuanlan.zhihu.com/p/363373024
https://blog.csdn.net/chentuo2000/article/details/115539377
点击Publish:
订阅测试窗口收到消息