移植MQTT-C库(附源码)

2023-05-16

Software (mqtt.org)中mqtt客户端的c库里面有一个叫MQTT-C的库,就2个实现文件,算比较简单的了,实现了基本的mqtt客户端功能,移植一下试试。

我的移植代码放在我的资源里面:https://download.csdn.net/download/oushaojun2/87281533?spm=1001.2014.3001.5501

进到这个项目的github仓库地址:https://github.com/LiamBindle/MQTT-C

src里面有两个文件mqtt.c和mqtt_pal.c,第一个是mqtt的实现,第二个是移植需要的文件。移植文件里面主要包括了常见平台的socket接收和发送函数的封装,假如移植到自己的平台可能需要修改这个文件里面的代码,目前的移植是想要在visual studio里面移植,里面已经有了移植接口了。

移植到visual studio里面的步骤如下:

1 将MQTT-C的代码增加到visual studio的一个空白工程里面。需要的文件如下,记得删掉创建工程是自带的文件和修改文件包含路径:

2 修改mqtt_pal.h,128行增加一行:#pragma comment(lib,"ws2_32.lib"),为了在win32平台下链接到ws2_32.lib库,否则编译

3 修改posix_sockets.h内容,虽然这个头文件是按照socket标准接口来调用的,但是win32的socket接口跟linux的接口有些不一样,例如close在win32里面是没有的,gai_strerror在win32里面没效果,win32需要调用WSAStartup函数。修改如下:

#if !defined(__POSIX_SOCKET_TEMPLATE_H__)
#define __POSIX_SOCKET_TEMPLATE_H__

#include <stdio.h>
#include <sys/types.h>
#if !defined(WIN32)
#include <sys/socket.h>
#include <netdb.h>
#else
#include <ws2tcpip.h>
#define  close closesocket
#endif
#if defined(__VMS)
#include <ioctl.h>
#endif
#include <fcntl.h>

/*
    A template for opening a non-blocking POSIX socket.
*/
int open_nb_socket(const char* addr, const char* port);

int open_nb_socket(const char* addr, const char* port) {
    struct addrinfo hints = {0};

    hints.ai_family = AF_UNSPEC; /* IPv4 or IPv6 */
    hints.ai_socktype = SOCK_STREAM; /* Must be TCP */
    int sockfd = -1;
    int rv;
    struct addrinfo *p, *servinfo;

#if defined(WIN32)
    {
        WSADATA wsaData;
        int iResult;
        iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
        if (iResult != NO_ERROR) {
            printf("WSAStartup failed: %d\n", iResult);
            return 1;
        }
    }
#endif

    /* get address information */
    rv = getaddrinfo(addr, port, &hints, &servinfo);
    if(rv != 0) {
#if defined(__UNIX__)
        fprintf(stderr, "Failed to open socket (getaddrinfo): %s\n", gai_strerror(rv));
#else
        fprintf(stderr, "Failed to open socket (getaddrinfo): %s\n", gai_strerrorA(rv));
#endif
        return -1;
    }

    /* open the first possible socket */
    for(p = servinfo; p != NULL; p = p->ai_next) {
        sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
        if (sockfd == -1) continue;

        /* connect to server */
        rv = connect(sockfd, p->ai_addr, p->ai_addrlen);
        if(rv == -1) {
          close(sockfd);
          sockfd = -1;
          continue;
        }
        break;
    }  

    /* free servinfo */
    freeaddrinfo(servinfo);

    /* make non-blocking */
#if !defined(WIN32)
    if (sockfd != -1) fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFL) | O_NONBLOCK);
#else
    if (sockfd != INVALID_SOCKET) {
        int iMode = 1;
        ioctlsocket(sockfd, FIONBIO, &iMode);
    }
#endif
#if defined(__VMS)
    /* 
        OpenVMS only partially implements fcntl. It works on file descriptors
        but silently fails on socket descriptors. So we need to fall back on
        to the older ioctl system to set non-blocking IO
    */
    int on = 1;                 
    if (sockfd != -1) ioctl(sockfd, FIONBIO, &on);
#endif

    /* return the new socket fd */
    return sockfd;
}

#endif

 4 修改simple_publisher.c文件,这个文件的接口都是posix接口,在win32环境下有些要修改,例如建立线程函数等。修改如下:


