基于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 ...
随机推荐
- @Html.Raw() 方法输出带有html标签的字符串
@Html.Raw() 方法输出带有html标签的字符串,如:@Html.Raw("<div style='color:red'>输出字符串</div>") ...
- tomcat加载web.xml
这几天看tomcat的源码,疑问很多,比如之一“ tomcat 怎么加载 web.xml”,下面是跟踪的过程,其中事件监听器有一个观察者模式,比较好.记录下来以供参考 >>>> ...
- python 多线程探索
前面已经了解过了,python多线程效率较低的主要原因是存在GIL,即Global Interpreter Lock(全局解释器锁).这里继续详细的看下GIL的说明与如何避免GIL的影响,从而提高py ...
- 开发板ping得通本地,但是不能上网
在3531D的开发板上面接入LAN7500的USB转以太网口,加载完驱动后. 然后开启eth1 查看一下ifconfig,发现没有ip,配置一下ip 直接ping本地发现可以ping得通,代表链路是连 ...
- 脚本实现自动化安装lamp&lnmp
#备注:前提是将lnmp和lnmp自动化脚本写好放在相应的路径, 脚本已写好,请查看我博客中的 shell脚本 专栏! #!/bin/bash #安装lamp或者lnmp path=/server/s ...
- web开发快速提高工作效率的一些资源
前端学习资源实在是又多又广,在这样的一个知识的海洋里,我们像一块海绵一样吸收,想要快速提高效率,平时的总结不可缺少,以下总结了一些,排版自我感觉良好,推送出来,后续持续跟新中...... 开发工具 H ...
- SpringMVC框架的多表查询和增删查改
必须声明本文章==>http://www.cnblogs.com/zhu520/p/7883268.html 一: 1):我的运行环境 我使用myeclipse(你也可以使用eclipse),t ...
- JAVA基础针对自己薄弱环节总结02(循环)
循环 A:水仙花. classShuiXianHua { public static void main(String[] args) { for(int i=101;i<1000;i++) { ...
- 5.cocos2d锚点
创建一个层T1LayerAnchorPoint AppDelegate.cpp bool AppDelegate::applicationDidFinishLaunching() { // initi ...
- 10款最好的Python IDE
Python 的学习过程少不了集成开发环境(IDE)或者代码编辑器.这些 Python 开发工具帮助开发者加快使用 Python 开发的速度,提高效率.高效的代码编辑器或者 IDE 应该会提供插件,工 ...