基于uIP和uC/OS-II嵌入式网络开发

——uIP主动发送数据分析

摘要:uIP协议栈简单易用,可以为16位单片机或者是更低级的处理器使用,占用的资源很少,相关移植网上有详细介绍,本文主要讨论uIP如何主动发送数据。所用的开发板是STM32系列的,uC/OS-II操作系统,开发板作为服务器端。

关键字:uIP;uC/OS-II;STM32

1 系统要实现的功能

主要功能如下:

开发板作为服务器端,PC做客户端。连接成功以后,服务器始终作为主动的一方,主动与PC发送数据,PC收到后回传数据帧给服务器。服务器不给PC交互数据时,则认为是空闲的,这时需要以3秒的频率给PC发送心跳,维持连接。

系统主要任务如下:

心跳任务:负责心跳的发送,3秒到了,如果服务器不在给PC交互数据中,则主动发送心跳帧给PC机;

网络数据接收任务:负责轮询的方式接收数据,OSTimeDly(1),1毫秒轮询一次,在这个任务中也处理500毫秒ARP超时和10秒TCP超时。收到数据以后交给协议栈处理,协议栈调用应用程序回调函数tcp_demo_appcall,判断是否新数据到来,若是则调用newdata()。在newdata()函数中处理PC发送过来的数据,并通过发送信号量的方式与主任务交互。

主任务:负责与PC的数据交互,合适的时机主动发送数据给PC机,然后通过信号量等待PC客户端的数据,等到以后再传下一包数据。

2 需要解决的问题

2.1 客户端多次重连

UIP协议栈默认提供20个用于listen状态的tcp结构,10个用于连接状态的tcp结构。这样服务器端可以处理多个客户端,现在考虑的是,服务器端只希望处理一个客户端。而客户端连接服务器的时候,不管现在服务器是否已经有客户端连接了,这样就需要服务器在收到新的客户端连接时,若已经与前一个客户端建立连接,需要先将其断开,然后再与这个新的建立连接。

2.1.1        若客户端主动关闭连接,这样很好理解

先看连接过程,3次握手:

UIP_CLOSED收到客户端的连接SYN,则变为UIP_SYN_RCVD,并给客户端发送SYN+ACK,收到客户端的ACK,则变为UIP_ESTABLISHED,表明建立了连接。

关闭过程:

UIP_ESTABLISHED,收到客户端的FIN,变为UIP_LAST_ACK,并给客户端发送FIN+ACK,

收到客户端的ACK,则变为UIP_CLOSED,标示已经断开了连接。

2.1.2        客户端不断开原来的连接,直接来新连接

鉴于上面的分析,此时有两种办法,在程序回调函数tcp_demo_appcall中,判断是连接,然后调用connected函数。

(1)服务器不管客户端,直接自己释放资源,代码如下:

这是方式是,客户端没有执行断开连接,服务器端也没有执行断开连接,服务器端只是单方面释放了自已占用的资源。

(2)服务器端主动关闭与客户端的连接,然后在与新的客户端连接,主要代码如下:

执行pool推送以后,协议栈调用调用回调函数,进行判断,代码如下:

2.2 主动发送数据

网口接收数据任务,收到数据后,交给协议栈处理。协议栈通过发送信号量的方式与主任务同步,这样主任务就可以发送下包数据。这样的流程,前提是网口接收数据任务优先级低,主任务可以在250毫秒内发送应用数据给客户端,若等待网口接收数据任务将ACK包发送给客户端以后,此时主任务再发送数据就算是主动发送了。

Uip协议栈提供了一个poll功能,可以这种方式主动发送数据,主要代码如下:

需要发送数据时,先调用uip_send将数据拷贝到uip_buf缓存,然后利用pool功能,获取要发送数据的IP首部和TCP首部,最后再组建MAC的14个字节,最终通过调用物理层函数实现数据发送。下面讨论主动发送数据带来的问题。

2.2.1 数据冲撞

发送心跳的任务与主任务都在运行,不能保证两者不发生碰撞,实际测试中存在这样一种状况,心跳数据包刚发送完,250毫秒内客户端没有ACK,但此刻主任务也要发送数据,造成冲撞,如下图所示,分析这一过程。

