使用LwIP回调编程实现paho开源MQTT库的移植

文章内容为工程调试记录,内容排版散乱,阅读慎入!

LwIP Raw回调编程调试记录

1. tcp_connect连接问题:

1.1 连接不上,没有任何的回调函数调用,延时可以偶尔的解决这个问题

解决思路:在软件定时器中定时检测TCP_PCB块的状态

1.2 服务器主动断开的话,在tcp_recv_cb中会收到无数据响应

此处判断其状态应该是已经断开

1.3 tcpip_callback做何用,屏蔽之后带来的影响

该函数往tcpip线程中发送了一条邮箱,tcpip线程收到之后,调用传入的回调函数将当前申请的tpcb加入到tcp_tw_pcbs链表中,并在tcpip进程中注册一个定时器事件作为当前pcb连接的内核定时器。通过后续的分析,发现当前tpcb建立连接后,连接成功回调函数立马将当前tpcb从tcp_tw_pcbs中移除。可见,其本意是通过tcpip回调的方式为当前tpcb开启一个内核定时器。因为在普通进程或函数中调用的tcp_connect虽然也有TCP_REG的调用,但却是把当前tpcb的软件定时器事件挂在到当前线程的软件定时器链表上,如果当前调用tcp_connect的进程不是通过sys_thread_new建立的,或者就是个普通函数,这时候连软件定时器链表也找不到,自然也没有定时事件的触发,这也是困扰我多日为什么tpcb的内核定时器没有启动的原因。

1.4 2个软件定时器分别负责断开检测及重发

工程自带有ttcp.c例程用于测试raw tcp,而自己也主要参考该例程编写,此处困扰我多日的一点是:采用了软件定时器来处理数据的续发。因为数据可能无法一次性传完,ttcp.c中采用了软件定时器来发送当前要发送数据的剩余数据,在实际测试中大多数时候是没问题的,但是参照该种方式,却时不时的出现数据无法发送。调试发现是发送缓存区满,抓包发现是服务器不停的回复一个ACK值,而lwip客户端没有重发,经过老衲五木的提醒,意识到lwip中tcp内核的数据处理是在一个进程中完成的,频繁的触发定时器来执行tpc_write操作,可能打乱tcp内核的执行顺序,出现上述问题。改良后的版本使用tcp_sent来续发剩余数据,只用一个软件定时器来检测tpcb的状态。

1.5. 建立连接及未连接的状态

建立连接: Current PCB:2000b3f8, state = ESTABLISHED

一直未连接成功: Current PCB:2000b3f8, state = SYN_SENT

服务器断开:

其它状态

连接不上,偶尔会回调tcp_err中的回调函数,需要标识该状态,等待定时器的处理

2. 接收到的数据异常

犯了个低级的错误,将之前的代码

mqtt_comm_p->recvlen = p->tot_len;

for ( data_p = mqtt_comm_p->recvbuf, q = p;  q->next != NULL; q = q->next )

{

memcpy(data_p, q->payload, q->len);

data_p += q->len;

}

改成如下代码

mqtt_comm_p->recvlen = p->tot_len;

data_p = mqtt_comm_p->recvbuf;

q = p;

while ( q != NULL )

{

memcpy(data_p, q->payload, q->len);

data_p += q->len;

q = q->next;

}

3. 数据无法立即发送,接收数据后才会发送出去

在调用tcp_write之后,数据并不会立即发送出去,紧随其后调用tcp_ouput会将挂在的数据发送出去。在当前写的发送函数中,没有选择使用tcp_output,因为tpcb的内核定时器会定时调用它。

4. 数据发送的处理

最初的想法:使用队列来完成数据的填充与发送。这将使得数据的发送效率降低,因为涉及到数据入队列的复制,数据出队列的复制,以及数据拷贝到发送队列的复制。

新的改进:无队列,无发送缓冲区,使用信号灯及指针来处理发送。这也是不违背为了节省内存的初衷。

5. 花费一天时间寻找两个HardFault:

5.1 buf指向了常驻内存区,之后又指向了静态内存区域

//vPortFree(RtCtrlPkgAck);

//vPortFree(PicInfoPkg);

//vPortFree(buf);

释放出错,

5.2 订阅主题初始化不完全

mqtt_comm_p->MQTTPkgSubs->subcount = 1;  //  只有一个订阅主题

// 之前初始化循环值为mqtt_comm_p->MQTTPkgSubs->subcount,也就是1而不是4,导致后面要用的主题名称topicString内部变量未初始化,拷贝数据内存硬件错误 。

for (i = 0; i < 4 ; i++)

