基于Lwip协议栈中独立模式下回调函数的使用
一、使用Lwip协议独立模式开发
最近在STM32F4上边移植了Lwip,Lwip是一个小型开源的TCP/IP协议栈,有无操作系统的支持都可以运行。我当前只测试了TCP Server功能,然后对TCP Server在Lwip协议栈的运行进行了分析。Lwip协议栈提供了有三种API,Raw API使用独立模式来开发,Netconn API、Socket API是使用实时操作系统(RTOS)进行多线程来开发,由于我是没有跑操作系统,所以使用独立模式的工作模型,这种工作模型的运行机制是基于轮询模式不停地检查是否收到数据包。

Lwip使用事件回调机制与应用层通信,因此,应在进行通信之前,对相关事件注册回调函数。
二、Lwip协议栈中TCP的应用
对于TCP的应用需要使用以下的TCP Raw API函数接口:

我是使用正点原子的例程来调试的:
//TCP Server 测试
void tcp_server_test(void)
{
err_t err;
struct tcp_pcb *tcppcbnew; //定义一个TCP服务器控制块
struct tcp_pcb *tcppcbconn; //定义一个TCP服务器控制块
u8 *tbuf;
u8 key;
u8 res=0;
u8 t=0;
u8 connflag=0; //连接标记
printf("Explorer STM32F4 \r\n");
printf("TCP Server Test \r\n");
printf("ATOM@ALIENTEK \r\n");
printf("KEY0:Send data \r\n");
printf("KEY_UP:Quit \r\n");
tbuf=mymalloc(SRAMIN,200); //申请内存
if(tbuf==NULL)return ; //内存申请失败了,直接退出
sprintf((char*)tbuf,"Server IP:%d.%d.%d.%d",lwipdev.ip[0],lwipdev.ip[1],lwipdev.ip[2],lwipdev.ip[3]);//服务器IP
printf("Server IP:%d.%d.%d.%d\r\n",lwipdev.ip[0],lwipdev.ip[1],lwipdev.ip[2],lwipdev.ip[3]);
sprintf((char*)tbuf,"Server Port:%d",TCP_SERVER_PORT);//服务器端口号
printf("Server Port:%d\r\n",TCP_SERVER_PORT);
tcppcbnew=tcp_new(); //创建一个新的pcb
if(tcppcbnew) //创建成功
{
err=tcp_bind(tcppcbnew,IP_ADDR_ANY,TCP_SERVER_PORT); //将本地IP与指定的端口号绑定在一起,IP_ADDR_ANY为绑定本地所有的IP地址
if(err==ERR_OK) //绑定完成
{
tcppcbconn=tcp_listen(tcppcbnew); //设置tcppcb进入监听状态
tcp_accept(tcppcbconn,tcp_server_accept); //初始化LWIP的tcp_accept的回调函数
}else res=1;
}else res=1;
while(res==0)
{
key=KEY_Scan(0);
//if(key==WKUP_PRES)break;
if(key==KEY0_PRES)//KEY0按下了,发送数据
{
tcp_server_flag|=1<<7;//标记要发送数据
}
if(tcp_server_flag&1<<6)//是否收到数据?
{
printf("tcp_server_recvbuf=%s\r\n",tcp_server_recvbuf);
tcp_server_flag&=~(1<<6);//标记数据已经被处理了.
}
if(tcp_server_flag&1<<5)//是否连接上?
{
if(connflag==0)
{
sprintf((char*)tbuf,"Client IP:%d.%d.%d.%d",lwipdev.remoteip[0],lwipdev.remoteip[1],lwipdev.remoteip[2],lwipdev.remoteip[3]);//客户端IP
printf("Client IP:%d.%d.%d.%d \r\n",lwipdev.remoteip[0],lwipdev.remoteip[1],lwipdev.remoteip[2],lwipdev.remoteip[3]);
printf("Receive Data: \r\n");
connflag=1;//标记连接了
}
}else if(connflag)
{
connflag=0; //标记连接断开了
}
lwip_periodic_handle();
delay_ms(2);
t++;
if(t==200)
{
t=0;
LED0=!LED0;
}
}
tcp_server_connection_close(tcppcbnew,0);//关闭TCP Server连接
tcp_server_connection_close(tcppcbconn,0);//关闭TCP Server连接
tcp_server_remove_timewait();
memset(tcppcbnew,0,sizeof(struct tcp_pcb));
memset(tcppcbconn,0,sizeof(struct tcp_pcb));
myfree(SRAMIN,tbuf);
}
其中TCP连接建立后进入监听进行后注册了接收的回调函数tcp_accept(tcppcbconn,tcp_server_accept);,tcp_server_accept是回调的函数,程序源码如下:
//lwIP tcp_accept()的回调函数
err_t tcp_server_accept(void *arg,struct tcp_pcb *newpcb,err_t err)
{
err_t ret_err;
struct tcp_server_struct *es;
LWIP_UNUSED_ARG(arg);
LWIP_UNUSED_ARG(err);
tcp_setprio(newpcb,TCP_PRIO_MIN);//设置新创建的pcb优先级
es=(struct tcp_server_struct*)mem_malloc(sizeof(struct tcp_server_struct)); //分配内存
if(es!=NULL) //内存分配成功
{
es->state=ES_TCPSERVER_ACCEPTED; //接收连接
es->pcb=newpcb;
es->p=NULL;
tcp_arg(newpcb,es);
tcp_recv(newpcb,tcp_server_recv); //初始化tcp_recv()的回调函数
tcp_err(newpcb,tcp_server_error); //初始化tcp_err()回调函数
tcp_poll(newpcb,tcp_server_poll,1); //初始化tcp_poll回调函数
tcp_sent(newpcb,tcp_server_sent); //初始化发送回调函数
tcp_server_flag|=1<<5; //标记有客户端连上了
lwipdev.remoteip[0]=newpcb->remote_ip.addr&0xff; //IADDR4
lwipdev.remoteip[1]=(newpcb->remote_ip.addr>>8)&0xff; //IADDR3
lwipdev.remoteip[2]=(newpcb->remote_ip.addr>>16)&0xff; //IADDR2
lwipdev.remoteip[3]=(newpcb->remote_ip.addr>>24)&0xff; //IADDR1
ret_err=ERR_OK;
}else ret_err=ERR_MEM;
return ret_err;
}
从代码中可以看出回调函数tcp_server_accept里边又注册了四个回调函数。
tcp_recv(newpcb,tcp_server_recv); //初始化tcp_recv()的回调函数
tcp_err(newpcb,tcp_server_error); //初始化tcp_err()回调函数
tcp_poll(newpcb,tcp_server_poll,1); //初始化tcp_poll回调函数
tcp_sent(newpcb,tcp_server_sent); //初始化发送回调函数
只分析tcp_recv回调函数,另外三个也一样的查找方法。回调函数tcp_server_recv进入之后是接收到的数据处理,但是在程序中只是初始化一遍,到底是什么地方有调用,再进行注册的回调函数发现回调函数给函数指针pcb->recv赋值,也就是pcb->recv指向了回调函数tcp_server_recv,在循环的程序中只要找到调用pcb->recv就会对接收到的数据进行处理。
void tcp_recv(struct tcp_pcb *pcb, tcp_recv_fn recv)
{
LWIP_ASSERT("invalid socket state for recv callback", pcb->state != LISTEN);
pcb->recv = recv;
}
在程序的搜索pcb->recv,可得出调用的地方:
#define TCP_EVENT_RECV(pcb,p,err,ret) \
do { \
if((pcb)->recv != NULL) { \
(ret) = (pcb)->recv((pcb)->callback_arg,(pcb),(p),(err));\
} else { \
(ret) = tcp_recv_null(NULL, (pcb), (p), (err)); \
} \
} while (0)
也就是使用TCP_EVENT_RECV宏定义的地方会去调用pcb->recv,TCP_EVENT_RECV是在最底层的(在tcp层)就是tcp_input中调用,所以从后往前推可以调用了在应用层回调函数:tcp_input() -> * tcp_process() -> tcp_receive() (-> application)。
而tcp_input被IP层调用的。

