lwip---(五)以太网数据接收
low_level_init函数是与我们使用的与硬件密切相关初始化函数,代码如下:
static void low_level_init(struct netif *netif)
{netif->hwaddr_len = ETHARP_HWADDR_LEN; //设置变量enc28j60的hwaddr_len字段netif->hwaddr[0] = 'F'; //初始化变量enc28j60的MAC地址netif->hwaddr[1] = 'O'; //设什么地址用户自由发挥吧,但是不要与其他netif->hwaddr[2] = 'R'; //网络设备的MAC地址重复。netif->hwaddr[3] = 'E';netif->hwaddr[4] = 'S'; netif->hwaddr[5] = 'T';netif->mtu = 1500; //最大允许传输单元//允许该网卡广播和ARP功能,并且该网卡允许有硬件链路连接netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP;enc28j60_init(netif->hwaddr); //与底层驱动硬件驱动程序密切相关的硬件初始化函数
}
至此,终于变量enc28j60
被初始化好了,而且它描述的网卡芯片enc28j60
也被初始化好了,而且变量enc28j60
也被链入链表netif_list
。
接着上面的语句(8)调用netif_set_default
函数初始化缺省网络接口。协议栈除了有个netif_list
,全局变量指向netif
网络接口结构的链表,还有个全局变量netif_default
,全局变量指向缺省的网络接口结构。当IP层有数据发送时,它首先会以netif_list
为索引选择满足某个条件的网络接口发送数据包,但是,当找不到这样的接口时,协议栈就会调用缺省的网络接口直接发送数据包,所以(8)中的意思是把变量enc28j60
描述的网络接口设置为缺省的网络接口。
(9) 调用函数netif_set_up
使能网络接口,这通过一个简单语句来实现:
netif->flags |= NETIF_FLAG_UP;
至此,网卡初始化完成,能正常接收和发送数据包了。下面我们来讨论讨论关于网卡数据包的接收和发送。
LWIP中实现了接收一个数据包和发送一个数据包函数的框架,这两个函数分别是low_level_input
和low_level_output
,用户需要使用实际网卡驱动程序完成这两个函数。在第一篇中讲过,一个典型的LWIP应用系统包括这样的三个进程:首先是上层应用程序进程,然后是LWIP协议栈进程,最后是底层硬件数据包接收进程。这里我们就来讲讲第三个进程,看看数据包是怎样被接收并往上层传递的。但在这之前,有必要说说以太网网卡所收到的数据包的格式。如下图,
LWIP使用了一个eth_hdr
的数据结构来描述以太网数据包包头的14个字节。如下,
PACK_STRUCT_BEGINstruct eth_hdr {PACK_STRUCT_FIELD(struct eth_addr dest); //目标MAC地址PACK_STRUCT_FIELD(struct eth_addr src); //源MAC地址PACK_STRUCT_FIELD(u16_t type); //类型
} PACK_STRUCT_STRUCT;PACK_STRUCT_END
其中PACK_STRUCT_xxx
都是与编译器字对齐相关的宏定义,这里不作详细介绍了。上面的dest
、src
和type
三个字段分别和上图中的目的MAC地址、源MAC地址和类型域字段对应。
在上面讨论的基础上,我们来看看这个数据包接收进程,源代码如下:
void ethernetif_input(void *arg) //创建该进程时,要将某个网络接口结构的netif结构指
{ //针作为参数传入struct eth_hdr *ethhdr;struct pbuf *p;struct netif *netif = (struct netif *)arg;while (1){p = low_level_input (netif); // 接收一个数据包if (p == NULL) // 如果数据包为空,continue; // 则循环结束,启动下次接收过程ethhdr = p->payload; // 取得数据包内数据switch (htons(ethhdr->type)) // 判断数据包类型{ // 只对IP数据包和ARP数据包进行处理case ETHTYPE_IP: // IP数据包case ETHTYPE_ARP: // ARP数据包if (netif->input(p, netif)!=ERR_OK) // 将数据包发送到上层应用函数{ pbuf_free(p);p = NULL;}break;default:pbuf_free(p);p = NULL;break;} //switch} //while
} //main函数
要创建上面的这个进程,需要把个网络接口结构的netif
结构指针作为参数传入,在UC/OSII
中要用到下面的语句实现,
OSTaskCreate(ethernetif_input,(void *)&enc28j60,&T_ETHERNETIF_INPUT_STK[T_ETHERNETIF_INPUT_STKSIZE-1]ETH_IF_TASK_PRIO);
在数据包接收进程中,有三个需要注意的地方。
一是数据包接收的方法是查询方式,即处理器不断向网卡芯片中读取数据,如果读不到数据,则控制器会重新启动一个读取时序;如果能够成功读取到数据,则将数据通过网卡注册的input函数交往上层进行处理。使用查询方式实现的数据包接收进程其优先级必须低于系统中其他进程的优先级,否则它会阻塞比它优先级低的进程的运行。
上面的程序有个可以改进的地方,即在读取到的数据包为空时,接收进程调用系统函数将自己延时一段时间再启动下一个读取过程,这样可以使其不能阻止优先级更低的进程的运行,缺点是数据包的接收得不到及时的响应。
其实数据包的接收可以采用中断的方式来实现,这种方式是一种比较好的方式。一般的网卡芯片都有中断功能,即当网卡接收到一个数据包后,它可以产生中断信号告诉控制器自己接收到一个数据包。控制器此时启动一个读取数据包时序,就能有效的读取到非空数据包。所以可以这样来实现一个接收数据包进程:在无数据包收到时,数据包接收进程阻塞在一个信号量下,当有数据包到来时,网卡芯片产生一个中断信号,处理器进入中断处理,并释放一个信号量。中断退出后,数据包接收进程得到信号量,并从网卡芯片中读取数据包,并将数据包递交给上层进行处理。
第二个需要注意的地方是htons(ethhdr->type)函数的使用,htons
函数的功能是将一个半字长的数据从网络字节顺序转换到我们的处理器支持的字节顺序。解释一下,在计算机体系结构和计算机通信领域中,对于半字、字等的存储机制有可能不同。目前通常采用的存储机制主要有两种:big-endian
和little-endian
,即大端和小端。对于大端模式,某个半字或字数据的高位字节被在内存的低地址端,低位字节排放在内存的高地址端。对于小端模式,则恰好相反。由于我们使用的ARM处理器使用的是小端模式,而接收到的网络字节数据用的是大端模式,所以这里调用函数htons实现大端与小端的转换,实际就是将两个字节交换顺序即可。这样调用htons(ethhdr->type)
后,ethhdr->type
的值就为0x0800
或0x0806
等。
最后需要注意的地方,netif->input
在结构enc28j60
初始化时已经被设置为指向tcpip_input
函数,所以实际上上面是调用tcpip_input
函数往上层递交数据包。tcpip_input
属于IP层函数,从这里我们可以看出LWIP的一个很大的特点,即各层之间没有明显的界限划分。像前面所讲的那样,LWIP协议栈进程完成初始化相关工作后,会阻塞在一个邮箱上等待数据包的输入,tcpip_input
函数就是向这个邮箱发送一条消息,且该消息中包含了收到的数据包存储的地址。LWIP协议栈进程从邮箱中取到该地址后就可以对数据包进行处理了。
至此,数据包的接收可算大功告成,关于数据包的发送,这点很简单,因为它不必像数据包接收那样要使用一个专门的进程来实现,而是这样的:当上层有数据包要发送时,直接调用netif->linkoutput
发送数据包就可以了。netif->linkoutput
在结构enc28j60
初始化时已经被设置为指向low_level_output
函数,该函数和底层硬件驱动密切相关,用于实现发送一个数据包的功能。用户应该结合具体网卡驱动实现该函数。
lwip---(五)以太网数据接收相关推荐
- matlab制作以太网数据接收上位机_Python制作串口通讯上位机
串口通讯具有简单易用的特点广泛应用于测试设备的通讯和数据传递.单片机与计算机的通讯等,本案例基于Python语言制作一个用于接收燃油质量流量计的串口通讯上位机,实现数据的读取和保存. 1. 相关知识点 ...
- matlab制作以太网数据接收上位机_3D激光扫描仪设计及数据处理
本文内容转载自<电子技术应用>2019年第10期,版权归<电子技术应用>编辑部所有. 段清明,王凡,徐琳琳,全文俊 吉林大学仪器科学与电气工程学院 摘要:利用2D激光雷达配合云 ...
- linux内核网络协议栈--数据接收流程图(五)
各层主要函数以及位置功能说明: 1)sock_read:初始化msghdr{}的结构类型变量msg,并且将需要接收的数据存放的地址传给msg.msg_iov->iov_base. net/soc ...
- 基于小梅哥AC620开发板的NIOS II LWIP百兆以太网例程移植到自己做的板子上
原程序是运行在小梅哥AC620开发板上的:基于小梅哥AC620开发板的NIOS II LWIP百兆以太网例程_ZLK1214的专栏-CSDN博客_小梅哥ac620[开发板]开发板型号:小梅哥AC620 ...
- 以太网数据包、IP包、TCP/UDP 包的结构(转)
源:以太网数据包.IP包.TCP/UDP 包的结构 版本号(Version):长度4比特.标识目前采用的IP协议的版本号.一般的值为0100(IPv4),0110(IPv6). IP包头长度(Head ...
- STM32CubeMX HAL库串口+DMA数据发送不定长度数据接收
参考资料:1.ST HAL库官网资料 2.https://blog.csdn.net/u014470361/article/details/79206352#comments 一.STM32CubeM ...
- 【Socket网络编程】7.以太网数据包、IP数据包、UDP数据包
以太网数据包.ip数据包.udp数据包 搭配这篇博文服用,效果更好:数据封装 和 数据拆封:https://blog.csdn.net/u011754972/article/details/11794 ...
- 基于STC89C52的韦根数据接收
韦根是一种开放的通讯协议,具体官方介绍如下: Wiegand协议是国际上统一的标准,有很多格式,标准的26-bit 应该是最常用的格式.此外,还有34-bit .37-bit 等格式.格式的含义如下: ...
- TCP/IP网络协议栈:以太网数据包结构、802.3、MTU
<TCP/IP网络协议栈:以太网数据包结构.802.3> <TCP/IP网络协议栈:ARP协议详解> <TCP / IP攻击:ARP缓存中毒的基本原理.TCP序列号预测和 ...
- linux 内核网络,数据接收流程图
4.3 数据接收流程图 各层主要函数以及位置功能说明: 1)sock_read:初始化msghdr{}的结构类型变量msg,并且将需要接收的数据存放的地址传给msg.msg_iov- ...
最新文章
- Nature:学术造假者瑟瑟发抖,论文图像查重AI技术重拳出击!
- Quartz.NET基础知识概述
- Splunk高管:中国才有真正意义的大数据
- 生活的色彩——摄影作品欣赏
- 文献学习(part87)--Linearized Alternating Direction Method with Adaptive Penalty for Low-Rank ...
- 《深入理解JVM.2nd》笔记(五):调优案例分析与实战
- flink开发案例_Flink 在人工智能领域的应用实践
- 网络蠕虫一般利用计算机系统,网络蠕虫是什么
- 银行卡号编码规则及其应用
- Jetson TK1 刷机安装Ubuntu系统与Mini PCI-e无线网卡
- 论文书写之如何引用参考文献(简单明了)
- 视频转码编辑工具:Compressor for Mac(4.5.4)
- 瑞云渲染 | 全面支持Anima®4渲染插件,实现高精度的群集角色!
- 关于SQL的学习记录(三、数据表)
- 碧山计划对乡土遗产保护的启示
- 软件工程不会教不懂编程的人如何编程
- UVALive 4513 Stammering Aliens
- c语言综合作业题库,计算机二级等级考试《C语言》选择题专项练习合集
- 我所遇到最牛的sshd远程连接
- 微软正式提供免费杀毒软件公测版下载
热门文章
- 张一鸣的大学四年收获及工作感悟
- linux jnlp 远程,OpenJDK “IcedTea”插件JNLPSecurityManager远程代码执行漏洞
- oracle查找用户名和密码忘记了,Oracle忘记用户名和密码的解决方案
- 1688如何做视频外链
- python3.7怎么设置字体大小,python字体大小如何设置
- 【Android安全】Android root原理及方案 | Magisk原理
- adb 截屏和录屏命令
- zoj 3141 - nie's Dog Biscuits
- Tesseract-OCR识别中文与训练字库实例(转)
- VMware vCenter Server 7.0 U2b/6.7 U3n/6.5 U3p 修复高危安全漏洞