{

mqtt_comm_p->MQTTPkgSubs->topicString[i].cstring = NULL;

mqtt_comm_p->MQTTPkgSubs->topicString[i].lenstring.data = NULL;

mqtt_comm_p->MQTTPkgSubs->topicString[i].lenstring.len = 0; // MQTTString_initializer

mqtt_comm_p->MQTTPkgSubs->QoSs[i] = 0; // 服务质量

教训:内存的分配和释放处理,一定要小心小心再小心;要养成变量初始化的好习惯。

6. 软件定时器中创建的tpcb控制块,使用tcp_write无法发送数据

6.1 tpcb块的tcp_connect操作中有TCP_REG调用,其本意是注册当前tpcb到tcp active链表中,再在tcpip超时链表中创建一个超时定时器。但是tcp_connect并不在tcpip中调用,也就无法在tcpip超时链表中创建定时器,tpcb内核定时器自然无法启动,数据无法发送,这也是为什么需要tcpip_callback(tcp_connect_timer_cb,
mqtt_comm_p->tpcb); 的原因;

改进:可以不在外部程序中调用tcpip_callback,而是直接tcp_connect中的TCP_REG(); 替换成tcpip_callback,不过这改动了内核的代码,慎重。

tpcb断开后,谨慎起见,也需要TCP_RMV删除,防止下次无法创建新的TCP内核定时器sys_timeout。

6.2 不使用软件定时器来触发重发,以免打乱tcp内核的数据处理,改用tcp_sent完成剩余数据发送。

6.3 以上2点原因得益于老衲五木的提点,在此非常感谢~。

7. LwIP raw tcp发送程序参考:

/ MQTT 数据发送函数
static OSStatus tcp_mqtt_send(struct mqtt_comm_t *mqtt_comm_p, uint8_t data[], uint32_t len, int msBlock, uint8_t apiflag)
{
    mqtt_comm_p->sendbuf = data;
    mqtt_comm_p->sendlen = len;
    mqtt_comm_p->apiflags = apiflag; 

    sys_log("tcp_mqtt_send,sendlen: %d.\r\n", mqtt_comm_p->sendlen);

    tcp_send_data(mqtt_comm_p);

    if( xSemaphoreTake(mqtt_comm_p->sendsema, msBlock) != pdTRUE )
    {
        sys_log("tcp send timeout.\r\n");
        return TCP_MQTT_SEND_ERR;
    }

   if (mqtt_comm_p->sendlen != 0)
      return TCP_MQTT_SEND_ERR;
   else
      return NoErr;
}

// TCP 数据发送函数
void tcp_send_data(struct mqtt_comm_t *mqtt_comm_p)
{
    err_t err;
    uint32_t len;

    len = mqtt_comm_p->sendlen;
    len = (len > tcp_sndbuf(mqtt_comm_p->tpcb) ) ?  tcp_sndbuf(mqtt_comm_p->tpcb) : len;

    // 连接断开
    if ( (mqtt_comm_p->tpcb == NULL ) || (mqtt_comm_p->tpcb->state != ESTABLISHED) )
    {
        xSemaphoreGive(mqtt_comm_p->sendsema);
        sys_log("tcp_send_data: tpcb error.\r\n");
        return;
    }

    // 发送缓冲区满
    if(tcp_sndbuf(mqtt_comm_p->tpcb) == 0)
    {
        vTaskDelay(1);
        return;
    } 

    do
    {
        err = tcp_write(mqtt_comm_p->tpcb, mqtt_comm_p->sendbuf, len, mqtt_comm_p->apiflags);
        if (err == ERR_MEM)
            len /= 2;
    }
    while (err == ERR_MEM && len > 1);

    if (err == ERR_OK)
    {
        mqtt_comm_p->sendlen -= len;
        mqtt_comm_p->sendbuf += len;
    }
    else
        sys_log("[tcp_send_data]: tcp_write failed.\r\n");
}

err_t tcp_send_cb(void *arg, struct tcp_pcb *tpcb, u16_t len)
{
  struct mqtt_comm_t *mqtt_comm_p = arg;

  if ( mqtt_comm_p->sendlen > 0 )
  {
    tcp_send_data(mqtt_comm_p);
  }
  else
  {
    xSemaphoreGive(mqtt_comm_p->sendsema);
  }

  return ERR_OK;
}

MQTT移植记录

未完待续

时间: 2025-01-31 08:57:37

使用LwIP回调编程实现paho开源MQTT库的移植的相关文章

Paho -物联网 MQTT C Cient的实现和详解

概述   在文章Paho - MQTT C Cient的实现中,我介绍了如何使用Paho开源项目创建MQTTClient_pulish客户端.但只是简单的介绍了使用方法,而且客户端的结果与之前介绍的并不吻合,今天我就结合新的例子,给大家讲解一下Paho使用MQTT客户端的主要过程.  如同前面介绍的,MQTT客户端分为同步客户端和异步客户端.今天主要讲解的是同步客户端,结构还是如同步客户端中介绍的:   1.创建一个客户端对象:  2.设置连接MQTT服务器的选项:  3.如果多线程(异步模式)

有哪些适合学生参与的 C++,网络编程方面的开源项目?

有哪些适合学生参与的 C++,网络编程方面的开源项目? Tinyhttpd是一个超轻量型Http Server,使用C语言开发,全部代码只有502行(包括注释),附带一个简单的Client,可以通过阅读这段代码理解一个 Http Server 的本质.下载链接链接:LippiOuYang/Tinyhttpd · GitHub nginx: download高性能web服务器 libevent/libevent · GitHubC语言写的事件驱动框架 ACE:C++面向对象网络编程工具包 Boos

iOS网络编程(7) 第三方开源库-----&gt;AFNetworking

AFNetworking是一个为 iOS 和 Mac OSX 制作的令人愉快的网络库,它建立在URL 装载系统框架的顶层,内置在Cocoa里,扩展了强有力的高级网络抽象.它的模块架构被良好的设计,拥有丰富的功能,因此,使用起来,必定赏心悦目. @原文链接https://github.com/AFNetworking/AFNetworking,我在此基础上了点配置修改 @介绍 1.支持HTTP请求和基于REST的网络服务(包括GET.POST. PUT.DELETE等) 2.支持ARC 3.要求i

【Android开源项目解析】RecyclerView侧滑删除粒子效果实现——初探Android开源粒子库 Leonids

前两天在微博上看到了这个侧滑删除的粒子效果,但是只有IOS的,所以心血来潮,写了个玩玩,下面简单介绍下实现的思路 项目简介 实现原理解析 代码实现 如何使用 更多参考 项目简介 先不废话,上效果图 项目地址:https://github.com/ZhaoKaiQiang/ParticleLayout 实现原理解析 其实看了那么多的关于侧滑删除的项目,再来思考这个问题,就so easy了! 咱们先分析下需求: - 侧滑手势检测 - 粒子跟手效果 - 删除状态判断 - 数据源刷新 ok,知道需求了,

Android开源工具库

 一.依赖注入DI 通过依赖注入减少View.服务.资源简化初始化,事件绑定等重复繁琐工作 1. AndroidAnnotations(Code Diet) android快速开发框架 项目地址:https://github.com/excilys/androidannotations 文档介绍:https://github.com/excilys/androidannotations/wiki 官方网站:http://androidannotations.org/ 特点:(1)依赖注入:包

android开源图表库MPAndroidChart文档翻译(上)

MPAndroidChart 是 Android 系统上一款开源的图表库.目前提供线图和饼图,支持选择.缩放和拖放. android开源图表库MPAndroidChar的githu地址: https://github.com/PhilJay/MPAndroidChart 文档地址:https://github.com/PhilJay/MPAndroidChart/wiki API地址:https://jitpack.io/com/github/PhilJay/MPAndroidChart/v2.

android开源图表库MPAndroidChart文档翻译(中)

在 android开源图表库MPAndroidChart文档翻译(上) 中,介绍了mpandroidchart的创建,回调方法,还有轴.这篇文章继续翻译剩余内容.文档内容比较多,这是中篇.最后的内容在下篇做翻译. 六.设置数据 要给图表设置数据,调用的方法为 public void setData(ChartData data) { ... } ChartData是渲染过程中所需图表所有数据和信息的基类.对于每种图表,有不同的子类用来设置特定图表的数据.例如LineData.可以用ArrayLi

C++100款开源界面库[转]

(声明:Alberl以后说到开源库,一般都是指著名的.或者不著名但维护至少3年以上的.那些把代码一扔就没下文的,Alberl不称之为开源库,只称为开源代码.这里并不是贬低,像Alberl前面那个系列的教程<2013 duilib入门简明教程>,还有本系列教程,还有前面介绍的CodeProject,基本上都是代码往上面一扔,就不用再怎么维护的.这些都称之为开源代码,其实开源代码对新手的帮助更大,因为很简明的说明了代码用法~O(∩_∩)O~) 前面两个教程已经对制作界面的几种方式进行了比较,权威界

十大开源Swift库开始你的下一个iOS项目

随着Swift变得更加成熟,开源Swift库变得越来越多样化并且数量迅速增长.在本文中,我策划了可用于引导您的下一个iOS项目的最佳开源Swift库.它们将使您的代码库更加强大,性能更高,并允许您 更快地发布iOS项目. 事实上,在我的应用中,我集成了几乎所有这些开源库,如SnapKit,MapKit,ChartKit等.基于我的移动开发经验,今天我将介绍最知名和最常用的iOS库,在MIT许可下免费,在Github上. 1. Alamofire 对于服务器通信,除了使用iOS的内置类  URLS