9118序号:这是发送的一个长度为11的心跳包,tcp块结构的发送序列号是230;

9120序号:183毫秒后主任务发送长度为38的数据帧,但是tcp快结构的发送序列号仍然是230;

9121序号:客户端认为这样的发送有误,TCP ACKed unseen segment。

实际客户端的应用层数据收到的数据是这样的,

FF FF 38 B2 00 00 00 00 80 0A FF

BE A9 B1 B1 BE A9 00 00 00 00 00 00 00 01 20 08 01 01 20 10 12 31 10 02 00 AA FF

第一个包是长度为11的心跳包,但是第二包的长度变为了27,即来到应用层的数据被截断了,协议栈把前面的11个字节给扔掉了。

2.2.2 稍加改进

找到了问题的原因,则试图进行改进,主动发送数据时,自定义变量保存这次要发送的数据长度,然后收到客户端对这包数据的ACK时清0。主动发送数据时,判断之前发送的数据是否都被ACK了,若有数据未被ACK,则将tcp块结构的发送序列号增加,实现的主要代码如下图所示。

使用这种方式随然可以避免数据被截断的问题,但是其它的问题还会出现。我发现的就有两个,一是收不到上位机的数据,非要等上位机重发,这样会造成时间的浪费,如下图所示。

84834:客户端已经发送了数据,但是收不到服务器端的ACK,将近300毫秒后,客户端重发这包数据,此时才收到服务器端的ACK。

二是上位机收不到服务器端的数据,但是服务器也不会超时重传,这样就会卡到这里,如下图所示。

145649:服务器端发送的数据,序列号是36996;

145660:收到客户端的ACK,ACK的是36977;

145567:服务器直接从37015开始发送数据,中间正好丢失一个长度为38的数据,下面客户端反复说明之前的一包没有收到,但是服务器端就是没有重传。

3 总结

由上看出,将UIP移植到操作系统以后,可以使用,但是UIP将发送和接收的数据都保存在一个缓存uip_buf中,且发送和接收数据的长度也都是一个变量len,这样会出现一些问题。对于超时重传等,我没有找到合适的解决办法,考虑到UIP和LWIP是同一个团队编写的,直接采用LWIP开发。

基于uIP和uC/OS-II嵌入式网络开发的更多相关文章

  1. uC/OS II原理分析及源码阅读(一)

    uC/OS II(Micro Control Operation System Two)是一个可以基于ROM运行的.可裁减的.抢占式.实时多任务内核,具有高度可移植性,特别适合于微处理器和控制器,是和 ...

  2. 【原创】uC/OS II 任务切换原理

    今天学习了uC/OS II的任务切换,知道要实现任务的切换,要将原先任务的寄存器压入任务堆栈,再将新任务中任务堆栈的寄存器内容弹出到CPU的寄存器,其中的CS.IP寄存器没有出栈和入栈指令,所以只能引 ...

  3. 【小梅哥SOPC学习笔记】NIOS II处理器运行UC/OS II

    SOPC开发流程之NIOS II 处理器运行 UC/OS II 这里以在芯航线FPGA学习套件的核心板上搭建 NIOS II 软核并运行 UCOS II操作系统为例介绍SOPC的开发流程. 第一步:建 ...

  4. uC/OS II 函数说明 之–OSTaskCreate()与OSTaskCreateExt()

    1. OSTaskCreate()    OSTaskCreate()建立一个新任务,能够在多任务环境启动之前,或者执行任务中建立任务.注意,ISR中禁止建立任务,一个任务必须为无限循环结构.    ...

  5. uc/os iii移植到STM32F4---IAR开发环境

    也许是先入为主的原因,时钟用不惯Keil环境,大多数的教程都是拿keil写的,尝试将官方的uc/os iii 移植到IAR环境. 1.首先尝试从官网上下载的官方移植的代码,编译通过,但是执行会报堆栈溢 ...

  6. 关于uC/OS的简单学习(转)

    1.微内核 与Linux的首要区别是,它是一个微内核,内核所实现的功能非常简单,主要包括: 一些通用函数,如TaskCreate(),OSMutexPend(),OSQPost()等. 中断处理函数, ...

  7. 【RL-TCPnet网络教程】第1章 当前主流的小型嵌入式网络协议栈

    第1章   当前主流的小型嵌入式网络协议栈 这几年物联网发展迅猛,各种新产品.新技术也是层出不穷,本章节就为大家介绍当前主流的小型嵌入式网络协议栈. 1.1  当前主流的嵌入式网络协议栈 1.2  u ...

  8. 【RL-TCPnet网络教程】第2章 嵌入式网络协议栈基础知识

    第2章        嵌入式网络协议栈基础知识 本章教程为大家介绍嵌入式网络协议栈基础知识,本章先让大家有一个全面的认识,后面章节中会为大家逐一讲解用到的协议. 基础知识整理自百度百科,wiki百科等 ...

  9. LwIP协议栈开发嵌入式网络的三种方法分析

    LwIP协议栈开发嵌入式网络的三种方法分析   摘要  轻量级的TCP/IP协议栈LwIP,提供了三种应用程序设计方法,且很容易被移植到多任务的操作系统中.本文结合μC/OS-II这一实时操作系统,以 ...

