【MQTT协议使用总结】基于-FreeRTOS平台-移植MQTT协议栈

news2024/12/29 7:24:00

文章目录

  • 仓库地址
  • 关键接口适配
    • FreeRTOS_read
    • FreeRTOS_write
    • NetworkInit && NetworkConnect && NetworkDisconnect
  • 总结

仓库地址

https://github.com/eclipse/paho.mqtt.embedded-c

在这里插入图片描述
这里官方给了一些平台适配案例,这里参考FreeRTOS的

关键接口适配

使用使用了lwip的一些接口

FreeRTOS_read

int FreeRTOS_read(Network* n, unsigned char* buffer, int len, int timeout_ms)
{
    // 将超时时间转换为 timeval 结构体表示的时间间隔
    struct timeval interval = {timeout_ms / 1000, (timeout_ms % 1000) * 1000};
    // 如果超时时间小于等于 0,将超时时间设置为 0.1 秒(100 微秒)
    if (interval.tv_sec < 0 || (interval.tv_sec == 0 && interval.tv_usec <= 0)) {
        interval.tv_sec = 0;
        interval.tv_usec = 100;
    }

    fd_set read_fds;
    // 初始化文件描述符集合,清空 read_fds
    FD_ZERO(&read_fds);
    // 将网络结构体中的套接字加入到文件描述符集合中,表示关注该套接字的可读事件
    FD_SET(n->my_socket, &read_fds);

    int bytes = 0;
    while(bytes < len) {
        // 使用 lwip_select 等待套接字可读事件,设置超时时间为 interval
        int result = lwip_select(n->my_socket + 1, &read_fds, NULL, NULL, &interval);
        // 如果返回值小于等于 0,表示没有可读事件发生或者发生错误,跳出循环
        if (result <= 0)
            break;
        // 套接字可读
        int rc = lwip_recv(n->my_socket, &buffer[bytes], (len - bytes), 0);
        // 如果接收返回值为 -1,表示接收出错
        if (rc == -1) {
            // 如果错误不是 EAGAIN(资源暂时不可用)或 EWOULDBLOCK(操作会阻塞)
            if (errno!= EAGAIN && errno!= EWOULDBLOCK)
                bytes = -1;
            break;
        }
        // 如果接收返回值为 0,表示连接已关闭
        else if (rc == 0) {
            bytes = 0;
            break;
        }
        // 如果接收成功,更新已接收的字节数
        else
            bytes += rc;
    }

    return bytes;
}

FreeRTOS_write

int FreeRTOS_write(Network* n, unsigned char* buffer, int len, int timeout_ms)
{
	TickType_t xTicksToWait = timeout_ms / portTICK_PERIOD_MS; /* convert milliseconds to ticks */
	TimeOut_t xTimeOut;
	int sentLen = 0;
	int flags = 0;

	vTaskSetTimeOutState(&xTimeOut); /* Record the time at which this function was entered. */
	do {
		int rc = 0;

		// 设置 lwip_send 为非阻塞方式
		flags = MSG_DONTWAIT;
		rc = lwip_send(n->my_socket, buffer + sentLen, len - sentLen, flags);
		if (rc > 0)
			sentLen += rc;
		else if (rc == 0 || (rc < 0 && errno == EAGAIN)) {
			// 缓冲区已满,等待一段时间再尝试发送
			vTaskDelay(pdMS_TO_TICKS(10)); // 延时10ms再尝试发送
		} else if (rc < 0) {
			sentLen = rc;
			break;
		}
	} while (sentLen < len && xTaskCheckForTimeOut(&xTimeOut, &xTicksToWait) == pdFALSE);

	return sentLen;
}

NetworkInit && NetworkConnect && NetworkDisconnect

// 初始化网络结构体
void NetworkInit(Network* n)
{
    // 将套接字初始化为无效值
    n->my_socket = -1;

    // 设置网络结构体的读取函数指针为 FreeRTOS_read
    n->mqttread = FreeRTOS_read;
    // 设置网络结构体的写入函数指针为 FreeRTOS_write
    n->mqttwrite = FreeRTOS_write;
}

// 连接到指定地址和端口
int NetworkConnect(Network* n, char* addr, int port)
{
    // 打开连接并获取套接字描述符
    n->my_socket = transport_open((int8_t*)addr, port);

    // 如果套接字描述符有效(不等于 -1),表示连接成功
    if (-1!= n->my_socket) {
        return 0;
    }

    // 连接失败,返回 -1
    return -1;
}