/**
 * @file
 * A simple program to that publishes the current time whenever ENTER is pressed.
 */
#if defined(__unix__)
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#else
#include <stdlib.h>
#include <stdio.h>
#pragma warning(disable : 4996)
#endif

#include <mqtt.h>
#include "posix_sockets.h"


/**
 * @brief The function that would be called whenever a PUBLISH is received.
 *
 * @note This function is not used in this example.
 */
void publish_callback(void** unused, struct mqtt_response_publish *published);

/**
 * @brief The client's refresher. This function triggers back-end routines to
 *        handle ingress/egress traffic to the broker.
 *
 * @note All this function needs to do is call \ref __mqtt_recv and
 *       \ref __mqtt_send every so often. I've picked 100 ms meaning that
 *       client ingress/egress traffic will be handled every 100 ms.
 */
#if defined(__UNIX__)
void* client_refresher(void* client);
#else
DWORD WINAPI client_refresher(LPVOID client);
#endif


/**
 * @brief Safelty closes the \p sockfd and cancels the \p client_daemon before \c exit.
 */
#if defined(__unix__)
void exit_example(int status, int sockfd, pthread_t *client_daemon);
#else
void exit_example(int status, int sockfd, HANDLE client_daemon);

#endif

/**
 * A simple program to that publishes the current time whenever ENTER is pressed.
 */
int main(int argc, const char *argv[])
{
    const char* addr;
    const char* port;
    const char* topic;

    /* get address (argv[1] if present) */
    if (argc > 1) {
        addr = argv[1];
    } else {
        addr = "test.mosquitto.org";
    }

    /* get port number (argv[2] if present) */
    if (argc > 2) {
        port = argv[2];
    } else {
        port = "1883";
    }

    /* get the topic name to publish */
    if (argc > 3) {
        topic = argv[3];
    } else {
        topic = "datetime";
    }

    /* open the non-blocking TCP socket (connecting to the broker) */
    int sockfd = open_nb_socket(addr, port);

    if (sockfd == -1) {
        perror("Failed to open socket: ");
        exit_example(EXIT_FAILURE, sockfd, NULL);
    }

    /* setup a client */
    struct mqtt_client client;
    uint8_t sendbuf[2048]; /* sendbuf should be large enough to hold multiple whole mqtt messages */
    uint8_t recvbuf[1024]; /* recvbuf should be large enough any whole mqtt message expected to be received */
    mqtt_init(&client, sockfd, sendbuf, sizeof(sendbuf), recvbuf, sizeof(recvbuf), publish_callback);
    /* Create an anonymous session */
    const char* client_id = NULL;
    /* Ensure we have a clean session */
    uint8_t connect_flags = MQTT_CONNECT_CLEAN_SESSION;
    /* Send connection request to the broker. */
    mqtt_connect(&client, client_id, NULL, NULL, 0, NULL, NULL, connect_flags, 400);

    /* check that we don't have any errors */
    if (client.error != MQTT_OK) {
        fprintf(stderr, "error: %s\n", mqtt_error_str(client.error));
        exit_example(EXIT_FAILURE, sockfd, NULL);
    }

    /* start a thread to refresh the client (handle egress and ingree client traffic) */
#if defined(__UNIX__)
    pthread_t client_daemon;
    if(pthread_create(&client_daemon, NULL, client_refresher, &client)) {
        fprintf(stderr, "Failed to start client daemon.\n");
        exit_example(EXIT_FAILURE, sockfd, NULL);

    }
#else
    HANDLE client_daemon;
    DWORD   dwThreadIdArray;
    client_daemon = CreateThread(
                        NULL,                   // default security attributes
                        0,                      // use default stack size  
                        client_refresher,       // thread function name
                        &client,          // argument to thread function 
                        0,                      // use default creation flags 
                        &dwThreadIdArray);   // returns the thread identifier 
#endif

    /* start publishing the time */
    printf("%s is ready to begin publishing the time.\n", argv[0]);
    printf("Press ENTER to publish the current time.\n");
    printf("Press CTRL-D (or any other key) to exit.\n\n");
    while(fgetc(stdin) == '\n') {
        /* get the current time */
        time_t timer;
        time(&timer);
        struct tm* tm_info = localtime(&timer);
        char timebuf[26];
        strftime(timebuf, 26, "%Y-%m-%d %H:%M:%S", tm_info);

        /* print a message */
        char application_message[256];
        snprintf(application_message, sizeof(application_message), "The time is %s", timebuf);
        printf("%s published : \"%s\"", argv[0], application_message);

        /* publish the time */
        mqtt_publish(&client, topic, application_message, strlen(application_message) + 1, MQTT_PUBLISH_QOS_0);

        /* check for errors */
        if (client.error != MQTT_OK) {
            fprintf(stderr, "error: %s\n", mqtt_error_str(client.error));
            exit_example(EXIT_FAILURE, sockfd, &client_daemon);
        }
    }

    /* disconnect */
    printf("\n%s disconnecting from %s\n", argv[0], addr);
#if defined(__UNIX__)
    sleep(1);
#else
    Sleep(1000);
#endif

    /* exit */
    exit_example(EXIT_SUCCESS, sockfd, &client_daemon);
}