随机推荐

  1. vsphere client 创建虚拟机 如何关联到本地iso文件

    问题:以前用过vmere 创建虚拟机,都要在虚拟机启动之前配置系统镜像文件,第一次使用vspere client时在创建虚拟机后,我就想着应该先配置,再启动,其实,非也,应该这样,先启动虚拟机,在点按 ...

  2. Git版本控制工具(1)

    学习Git的最佳资料网站: https://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000/ 这 ...

  3. [Java反射基础三]方法反射的基本操作

    本文接上文“获取类的信息”,利用反射(invoke)来获取一个类中的方法来执行. 1.定义一个类,包含三个名称相同,参数不同的方法 class A{ public void print(){ Syst ...

  4. DOM Tree

    DOM Tree   什么是DOM树:网页的所有内容在内存当中,其实是以树形结构存储的 何时创建:当浏览器,读取html中内容的时候,会马上开始创建DOM树. 如何创建: 1.读到HTML的时候还没有 ...

  5. 关于moucedown 的3种触发方式

    与 click 事件不同   mousedown 按下鼠标就可以触发 click 只能用鼠标左键触发, 而mousedown 可以由单击.中键.或右击 触发 根据对event.which 的判断,可以 ...

  6. Qt 日志输出文件

    在Qt开发过程当中经常使用qDebug等一些输出来调试程序,但是到了正式发布的时候,都会被注释或者删除,采用日志输出来代替.     做过项目的童鞋可能都使用过日志功能,以便有异常错误能够快速跟踪.定 ...

  7. jdk1.8 对数组及arrays类对数组的操作与增强

    数组的初始化有两种方式 静态初始化: 初始化时由程序员显示置顶每个数组的初始值,由系统决定数组长度.如: int[] a1 = new int[] {1,2,3,4}; 动态初始化:初始化时由程序员只 ...

  8. SQL Server ->> 间接实现COUNT(DISTINCT XXX) OVER(PARTITION BY YYY)

    SQL Server 2005版本开始支持了窗口函数(Windowing Function)和OVER字句.SQL Server 2012版本开始支持了窗口函数的ORDER BY字句实现连续/累计聚合 ...

  9. C++使用BOOST操作文件、目录

    开始使用 在BOOST库出现之前,C++对于文件和目录的操作,大都借助于UNIX提供的底层文件和目录接口,从使用角度来看,这些底层的操作不够友好.BOOST中filesystem库是一种可移植的文件系 ...

  10. 【2017.09.15 智能驾驶/汽车电子】汽车高级驾驶辅助ADAS常用传感器厂商:激光雷达篇

    不定期更新,主要是汇总Internet上的激光雷达厂商资讯,不涉及技术,以备参考. 1. Innoviz:固态激光雷达公司 新闻链接:http://36kr.com/p/5092055.html 激光 ...