// 断开网络连接
void NetworkDisconnect(Network* n)
{
    // 如果套接字描述符有效(大于 0)
    if (n->my_socket > 0 ) {
        // 关闭套接字
        closesocket(n->my_socket);
        // 将套接字描述符重置为无效值
        n->my_socket = -1;
    }
}
int32_t transport_open(int8_t* servip, int32_t port)
{
    int32_t *sock = &mysock;
    int32_t ret;
    int32_t opt;
    struct sockaddr_in addr;

    // 初始化服务器信息
    memset(&addr, 0, sizeof(addr));
    addr.sin_len = sizeof(addr);
    addr.sin_family = AF_INET;
    // 填写服务器端口号
    addr.sin_port = PP_HTONS(port);
    // 填写服务器 IP 地址
    addr.sin_addr.s_addr = inet_addr((const char*)servip);

    // 创建套接字
    *sock = socket(AF_INET, SOCK_STREAM, 0);
    // 连接服务器
    ret = connect(*sock, (struct sockaddr*)&addr, sizeof(addr));
    if(ret!= 0)
    {
        // 连接失败,关闭链接
        close(*sock);
        // 返回 -1 表示连接失败
        return -1;
    }
    // 连接成功,设置 TCP_NODELAY 选项(禁用 Nagle 算法)
    opt = 1;
    setsockopt(*sock, IPPROTO_TCP, TCP_NODELAY, (void *)&opt, sizeof(int));

    // 返回套接字描述符
    return *sock;
}

总结

实际使用过程中,遇到了lwip阻塞的阻塞情况,注意采用非阻塞方式进行读取。

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

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

相关文章

LeetCode_sql_day27(1225.报告系统状态的连续信息)

目录 描述&#xff1a; 1225.报告系统状态的连续信息 数据准备&#xff1a; 分析&#xff1a; 代码&#xff1a; 总结&#xff1a; 描述&#xff1a; 1225.报告系统状态的连续信息 表&#xff1a;Failed ----------------------- | Column Name | Type | ----------…

MyBatis动态SQL中的`if`标签使用【后端 19】

MyBatis动态SQL中的if标签使用 引言 MyBatis 是一个优秀的持久层框架&#xff0c;它支持定制化 SQL、存储过程以及高级映射。在 MyBatis 中&#xff0c;动态 SQL 是一个非常强大的特性&#xff0c;它允许你根据不同的条件来动态构建 SQL 语句。if 标签是动态 SQL 中最常用的一…

nginx服务器安装和部署代理