#if defined(__UNIX__)
void exit_example(int status, int sockfd, pthread_t *client_daemon)
{
    if (sockfd != -1) close(sockfd);
    if (client_daemon != NULL) pthread_cancel(*client_daemon);
    exit(status);
}
#else
void exit_example(int status, int sockfd, HANDLE client_daemon)
{
    if (sockfd != -1) close(sockfd);
    if (client_daemon != NULL) CloseHandle(client_daemon);
    exit(status);
}
#endif



void publish_callback(void** unused, struct mqtt_response_publish *published)
{
    /* not used in this example */
}

#if defined(__UNIX__)
void* client_refresher(void* client)
#else
DWORD WINAPI client_refresher(LPVOID client)
#endif
{
    while(1)
    {
        mqtt_sync((struct mqtt_client*) client);
#if defined(__UNIX__)
        usleep(100000U);
#else
        Sleep(100);
#endif
    }
    return NULL;
}

点击visual studio编译后运行,这个程序会去连接test.mosquitto.org的1883接口,当用户在命令行点击换行后将发布消息到datetime主题,消息内容为当前时间。

在另外一个mqtt客户端订阅这个主题后会收到发布到datetime主题的消息:

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

移植MQTT-C库(附源码) 的相关文章

  • Qt图形视图框架封装-拿来就用

    简述 图形视图 xff08 Graphics View xff09 提供了一个平台 xff0c 用于大量自定义2D图元的管理与交互 xff0c 并提供了一个视图部件 xff08 view widget xff09 来显示可以缩放和旋转的图元

