移植MQTT-C库(附源码)

news2025/1/23 13:15:38

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主题,当用户在命令行点击换行后将发布消息到datetime主题,消息内容为当前时间。

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/89713.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

BigInteger类和BigDecimal类

BigInteger类 BigInteger适合保存比较大的整型 当在编程中遇到需要保存一个特别大的数字&#xff0c;比如地球的人口。 这时如果用long类型保存可能都不够了&#xff0c;此时就需要用到BigInteger BigInteger不能直接*/add()加subtract()减multiply()乘divide()除 使用演示&…

推荐系统学习笔记-论文研读--渐进分层抽取的多任务学习模型

研究背景 多任务相关性的复杂性和竞争性&#xff0c;MTL模型往往会出 现性能退化和负迁移跷跷板现象&#xff0c;即一项任务的性能往往会因影响其他任 务的性能而得到提高 研究成果 跷跷板现象的发现&#xff0c;MTL由于复杂的内在关联性而没有优于相应的单任务模型从联合表…

[附源码]Node.js计算机毕业设计仿咸鱼二手物品交易系统Express

项目运行 环境配置&#xff1a; Node.js最新版 Vscode Mysql5.7 HBuilderXNavicat11Vue。 项目技术&#xff1a; Express框架 Node.js Vue 等等组成&#xff0c;B/S模式 Vscode管理前后端分离等等。 环境需要 1.运行环境&#xff1a;最好是Nodejs最新版&#xff0c;我…

11月CPI超预期放缓,下一步加息基调且看今晚

随着2022年最后一次美国“通胀报告”的公布&#xff0c;美联储未来政策走向决议将迎来关键时刻&#xff01;12月13日晚&#xff0c;美国劳工部公布的报告显示&#xff0c;美国11月CPI同比增长7.1%&#xff0c;超预期放缓&#xff0c;增速低于预期值7.3%和前值 7.7%。美国11月核…

SSM个人饮食管理系统

开发工具(eclipse/idea/vscode等)&#xff1a; 数据库(sqlite/mysql/sqlserver等)&#xff1a; 功能模块(请用文字描述&#xff0c;至少200字)&#xff1a; 个人饮食管理系统 网站前台&#xff1a;关于我们、联系我们、新闻信息、食谱信息、交流信息 管理员功能&#xff1a; 1、…

JavaScript(四):流程控制

流程控制if语句if else 语句&#xff08;双分支语句&#xff09;if -else if语句&#xff08;多分支语句&#xff09;三元表达式switch语句for循环while循环do while 循环continue关键字break关键字if语句 语法结构 if&#xff08;条件表达式&#xff09;{ //条件成立执行的代…

计算摄影——风格迁移

这一章来总结一下图像风格迁移相关的内容&#xff0c;风格迁移的任务是将一幅图作为内容图&#xff0c;从另外一幅画中抽取艺术风格&#xff0c;两者一起合成新的艺术画&#xff0c;要求合成的作品即保持内容图的主要结构和主体&#xff0c;又能够具有风格图的风格&#xff0c;…

精品spring boot+MySQL新冠物资管理系统vue

《spring bootMySQL新冠物资管理系统》该项目含有源码、论文等资料、配套开发软件、软件安装教程、项目发布教程等 使用技术&#xff1a; 操作系统&#xff1a;Windows 10、Windows 7、Windows 8 开发语言&#xff1a;Java 使用框架&#xff1a;spring boot 前端技术&…

人乳铁蛋白:艾美捷Kamiya ELISA试剂盒解决方案

乳铁蛋白是一个分子量为80 kDa的铁结合糖蛋白&#xff0c;属于转铁蛋白家族 [1] 。乳铁蛋白在初乳和牛奶中含量高&#xff0c;在眼泪、唾液、和支气管分泌物、胆汁和胃肠液等粘膜分泌物中的含量较低。此外&#xff0c;乳铁蛋白也是中性粒细胞的组成成分。 1939年Sorensen等人在…

java计算机毕业设计springboot+vue健康体检信息管理系统

