基于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 ...
随机推荐
- 为OLED屏添加GUI支持2:2D图形库
为OLED屏添加GUI支持2:2D图形库 本文博客链接:http://blog.csdn.net/jdh99,作者:jdh,转载请注明. 环境: 主机:WIN10 开发环境:MDK5.13 MCU:S ...
- SQL Source Control
https://documentation.red-gate.com/display/SOC5/SQL+Source+Control+5+documentation Working with migr ...
- 6.控制器(ng-Controller)
转自:https://www.cnblogs.com/best/tag/Angular/ ngController指令给视图添加一个控制器,控制器之间可以嵌套,内层控制器可以使用外层控制器的对象,但反 ...
- 关于es6中对象的扩展
1.Object.is() es5比较两个值是否相等,只有两个运算符,相等(==) 和 严格相等(===),他们都有缺点,前者会自动转换数据类型,后者的NaN不等于自身,以及+0 等于 -0.es6提 ...
- Android控件-单选按钮RadioButton
RadioGroup单选按钮用法,还是先看效果图 先中后,点RadioGroup测试按钮,可在标题栏显示选择结果,点清除可以清除选择.下面上代码,main.xml: <RadioGroup an ...
- axure中使用HighCharts模板制作统计图表
一. 步骤: 1.在axure中新建页面,发布并生成html文件: 2.将HighCharts文件夹,拷贝到生成的html文件中: 3.拖拽“内部框架组件”到界面中 4.双击界面中的内部框架,设置链接 ...
- 解决电信或网通的DNS劫持
大家有没有碰到访问一些不存在域名或者网站时,浏览器本应显示一个网址不存在之类的信息,但是因为现在很多ISP做了DNS劫持将不存在的域名或网址重定向到ISP的广告页面,烦人的狠.其实tomato可以解决 ...
- 海思平台服务器版软件V15.2产品发布
深度操作系统海思平台服务器版软件是武汉深之度科技有限公司发布的针对华为海思平台的TaiShan系列服务器发布的企业级服务器操作系统软件产品,主要面向企业级服务器应用场景,为用户在国产化平台上提供更具可 ...
- Command "python setup.py egg_info" failed with error code 1 in /tmp/pip-build-qvc66dfs/supervisor/
# 安装supervisor 出错 pip3 install supervisor # 解决 sudo pip3 install supervisor
- Silverlight 应用程序中未处理的错误
Silverlight 开发中遇到个错误: SCRIPT5022: Silverlight 应用程序中未处理的错误 代码: 2108 类别: InitializeError 消息: 无法下载初始屏幕或 ...