一、mosquitto基本使用
1、下载与编译
wget http://mosquitto.org/files/source/mosquitto-1.5.5.tar.gz
解压后 make && make install
2、交叉编译
更改CMakeLists.txt
添加:
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR arm)
SET(CMAKE_C_COMPILER /home/test/am3352/ti-processor-sdk-linux-rt-am335x-evm-04.03.00.05/linux-devkit/sysroots/x86_64-arago-linux/usr/bin/arm-linux-gnueabihf-gcc)
SET(CMAKE_CXX_COMPILER /home/test/am3352/ti-processor-sdk-linux-rt-am335x-evm-04.03.00.05/linux-devkit/sysroots/x86_64-arago-linux/usr/bin/arm-linux-gnueabihf-g++)
然后执行:
mkdir cross && cd cross && cmake … & make
编译完成后cross目录下就出现了lib、src/mosquitto、client/测试程序。
3、启动mosquitto mosquitto -d -v
mosquitto [-c config file] [ -d | --daemon ] [-p port number] [-v]
参数说明:
-c 后面跟的是启动mosquitto可以调整的参数,比如是否开启基本认证,端口是什么,SSL单向和双向的认证配置等等。
-d 表示MQTT mosquitto将在后台运行。
-p 代表当前的mosquitto服务实例启动以后,其监听端口号,这个配置的覆盖[-c config file] 指定的配置文件中的端口。
-v 代码调试模式(verbose)在终端输出更多的信息。
4、测试
(1)订阅命令mosquitto_sub参数说明
-d 打印debug信息
-h 指定要连接的域名 默认为localhost
-i 指定clientId
-I 指定clientId前缀
-k keepalive 每隔一段时间,发PING消息通知broker仍处于连接状态,默认为60秒。
-q 指定希望接收到QoS为什么的消息 默认QoS为0
-R 不显示陈旧的消息
-t 订阅topic
-v 打印消息
–will-payload 指定一个消息,该消息当客户端与broker意外断开连接时发出。该参数需要与–will-topic一起使用
–will-qos Will的QoS值。该参数需要与–will-topic一起使用
–will-retain 指定Will消息被当做一个retain消息(即消息被广播后,该消息被保留起来)。该参数需要与–will-topic一起使用
–will-topic 用户发送Will消息的topic
(2)发布命令mosquitto_pub参数说明
-d 打印debug信息
-f 将指定文件的内容作为发送消息的内容
-h 指定要连接的域名 默认为localhost
-i 指定要给哪个clientId的用户发送消息
-I 指定给哪个clientId前缀的用户发送消息
-m 消息内容
-n 发送一个空(null)消息
-p 连接端口号
-q 指定QoS的值(0,1,2)
-t 指定topic
-u 指定broker访问用户
-P 指定broker访问密码
-V 指定MQTT协议版本
–will-payload 指定一个消息,该消息当客户端与broker意外断开连接时发出。该参数需要与–will-topic一起使用
–will-qos Will的QoS值。该参数需要与–will-topic一起使用
–will-retain 指定Will消息被当做一个retain消息(即消息被广播后,该消息被保留起来)。该参数需要与–will-topic一起使用
–will-topic 用户发送Will消息的topic
(3)测试详情,三个客户端同时订阅另一客户端的消息
5、问题解决
(1)mosquitto_sub: error while loading shared libraries: libmosquitto.so.1: cannot open shared object file: No such file or directory
ln -s /usr/local/lib/libmosquitto.so.1 /usr/lib/libmosquitto.so.1
(2)Starting in local only mode. Connections will only be possible from clients running on this machine
大于2.0.0版本的mosquitto,默认配置只会绑定到localhost,如果希望其他机器访问,
需要使用配置文件添加:
listener 1883
默认情况下,不允许匿名连接(没有用户名/密码),如果想允许匿名连接:
allow_anonymous true
二、mosquitto库
1、常用函数说明
1、库的初始化与清除
(1)初始化mosquitto库,必须在任何其他mosqtto函数前调用
int mosquitto_lib_init (NULL);
返回值:
MOSQ_ERR_SUCCESS:成功。
MOSQ_ERR_UNKNOWN:在 Windows 上,无法初始化套接字。
(2)释放与库关联的资源
int mosquitto_lib_cleanup (NULL);
返回值:MOSQ_ERR_SUCCESS 总是
2、客户端创建与销毁
(1)创建一个新的 mosquitto 客户端实例
struct mosquitto *mosquitto_new(const char *id, bool clean_session, void *obj);
.id::用作客户端 ID 的字符串。如果为 NULL,将生成一个随机客户端 ID。如果 id 为 NULL,则 clean_session 必须为真。
.clean_session:设置为 true 指示代理在断开连接时清除所有消息和订阅。设置为 false 指示它保留它们,注意,客户端永远不会在断开连接时丢弃自己的传出消息。调用mosquitto_connect或mosquitto_reconnect将导致消息被重新发送。使用mosquitto_reinitialise将客户端重置为其原始状态。如果 id 参数为 NULL,则必须设置为 true。
.obj: 用户指针,将作为参数传递给指定的任何回调。
返回值:成功则指向struct mosquitto类型的指针。失败时为 NULL。可查询 errno 以确定失败的原因。
(2)释放与 mosquitto客户端实例关联的内存
void mosquitto_destroy(struct mosquitto *mosq);
3、用户名和密码
(1)为 mosquitto实例配置用户名和密码。默认情况下,不发送用户名或密码。此函数必须在调用mosquitto_connect之前调用,是否需要调用此函数取决于broker设置了匿名访问。
int mosquitto_username_pw_set(struct mosquitto *mosq, const char *username, const char *password);
.mosq:mosquitto实例。
.username:用户名字符串,或 NULL 以禁用身份验证。
.password:密码字符串。当用户名有效时设置为 NULL,则仅发送用户名。
4、连接
(1)连接到broker
int mosquitto_connect(struct mosquitto *mosq, const char *host, int port, int keepalive);
.mosq:mosquitto实例。
.host:broker的主机名或 IP 地址。
.port:网络端口,通常是 1883 。
.keepalive:如果在这段时间内(秒数)没有交换其他消息,broker应该向客户端发送 PING 消息。
(2)重新连接到broker,可使用此函数在连接丢失后重新连接,它使用mosquitto_connect调用中提供的值。不能在mosquitto_connect之前调用。
int mosquitto_reconnect(struct mosquitto *mosq);
(3)断开连接
int mosquitto_disconnect(struct mosquitto *mosq);
5、发布与订阅
(1)发布给定主题的消息
int mosquitto_publish(struct mosquitto *mosq,int *mid, const char *topic,int payloadlen, const void *payload,int qos,bool retain);
.mosq:mosquitto实例。
.mid:指向 int 的指针。如果不为 NULL,该函数会将其设置为此特定消息的消息 ID。然后可以将其与发布回调一起使用,以确定何时发送消息。请注意,尽管 MQTT 协议不对 QoS=0 的消息使用消息 ID,但 libmosquitto 会为它们分配消息 ID,以便可以使用此参数跟踪它们。
.topic:要发布到的主题的以 null 结尾的字符串。
.payloadlen:有效载荷的大小(字节)。有效值介于 0 和 268,435,455 之间。
.payload:有效载荷,指向要发送的数据的指针。如果 payloadlen > 0 这必须是一个有效的内存位置。
.qos:服务质量,整数值0、1 或 2,指示要用于消息的服务质量。
.retain:保持,设置为 true 以使消息保留
(2)订阅一个主题,需等待客户端连接成功后才可订阅,连接断开重连后需要重新订阅,所以订阅一般放在连接回调中。
int mosquitto_subscribe(struct mosquitto *mosq, int *mid, const char *sub,int qos);
.mosq:mosquitto实例。
.mid:指向 int 的指针。如果不为 NULL,该函数会将其设置为此特定消息的消息 ID。然后可以将其与 subscribe 回调一起使用,以确定消息何时发送。
.sub:订阅主题。
.qos:服务质量,此订阅请求的服务质量。
(3)退订一个主题
int mosquitto_unsubscribe(struct mosquitto *mosq, int *mid, const char *sub);
.mosq:一个有效的mosquitto实例。
.mid:指向 int 的指针。如果不为 NULL,该函数会将其设置为此特定消息的消息 ID。然后可以将其与取消订阅回调一起使用,以确定消息何时发送。
.sub:退订主题。
6、回调设置
(1)连接成功回调设置
void mosquitto_connect_callback_set(struct mosquitto *mosq, void (*on_connect)(struct mosquitto *, void *, int));
.mosq:mosquitto实例。
.on_connect:回调函数,形式为 void callback(struct mosquitto *mosq, void *obj, int rc)。
.mosq:mosquitto 实例。
.obj:mosquitto_new中提供的用户数据
.rc:连接响应的返回码。这些值由使用的MQTT协议版本定义。
(2)断开连接回调设置
void mosquitto_disconnect_callback_set(struct mosquitto *mosq, void (*on_disconnect)(struct mosquitto *, void *, int));
.mosq:mosquitto实例。
.on_disconnect:回调函数,形式为void callback(struct mosquitto *mosq, void *obj, int rc)。
.mosq:mosquitto 实例。
.obj:mosquitto_new中提供的用户数据
.rc:指示断开连接原因的整数值。值 0 表示客户端已调用mosquitto_disconnect。任何其他值表示断开是意外的。
(3)发布回调设置,消息成功发送到broker后执行
void mosquitto_publish_callback_set(struct mosquitto *mosq,void (*on_publish)(struct mosquitto *, void *, int))
.mosq:mosquitto实例。
.on_disconnect:回调函数,形式为 void callback(struct mosquitto *mosq, void *obj, int mid);
.mosq:mosquitto 实例。
.obj:mosquitto_new中提供的用户数据
.mid:已发送消息的消息 ID。
(4)订阅回调设置,broker响应订阅请求时调用
void mosquitto_subscribe_callback_set(struct mosquitto *mosq,
void (*on_subscribe)(struct mosquitto *, void *, int, int, const int *));
.mosq:mosquitto实例。
.on_subscribe:回调函数,形式为 void callback(struct mosquitto *mosq, void *obj, int mid, int qos_count, const int *granted_qos);
.mosq:mosquitto 实例。
.obj:mosquitto_new中提供的用户数据。
.mid:订阅消息的消息 ID。
.qos_count:授予订阅的数量(granted_qos 的大小)。
.grant_qos:一个整数数组,指示每个订阅的授予 QoS。
(5)消息回调设置,从broker接收到消息时调用
void mosquitto_message_callback_set(struct mosquitto *mosq, void (*on_message)(struct mosquitto *, void *, const struct mosquitto_message *))
.mosq:mosquitto实例。
.on_message:回调函数,形式为void callback(struct mosquitto *mosq, void *obj, const struct mosquitto_message *message)
.mosq:mosquitto 实例。
.obj:mosquitto_new中提供的用户数据。
.message:消息数据。回调完成后,该变量和关联的内存将由库释放。客户应该复制它需要的任何数据。
(6)取消订阅回调设置,当broker响应取消订阅请求时调用
void mosquitto_unsubscribe_callback_set(struct mosquitto *mosq, void (*on_unsubscribe)(struct mosquitto *, void *, int));
.mosq:mosquitto实例。
.on_unsubscribe:回调函数,形式为 void callback(struct mosquitto *mosq, void *obj, int mid)
.mosq:mosquitto 实例。
.obj:mosquitto_new中提供的用户数据
.mid:取消订阅消息的消息 ID。
(7)日志记录回调设置,如果需要来自客户端库的事件日志信息,则应使用此选项
void mosquitto_log_callback_set(struct mosquitto *mosq, void (*on_log)(struct mosquitto *, void *, int, const char *));
.mosq:mosquitto实例。
.on_log:回调函数,形式为void callback(struct mosquitto *mosq, void *obj, int level, const char *str)
.mosq:mosquitto 实例。
.obj:mosquitto_new中提供的用户数据
.level:来自值的日志消息级别: MOSQ_LOG_INFO、 MOSQ_LOG_NOTICE、 MOSQ_LOG_WARNING、 MOSQ_LOG_ERR、 MOSQ_LOG_DEBUG
.str:消息字符串。
7、循环
(1)libmosquitto必须循环处理一些网络消息,必须经常调用如下函数以保持客户端与broker之间的通信正常,此函数不能在回调中调用。此函数一般不使用,一般使用mosquitto_loop_forever和mosquitto_loop_start
int mosquitto_loop(struct mosquitto *mosq, int timeout, int max_packets);
.mosq:mosquitto实例。
.timeout:在超时之前等待 select() 调用中的网络活动的最大毫秒数。设置为 0 以立即返回。设置负数以使用默认值 1000 毫秒。
.max_packets:此参数当前未使用,应设置为 1 以便将来兼容。
(2)阻塞循环调用loop,如果服务器连接丢失,它会处理重新连接。如果在回调中调用 mosquitto_disconnect(),它将返回。
int mosquitto_loop_forever(struct mosquitto *mosq, int timeout, int max_packets);
.mosq:mosquitto实例。
.timeout:在超时之前等待 select() 调用中的网络活动的最大毫秒数。设置为 0 以立即返回。设置负数以使用默认值 1000 毫秒。
.max_packets:此参数当前未使用,应设置为 1 以便将来兼容。
(3)非阻塞方式调用loop,其实就是创建了一个线程
int mosquitto_loop_start( struct mosquitto *mosq);
.mosq:mosquitto实例。
(4)停止使用mosquitto_loop_start创建的网络线程。此调用将阻塞,直到网络线程完成。要结束网络线程,必须调用mosquitto_disconnect或将 force 参数设置为 true。
int mosquitto_loop_stop(struct mosquitto *mosq, bool force);
2、示例
1、订阅sub.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "mosquitto.h"
#define HOST "localhost"
#define PORT 1883
#define KEEP_ALIVE 60
struct sub {
struct mosquitto *mosq;
int running;
int private;
} sub;
static void connect_callback(struct mosquitto *mosq, void *obj, int rc)
{
int ret;
struct sub *sub = (struct sub *)obj;
printf("%s private:%x\n", __func__, sub->private);
if (!rc) {
sub->running = 1;
ret = mosquitto_subscribe(mosq, NULL, "sub1", 2);
if (ret) {
perror("subscribe failed");
exit(-1);
}
ret = mosquitto_subscribe(mosq, NULL, "sub2", 2);
if (ret) {
perror("subscribe failed");
exit(-1);
}
} else {
perror("connect failed");
}
}
static void disconnect_callback(struct mosquitto *mosq, void *obj, int rc)
{
struct sub *sub = (struct sub *)obj;
sub->running = 0;
printf("%s\n", __func__);
}
void subscribe_callback(struct mosquitto *mosq, void *obj, int mid, int qos_count, const int *granted_qos)
{
printf("%s\n", __func__);
}
void message_callback(struct mosquitto *mosq, void *obj,
const struct mosquitto_message *msg)
{
struct sub *sub = (struct sub *)obj;
printf("%s\n", __func__);
printf("Recieve message:\n \ttopic:%s\n\tpayload:%s\n\tpayloadlen:%d\n",
(char *)msg->topic, (char *)msg->payload, msg->payloadlen);
}
void log_callback(struct mosquitto *mosq, void *obj, int level, const char *str)
{
printf("%s\n", __func__);
printf("log level:%d str:%s\n", level, str);
}
int main(int argc, char **argv)
{
struct mosquitto *mosq = sub.mosq;
sub.private = 0xaa55;
sub.running = 0;
int ret = 0;
mosquitto_lib_init();
mosq = mosquitto_new("sub", true, &sub);
if (mosq == NULL) {
perror("create mosquitto failed");
ret = -1;
goto cleanup_lib;
}
mosquitto_connect_callback_set(mosq, connect_callback);
mosquitto_disconnect_callback_set(mosq, disconnect_callback);
mosquitto_subscribe_callback_set(mosq, subscribe_callback);
mosquitto_message_callback_set(mosq, message_callback);
//mosquitto_log_callback_set(mosq, log_callback);
ret = mosquitto_connect(mosq, HOST, PORT, KEEP_ALIVE);
if (ret) {
perror("connect failed");
goto destroy_mosquitto;
}
ret = mosquitto_loop_start(mosq);
if (ret) {
perror("loop failed");
goto destroy_mosquitto;
}
while (!sub.running) {
sleep(1);
}
getchar();
destroy_mosquitto:
mosquitto_destroy(mosq);
cleanup_lib:
mosquitto_lib_cleanup();
end:
return ret;
}
2、发布pub.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "mosquitto.h"
#define HOST "localhost"
#define PORT 1883
#define KEEP_ALIVE 60
struct pub {
struct mosquitto *mosq;
int running;
int private;
} pub;
static void connect_callback(struct mosquitto *mosq, void *obj, int rc)
{
int ret;
struct pub *pub = (struct pub *)obj;
printf("%s private:%x\n", __func__, pub->private);
if (!rc) {
pub->running = 1;
} else {
perror("connect failed");
}
}
static void disconnect_callback(struct mosquitto *mosq, void *obj, int rc)
{
struct pub *pub = (struct pub *)obj;
pub->running = 0;
printf("%s\n", __func__);
}
void subscribe_callback(struct mosquitto *mosq, void *obj, int mid,
int qos_count, const int *granted_qos)
{
printf("%s\n", __func__);
}
void message_callback(struct mosquitto *mosq, void *obj,
const struct mosquitto_message *msg)
{
struct pub *pub = (struct pub *)obj;
printf("%s\n", __func__);
printf("Recieve message:\n \ttopic:%s\n\tpayload:%s\n\tpayloadlen:%d\n",
(char *)msg->topic, (char *)msg->payload, msg->payloadlen);
}
void publish_callback(struct mosquitto *mosq, void *obj, int mid)
{
printf("%s\n", __func__);
}
void log_callback(struct mosquitto *mosq, void *obj, int level, const char *str)
{
printf("%s\n", __func__);
printf("log level:%d str:%s\n", level, str);
}
int main(int argc, char **argv)
{
struct mosquitto *mosq = pub.mosq;
pub.private = 0xaa55;
pub.running = 0;
int ret = 0, i;
char buf[32] = {0};
mosquitto_lib_init();
mosq = mosquitto_new("pub", true, &pub);
if (mosq == NULL) {
perror("create mosquitto failed");
ret = -1;
goto cleanup_lib;
}
mosquitto_connect_callback_set(mosq, connect_callback);
mosquitto_disconnect_callback_set(mosq, disconnect_callback);
mosquitto_publish_callback_set(mosq, publish_callback);
// mosquitto_log_callback_set(mosq, log_callback);
ret = mosquitto_connect(mosq, HOST, PORT, KEEP_ALIVE);
if (ret) {
perror("connect failed");
goto destroy_mosquitto;
}
ret = mosquitto_loop_start(mosq);
if (ret) {
perror("loop failed");
goto destroy_mosquitto;
}
while (!pub.running) {
sleep(1);
}
for (i = 0; i < 100; i++) {
snprintf(buf, sizeof(buf), "pub:%d", i);
ret = mosquitto_publish(mosq, NULL, "sub1", strlen(buf), buf, 0, 1);
if(ret){
perror("pub failed");
}
ret = mosquitto_publish(mosq, NULL, "sub2", strlen(buf), buf, 0, 1);
if(ret){
perror("pub failed");
}
sleep(1);
}
getchar();
destroy_mosquitto:
mosquitto_destroy(mosq);
cleanup_lib:
mosquitto_lib_cleanup();
end:
return ret;
}
3、测试![在这里插入图片描述](https://img-blog.csdnimg.cn/921f2e6243c842bc8e51f3d22dc02b64.png#pic_center)
三、实用工具
1、mqtt.fx
http://mqttfx.jensd.de/index.php/download
MQTT.fx是MQTT客户端工具,支持通过Topic订阅和发布消息,可以方便测试我们自己的程序是否正确发布了或者订阅了消息。
2、mosquitto window
https://mosquitto.org/download/
windows下的mosquitto可以帮助我们测试客户端程序。