项目介绍 随时代变化,中国作为一个经济发展快速,人口基础较为庞大的国家,健康体检产业发展迅速。健康体检也基本上成为了每家每年必须要考虑的事情。在用户群体的规模如此庞大的基础上,以健康体检为主要内容的行业应运而生,并且随着用户数量的不断增加,其规模也随之不断发展。…

java实现延时处理

业务场景: 1、生成订单30分钟未支付,则自动取消,我们该怎么实现呢? 2、生成订单60秒后,给用户发短信 延时任务和定时任务的区别: 定时任务有明确的触发时间,延时任务没有;定时任务有执行周期,而延时任务在某事件触发后一段时间内执行,没有执行周期;定时任务一般执行的…

大三Web课程设计(可以很好的应付老师的作业) 家乡主题网页设计 我的家乡广州

家乡旅游景点网页作业制作 网页代码运用了DIV盒子的使用方法&#xff0c;如盒子的嵌套、浮动、margin、border、background等属性的使用&#xff0c;外部大盒子设定居中&#xff0c;内部左中右布局&#xff0c;下方横向浮动排列&#xff0c;大学学习的前端知识点和布局方式都有…

【Redis】介绍

文章目录NoSQL 数据库Redis 数据库配套视频课&#xff1a; https://www.bilibili.com/video/BV1Rv41177Af/NoSQL 数据库 1、NoSQL 定义 NoSQL&#xff08;Not Only SQL&#xff09;泛指非关系型数据库。NoSQL不依赖业务逻辑方式存储&#xff0c;而以简单的key-value模式存储&…

27K测试老鸟6年经验的面试心得,四种公司、四种问题…

这里总结了下自己今年的面试情况 先说一下自己的个人情况&#xff0c;普通二本计算机专业毕业&#xff0c;懂python&#xff0c;会写脚本&#xff0c;会selenium&#xff0c;会性能。趁着金三银四跳槽季&#xff0c;面试字节跳动测试岗技术面都已经过了&#xff0c;本来以为是…

阿里云弹性公网ip如何从包年包月转换为按量付费(按使用流量计费)?

开通包年包月的弹性公网ip后&#xff0c;想将计费方式转换为按量付费&#xff08;按使用流量计费&#xff09;的&#xff0c;应该如何操作呢&#xff1f; 操作流程如下&#xff1a; 第一步&#xff0c;包年包月转换为按量付费&#xff08;按固定带宽计费&#xff09;。 1.登录…

【C++11】基础改变

1.关于C11的介绍 C11标准为C编程语言的第三个官方标准&#xff0c;正式名叫ISO/IEC 14882:2011 - Information technology -- Programming languages -- C 。 在正式标准发布前&#xff0c;原名C0x。它将取代C标准第二版ISO/IEC 14882:2003 - Programming languages -- C 成为C…

上线3天,下载4万,ChatGPT中文版VSCode插件来了

ChatGPT 这几天可谓是风头无两。作为一个问答语言模型&#xff0c;它最大的优点就是可以回答与编程相关的问题&#xff0c;甚至回复一段代码。 尽管有人指出 ChatGPT 生成的代码有错误&#xff0c;但程序员们还是对它写代码、找 bug 的功能很感兴趣&#xff0c;有人还给 VScode…

一千字认识NodeJS

在学习Node.js时&#xff0c;发现一处课程通过介绍网站开发来引入Node.js的思路很有意思&#xff0c;在此将整个过程以自己的理解记录了下来&#xff0c;供大家学习交流&#x1f600;&#x1f600;&#x1f600; 前端和后端 众所周知&#xff0c;前端程序yuan通常写的是浏览器…

数据库--------用户的授权

目录 授权例1例2例3例4例5收回例1例2例3授权 GRANT语句格式: GRANT <权限> [,<权限>]

SpringBoot打的jar包瘦身

文章目录正常打包瘦身方法一&#xff1a;Dloader.path指定依赖包位置瘦身方法二&#xff1a;配置文件里指定依赖包位置正常打包 <build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plu…