文章目录 Linux下面安装nginx nginx下载官网: [nginx: download](https://nginx.org/en/download.html) 使用yum命令安装gcc环境 yum install -y wget gcc-c pcre-devel zlib-devel openssl-devel//安装多个环境 wget gcc pcre-devel 支持正则表达式 zlib-devel提供了压缩和…

C++:输入输出,字符串,命名空间

1.输出斐波那契数列的前20项 #include <iostream>using namespace std;int main() {int a[20];int i;for(i0;i<20;i){if(i<2){a[i]1;cout<<a[i]<<ends;}else{a[i]a[i-1]a[i-2];cout<<a[i]<<ends;}}return 0; } 2.输入一个字符&#xff0…

Java泛型(“代码模板”,一套代码套用各种类型)

1.什么是泛型 a.定义 i.如果不用泛型定义&#xff0c;在使用ArrayList时需要为每个class编写特定类型代码。 ii.泛型就是定义一种模板&#xff0c;既实现了编写一次&#xff0c;万能匹配&#xff0c;又通过编译器保证了类型安全。 iii.编写模板代码来适应任意…

python调用c++动态链接库,环境是VS2022和vscode2023

目录 前言&#xff1a;配置环境&#xff1a;基础夯实&#xff08;对于ctypes的介绍&#xff09;&#xff1a;1. 加载共享库2. 定义函数原型3. 调用函数4. 处理数据结构5. 处理指针6. 错误处理7. 使用 ctypes.util总结 效果展示&#xff1a;操作步骤(保姆级教学)一在VS中创建dll…

【LGR-200-Div.4】洛谷入门赛 #27 A - H题解,包含(C++, Go语言)

前言&#xff1a; 本文为【LGR-200-Div.4】洛谷入门赛 #27 A - H题解 我只是一个只会各种暴力法的蒟蒻&#xff0c;这场比赛没有参加&#xff0c;是比赛完去写的&#xff0c;但是那个题目昨天晚上才能提交&#xff0c;导致拖久了一点 最后面贴一个Go语言的&#xff0c;反正也没…

前端分段式渲染较长文章

实现思路&#xff1a; 1. 后端返回整篇文章。 2. JavaScript 分段处理&#xff1a;将文章按一定的字符或段落长度分割&#xff0c;然后逐步将这些段落追加到页面上。 3. 定时器或递归调用&#xff1a;使用 setInterval 或 setTimeout 来控制段落的逐步渲染。 代码实现示例 …

2024年企业必备10款源代码加密软件!保护公司源代码 !

随着科技的发展&#xff0c;企业的数据安全性变得愈发重要&#xff0c;特别是源代码——企业的核心资产之一。源代码一旦泄露&#xff0c;可能带来不可估量的损失&#xff0c;导致竞争优势丧失、知识产权泄露&#xff0c;甚至对企业的未来生存造成威胁。因此&#xff0c;确保源…

Centos中关闭swap分区,关闭内存交换

概述&#xff1a; Swap 分区是 Linux 系统中扩展物理内存的一种机制。Swap的主要功能是当全部的RAM被占用并需要更多内存时&#xff0c;用磁盘空间代理RAM内存。Swap对虚拟化技术资源损耗非常大&#xff0c;一般虚拟化是不允许开启交换空间的&#xff0c;如果不关闭Swap&…

城市脉络下的空间句法:整合度与选择度的深度解析

上回写过一篇&#xff0c;基于空间句法的路网整合度、选择度分析&#xff0c;当时碍于篇幅和侧重点&#xff0c;主要讲了如何安装sDNA这个插件来实现路网的整合度、选择度分析&#xff0c;并且分析部分也只是画了几条简单的线段&#xff0c;这次我们深化一下原理和指标的解析&a…

手势识别-Yolov5模型-自制数据集训练

1、源码下载&#xff1a; 大家可以直接在浏览器搜索yolov5即可找到官方链接&#xff0c;跳转进github进行下载&#xff1a; 这里对yolov5模型补充说明一下&#xff0c;它是存在较多版本的&#xff0c;具体信息可在master->tags中查看&#xff0c;大家根据需要下载。这些不同…

2024.9.18 作业

将配置桥接网络的过程整理成文档&#xff0c;发csdn 放资源里了 思维导图&#xff1a;

0基础带你入门Linux之简介

1.Linux和Windows对比 Window很明显的特征就是有C盘、D盘登各种磁盘 我们通过点击不同的盘符&#xff0c;点击里面存储的文件进行查阅的操作 而Linux则很简单&#xff0c;只有一个根目录&#xff0c;也可以说只有一个盘&#xff0c;整个系统所有的东西都是在根目录下的 我们可…

C/C++中哪些数据存放于栈区、堆区、静态区、常量区的详细说明

文章目录 1. 栈区&#xff08;Stack&#xff09;2. 堆区&#xff08;Heap&#xff09;3. 静态区&#xff08;Static&#xff09;4. 常量区&#xff08;Read-Only or Constant Section&#xff09;总结&#xff1a;栈&#xff08;Stack&#xff09;的生长&#xff1a;堆&#xf…

js中apply,call,bind的区别与用法

在JavaScript中&#xff0c;所有的函数再被调用的时候都会默认传入两个参数&#xff0c;一个是this&#xff0c;还有一个是arguments。在默认情况下this都是指当前的调用函数的对象。但是有时候我们需要改变this的指向&#xff0c;也就是说使函数可以被其他对象来调用&#xff…

【数据可视化】Arcgis api 4.x 专题图制作之分级色彩,采用自然间断法(使用simple-statistics JS数学统计库生成自然间断点)

1.效果 2.实现 2.1 分级色彩 分级色彩是在GIS制图中&#xff0c;通过不同颜色等级来表示数据量级差异的符号化方法&#xff0c;帮助用户直观识别和比较数据的大小。 2.2 分级方法 在GIS中进行分级色彩制图时&#xff0c;可以选择不同的分级方法来表示数据的分布和变化&#xf…

好用又便宜的电商分账系统

多部门联合治税的背景下&#xff0c;合规运营是企业的首要任务。确保税务合规不仅能避免法律风险&#xff0c;还能提升企业的信誉和运营效率&#xff0c;电商分账系统是电商行业必备的合规工具。今天&#xff0c;商淘云为您分享选择性价比高的电商分账系统的三大规则&#xff0…

鸿蒙Harmony应用开发,数据驾驶舱登录页面的实现

鸿蒙Harmony应用开发&#xff0c;数据驾驶舱登录页面的实现 ​ 首先我们有个Splash 过渡页面来判断当前是用户是否登录&#xff0c;我们先从preferences中获取token是否存在。如果不存在直接跳转登录即可&#xff0c;如果存在的情况我们再去获取下用户的信息看看token是否过期…