随机推荐

  • 带参数的宏定义

    带参数的宏定义的一般形式如下 xff1a define lt 宏名 gt xff08 lt 参数表 gt xff09 lt 宏体 gt 其中 xff0c lt 宏名 gt 是一个标识符 xff0c lt 参数表 gt 中的参数可以是一个 x
  • QT 视图(View)/委托(Delegate)/ 模型(Model)/项(Item) 之间的关系

    引言 在学习Qt 中此部分的使用是界面自定义出美观的重要部分 xff0c 熟练的使用 xff0c 可以制作出很好的效果 xff0c 在此梳理一下他们的关系 Model View xff08 模型 视图 xff09 视图 xff08 View
  • char(数字) 转换 int

    刷题遇到一个考点是 char型数字 转 int 进行计算的问题 一看就会 xff0c 一做就错 xff0c 显然是在这里的认识薄弱了 将一番搜索的结果记录下来 xff0c 以备再忘来打脸 char的定义参考 xff1a Java基本数据类型
  • ROS的ros_canopen调试(1)

    Ros canopen是ros支持can通信的package 链接 xff1a http wiki ros org ros canopen distro 61 indigo Ros canopen包结构如下 SocketCAN 是一组开源的
  • linux gcc编译错误:undefined reference to `aio_error‘解决方法

    include lt aio h gt void aiow completion handler sigval t sigval int ret struct aiocb req req 61 struct aiocb sigval siv
  • wait_event_interruptible_locked的使用方法

    wait event interruptible locked interface New wait event interruptible exclusive locked irq macros added They work just
  • printk在应用层的设置方式及读取内核打印信息的方法

    如果 printk 中没有加调试级别 xff0c 则使用默认的调试级别 注意 xff0c 调试级别和格式化字符串之间没有逗号 当前控制台的各打印级别可以通过下面的命令来查看 cat proc sys kernel printk 4 4 1
  • qt编译Qxlsx模块及安装

    主要参考如下地址 xff1a https www icode9 com content 4 715555 html 注意的点 xff1a 1 把下载的代码复制到根目录下 xff0c 路径不要有什么空格啥的 xff0c 否则你会发现perl老
  • GitLab 使用Tortoisegit询问“git@192.168.1.18‘s password“问题解决

    现象如下 xff1a 使用TortoiseGit去拉本地GitLab上建立的项目时 xff0c 一直提示输入密码 xff08 如下图 xff09 xff0c 这个密码又没有指定用户名 xff0c 就算你输入你用户名的密码也是失败 但是很诡异
  • 二代身份证读写器原理及开发

    身份证读写器的作用就是从身份证中读取身份信息 xff08 例如姓名 民族 身份证号等 xff09 xff0c 然后显示或者传输给其他模块使用 功能框架如下 xff1a 功能框图说明 xff1a 1 业务模块 负责向安全模块发送命令 xff0
  • JLINK V10 V11固件修复

    先去我的资源里面下载bootloader和app固件文件 步骤 xff1a 1 PC上安装JLINK V4 9工具 xff08 貌似不能使用太高版本的工具 xff0c 否则有问题 xff09 2 打开j flash v4 9 xff0c 新
  • ROS | 话题通信的编程实现

    ROS 话题通信的编程实现 1 创建功能包2 节点编程与消息定义2 1 案例说明2 2 话题消息的定义2 3 创建 cpp文件2 4 话题发布者编程2 5 话题订阅者编程 3 配置与编译3 1 在CMaKeLists txt中添加编译选项3
  • Cocos2dx 3.0配置环境

    3 15 cocos2dx 3 0rc0 终于放出来了 在这里不得不吐槽一件事 xff0c 3 0版本从Alpha xff0c 到beta xff0c 再到rc xff0c 三个版本竟然都有各自创建项目的方式 xff0c 这样真的不会被人打
  • linux 开机运行应用程序

    把运行应用程序的脚本放在 etc rc local里面 xff0c 如果没有 etc rc local xff0c 需要执行前面的3条指令新建这个文件 注意执行应用最好要在后台执行 xff08 后面加个 amp xff09 xff0c 否则
  • arm linux游戏手柄(joystick)驱动移植

    在arm linux中移植usb joystick驱动 xff0c 参考了如下经验 xff1a Linux系统中使用Xbox360手柄 知 行 博客园 cnblogs com 使用BlueZ连接蓝牙手柄 Dokin丶的博客 CSDN博客 蓝
  • linux ubuntu下网络调试助手(GUI)工具

    mNetAssist这个工具在ubuntu下可以运行 xff0c 是个带界面的tcp调试工具 1 UDP通讯 xff1b 2 可做 TCP客户端 xff1b 3 可做 TCP服务器 xff1b 4 可以 十六进制 传送接收数据 5 可以传送
  • fft的通俗解释

    FFT是离散傅立叶变换的快速算法 xff0c 可以将一个信号变换 到频域 有些信号在时域上是很难看出什么特征的 xff0c 但是如 果变换到频域之后 xff0c 就很容易看出特征了 这就是很多信号 分析采用FFT变换的原因 另外 xff0c
  • linux 字符驱动完整框架(poll,async,waitqueue,nonblock等)

    一个linux内核驱动的完整框架 xff0c 包含了能遇到的大部分内容 xff0c 例如timer poll async waitqueue nonblock等等 xff0c 不过基本上没啥大用 xff0c 就是用来熟悉基础的 xff0c
  • vscode远程调试Linux CUDA程序

    参考了 xff1a CUDA 01 第一个程序 知乎 zhihu com 1 本地安装插件 xff1a remote ssh xff0c Microsoft C C 43 43 与NVIDIA Nsight Visual Studio Co
  • 移植MQTT-C库(附源码)

    Software mqtt org 中mqtt客户端的c库里面有一个叫MQTT C的库 xff0c 就2个实现文件 xff0c 算比较简单的了 xff0c 实现了基本的mqtt客户端功能 xff0c 移植一下试试 我的移植代码放在我的资源里