当前位置: 首页 > news >正文

移植MQTT-C库(附源码)

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主题的消息:

相关文章:

  • 创建Series()对象--pandas
  • 【Linux】软件包管理器yum
  • 算法刷题打卡第49天:排序数组---计数排序
  • 【Linux】低级IO
  • 【Linux】shell命令以及运行原理
  • 【解决】Unity Player Log 自生成造成磁盘满占用率问题
  • 犀牛插件开发-基础核心-技术概览-总体架构-教程
  • 看2022年卡塔尔世界杯有感
  • 小黑被劝退了,生活学习依然继续的leetcode之旅:572. 另一棵树的子树
  • 数据库原理及MySQL应用 | 日志管理
  • web前端经典react面试题
  • web靶场搭建之帝国cms7.5
  • Spring Boot学习篇(一)
  • RosonQt140——Qt Charts模块介绍和Qt绘制图表
  • 正交编码器溢出处理
  • [微信小程序] 使用ES6特性Class后出现编译异常
  • angular组件开发
  • es的写入过程
  • JavaWeb(学习笔记二)
  • js ES6 求数组的交集,并集,还有差集
  • Js基础——数据类型之Null和Undefined
  • macOS 中 shell 创建文件夹及文件并 VS Code 打开
  • PAT A1120
  • session共享问题解决方案
  • SpringBoot几种定时任务的实现方式
  • Vue2.0 实现互斥
  • 表单中readonly的input等标签,禁止光标进入(focus)的几种方式
  • 读懂package.json -- 依赖管理
  • 如何使用Mybatis第三方插件--PageHelper实现分页操作
  • 携程小程序初体验
  • 字符串匹配基础上
  • nb
  • 【干货分享】dos命令大全
  • 测评:对于写作的人来说,Markdown是你最好的朋友 ...
  • ​【原创】基于SSM的酒店预约管理系统(酒店管理系统毕业设计)
  • ​LeetCode解法汇总2182. 构造限制重复的字符串
  • #stm32整理(一)flash读写
  • (javascript)再说document.body.scrollTop的使用问题
  • (每日持续更新)jdk api之FileFilter基础、应用、实战
  • (七)理解angular中的module和injector,即依赖注入
  • (五)MySQL的备份及恢复
  • ******IT公司面试题汇总+优秀技术博客汇总
  • **python多态
  • ./configure、make、make install 命令
  • .net core 客户端缓存、服务器端响应缓存、服务器内存缓存
  • .net core 源码_ASP.NET Core之Identity源码学习
  • .NET CORE使用Redis分布式锁续命(续期)问题
  • .net framework4与其client profile版本的区别
  • .net 后台导出excel ,word
  • .NET/C# 使用 SpanT 为字符串处理提升性能
  • .netcore 如何获取系统中所有session_如何把百度推广中获取的线索(基木鱼,电话,百度商桥等)同步到企业微信或者企业CRM等企业营销系统中...
  • .Net的C#语言取月份数值对应的MonthName值
  • .NET使用HttpClient以multipart/form-data形式post上传文件及其相关参数
  • [C#]winform部署yolov5-onnx模型
  • [C#]科学计数法(scientific notation)显示为正常数字