基于uIP和uC/OS-II嵌入式网络开发
基于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嵌入式网络开发的更多相关文章
- uC/OS II原理分析及源码阅读(一)
uC/OS II(Micro Control Operation System Two)是一个可以基于ROM运行的.可裁减的.抢占式.实时多任务内核,具有高度可移植性,特别适合于微处理器和控制器,是和 ...
- 【原创】uC/OS II 任务切换原理
今天学习了uC/OS II的任务切换,知道要实现任务的切换,要将原先任务的寄存器压入任务堆栈,再将新任务中任务堆栈的寄存器内容弹出到CPU的寄存器,其中的CS.IP寄存器没有出栈和入栈指令,所以只能引 ...
- 【小梅哥SOPC学习笔记】NIOS II处理器运行UC/OS II
SOPC开发流程之NIOS II 处理器运行 UC/OS II 这里以在芯航线FPGA学习套件的核心板上搭建 NIOS II 软核并运行 UCOS II操作系统为例介绍SOPC的开发流程. 第一步:建 ...
- uC/OS II 函数说明 之–OSTaskCreate()与OSTaskCreateExt()
1. OSTaskCreate() OSTaskCreate()建立一个新任务,能够在多任务环境启动之前,或者执行任务中建立任务.注意,ISR中禁止建立任务,一个任务必须为无限循环结构. ...
- uc/os iii移植到STM32F4---IAR开发环境
也许是先入为主的原因,时钟用不惯Keil环境,大多数的教程都是拿keil写的,尝试将官方的uc/os iii 移植到IAR环境. 1.首先尝试从官网上下载的官方移植的代码,编译通过,但是执行会报堆栈溢 ...
- 关于uC/OS的简单学习(转)
1.微内核 与Linux的首要区别是,它是一个微内核,内核所实现的功能非常简单,主要包括: 一些通用函数,如TaskCreate(),OSMutexPend(),OSQPost()等. 中断处理函数, ...
- 【RL-TCPnet网络教程】第1章 当前主流的小型嵌入式网络协议栈
第1章 当前主流的小型嵌入式网络协议栈 这几年物联网发展迅猛,各种新产品.新技术也是层出不穷,本章节就为大家介绍当前主流的小型嵌入式网络协议栈. 1.1 当前主流的嵌入式网络协议栈 1.2 u ...
- 【RL-TCPnet网络教程】第2章 嵌入式网络协议栈基础知识
第2章 嵌入式网络协议栈基础知识 本章教程为大家介绍嵌入式网络协议栈基础知识,本章先让大家有一个全面的认识,后面章节中会为大家逐一讲解用到的协议. 基础知识整理自百度百科,wiki百科等 ...
- LwIP协议栈开发嵌入式网络的三种方法分析
LwIP协议栈开发嵌入式网络的三种方法分析 摘要 轻量级的TCP/IP协议栈LwIP,提供了三种应用程序设计方法,且很容易被移植到多任务的操作系统中.本文结合μC/OS-II这一实时操作系统,以 ...
随机推荐
- 前端小结(5)---- iframe
iframe对应的div: <div id="iframezone"> <iframe id="iframe" frameborder='0' ...
- log4j的简单使用
引入jar包org.apache.log4j.Logger,项目src目录下建立一个log4j.properties配置文件 log4j.rootLogger=INFO,A1,R log4j.appe ...
- centos jdk8 install
0. 装了好多遍jdk 以前一直是 vi /etc/profile文件 追加 环境变量 ,仔细一看 profile文件提示 尽量不要修改!!! so... 1. 下载jdk8.tar.gz 2. 卸载 ...
- 2-3 Sass的函数功能-列表函数
列表函数主要包括一些对列表参数的函数使用,主要包括以下几种: length($list):返回一个列表的长度值: nth($list, $n):返回一个列表中指定的某个标签值 join($list1, ...
- .NET开源工作流RoadFlow-流程运行-运行时监控
流程实例参与者都可以随时查看流程实例的运行情况,如果是待办任务,则在待办事项列表的后面点查看,如果是已完成任务则可以在已办事项列表的后面点查看: 打开之后默认为列表方式显示流程的处理过程,还可以点图形 ...
- 139.00.006 Git学习-标签管理Tag
@(139 - Environment Settings | 环境配置) 一.Why 发布一个版本时,我们通常先在版本库中打一个标签(tag),这样,就唯一确定了打标签时刻的版本.将来无论什么时候,取 ...
- 【Udacity笔记】What is Machine Learning?
Teaching computers to learn to perform tasks from past experiences(recorded data) 一.Decision Tree(决策 ...
- Javascript之深入理解闭包
闭包算是js里面比较不容易理解的点,尤其是对于没有编程基础的人来说. 其实闭包要注意的就那么几条,如果你都明白了那么征服它并不是什么难事儿.下面就让我们来谈一谈闭包的一些基本原理. 闭包的概念 一个闭 ...
- qt 创建资源文件
我们编写的gui可能需要一些额外的资源(比如贴图用的图片),可用资源文件统一管理.以下以图片为例. 用qt creator 打开工程,为工程新建资源文件: 命名为“项目名.prc”,(此处为“cloc ...
- 比特币钱包Armory使用指南
转自:http://news.yibite.com/application/2014/0525/21603.shtml 比特币钱包Armory(https://bitcoinarmory.com/)是 ...