我只能写这么多,对协议的理解还不是很透彻,还没有深入去跟协议里边的东西,只是理解了TCP层的运行,而IP层还没有去跟进。但知道理解LWIP协议独立工作模式用回调函数的机制来与应用层进行通信可以在后面解析其它的协议的理解会有帮助。
三、参考文档
http://blog.csdn.net/houqi02/article/details/52205311
http://blog.csdn.net/morixinguan/article/details/65494239
http://blog.csdn.net/zbychhaozeng/article/details/6561490
by 羊羊得亿
2017-11-03 ShenZhen
基于Lwip协议栈中独立模式下回调函数的使用的更多相关文章
- python中进程池和回调函数
一.数据共享 1.进程间的通信应该尽量避免共享数据的方式 2.进程间的数据是独立的,可以借助队列或管道实现通信,二者都是基于消息传递的. 虽然进程间数据独立,但可以用过Manager实现数据共享,事实 ...
- Unity C# 调用 C++ DLL 并在 DLL 中调用 C# 的回调函数
Unity C# 调用 C++ DLL 并在 DLL 中调用 C# 的回调函数~~~ 呵呵... 看着有点晕.. 再解释一下就是 在Unity中 使用 C# 调用 C++ 写的 DLL, 但是在 ...
- 基于Frida框架打造Art模式下的脱壳工具(OpenMemory)的原理分析
本文博客地址:https://blog.csdn.net/QQ1084283172/article/details/80956614 作者dstmath在看雪论坛公布一个Android的art模式下基 ...
- vs2010中release模式下调试程序
debug模式调试信息全,但是速度很慢,在数据量比较大的时候非常影响调试效率,release模式速度快,但是没有调试信息.所以在编译的时候很多编译器会提供一种折中的编译方式,在release下提供调试 ...
- vue组件中—bus总线事件回调函数多次执行的问题
在利用vue组件进行事件监听时发现,如果对N个vue组件实例的bus总线绑定同一事件的回调函数,触发任意组件的对应事件,回调函数至少会被执行N次,这是为什么呢? 为此,调研了普通对象的事件绑定和触发实 ...
- CXF 入门:创建一个基于WS-Security标准的安全验证(CXF回调函数使用,)
http://jyao.iteye.com/blog/1346547 注意:以下客户端调用代码中获取服务端ws实例,都是通过CXF 入门: 远程接口调用方式实现 直入正题! 以下是服务端配置 ==== ...
- 关于as中的事件与回调函数
对于Observer模式, 在as中object(被观察者)既可以用事件(event),也可以用回调函数(caller)来通知观察者(observer).那在实际的开发中到底应该选择用event还是用 ...
- PHP – 在类中使用array_filter时回调函数的问题
了一个类处理好友,其中有一个方法用来同步好友,而这个方法中需要从微博传来的关注列表和粉丝列表中,找到互相关注的用户,记录一下经验,主要还是关于回调函数. 按照我最初的理解,这样写就可以了 privat ...
- CentOS 6.5安装在VMWare中Bridge模式下网卡eth0不能自动激活的问题
VMWare 12.5.2 CentOS 6.5 basic VMWare网卡配置选择Bridge方式 问题: 默认情况下ifconfig命令只能看到网络设备lo,看不到eth0,也没有分配合理的IP ...
随机推荐
- 我持续推动Rust语言支持Windows XP系统
前言 Rust好像长期以来不支持Windows XP系统.有不少用户发帖提议官方支持XP,基本上都被Rust官方开发人员明白的拒绝了.他们的对话大致上是以这种形式開始和结束的(当中乙方代表官方及其拥趸 ...
- .NET进阶——ORM基础认识
ORM对象关系映射,对象即实体,关系即关系数据库表.ORM即实现从实体对象关系数据库数据的映射. 本质上就是将数据从一种形式转换到还有一种形式.它详细又有哪些表现呢.咱们一步步学习,一步步分析. OR ...
- powerpoint(ppt) 的制作
1. 幻灯片母版 所谓母版,是共享的部分,也即想在全部幻灯片重复出现的元素. 首先,幻灯片母版在菜单栏的[视图]选择[幻灯片母版]显示和查看. 通过幻灯片母版的编辑和设计,可进一步: 在幻灯片的相关位 ...
- 28. Brackets安装angularjs插件
Brackets是Adobe公司研发的一款开源WEB前端开发框架,界面清爽简约,代码提示功能比较强大,而且支持第三方插件,其提供的插件库中有大量的对Brackets感兴趣的开发人员所开发的插件,使用者 ...
- VS初始化设置
来源于网上整理和 书<aps.net mvc企业级实战>中. 1.vs模版 版权注释信息 1.我的电脑上VS2015安装在D盘中,所以找的目录为:D:\Program Files (x86 ...
- 分享一个正则 选择html中所有的单标签
var str = /\B<.+?>/g;
- 【Uva 1630】Folding
[Link]: [Description] 你能对字符串进行压缩的操作; 即把连续出现的相同的子串改成它出现的次数+这个最基本的字符串的形式; 问你这个字符串最短能被压缩得多短; [Solution] ...
- 菜鸟之路——Java并发(二)ThreadLocal
一.什么是ThreadLocal ThreadLocal,非常多地方叫做线程本地变量,也有些地方叫做线程本地存储.事实上意思几乎相同.非常多博客都这样说:ThreadLocal为解决多线程程序 ...
- 发送邮件被退回,提示: Helo command rejected: Invalid name 错误
我自己配置的 postfix + dovecot server, 配置了outlook 后, 相同的账号. 在有的电脑上能收发成功, 在有的电脑上发送邮件就出现退信.提示 Helo command r ...
- socket编程之中的一个:计算机网络基础
在開始学习网络之前先复习下计算机网络基础吧. 鲁迅说,天下文章一大抄.看你会炒不会炒,基础知识就抄抄书吧. 一 分层模型 1 为什么分层 为了简化网络设计的复杂性.通讯协议採用分层结构.各层协议之间既 ...