lwip中各种函数,标志位的总结
- 格式:docx
- 大小:39.89 KB
- 文档页数:30
mem_init( ) 内存堆的初始化函数,主要是告知内存堆的起止地址,以及初始化空闲表,由lwip 初始化时自己调用,该接口为内部私有接口,不对用户层开放mem_malloc( ) 申请分配内存。
将总共需要的字节数作为参数传递给该函数,返回值是指向最新分配的内存的指针,而如果内存没有分配好,则返回值是NULLmem_calloc( ) 是对mem_malloc( )函数的简单包装,他有两个参数,分别为元素的数目和每个元素的大小,这两个参数的乘积就是要分配的内存空间的大小,与mem_malloc()不同的是它会把动态分配的内存清零。
有经验的程序员更喜欢使用mem_ calloc (), memp_num:这个静态数组用于保存各种类型缓冲池的成员数目memp_sizes:这个静态数组用于保存各种类型缓冲池的结构大小memp_tab:这个指针数组用于指向各种类型缓冲池当前空闲节点memp_init():内存池的初始化,主要是为每种内存池建立链表memp_tab,其链表是逆序的,此外,如果有统计功能使能的话,也把记录了各种内存池的数目。
memp_malloc():如果相应的memp_tab链表还有空闲的节点,则从中切出一个节点返回,否则返回空。
memp_free()把释放的节点添加到相应的链表memp_tab头上。
系统是调用内存堆分配函数mem_malloc进行内存分配的。
分配空间的大小包括pbuf结构头大小SIZEOF_STRUCT_PBUF,需要的数据存储空间大小length,还有一个offset系统是调用内存堆分配函数mem_malloc进行内存分配的。
段区域的offset的大小,这段区域用来存储数据的包头,如TCP包头,IP包头等pbuf_free(A)函数来删除pbuf结构PBUF_POOL 类型和PBUF_ROM类型、PBUF_REF类型需要通过memp_free()函数删除,PBUF_RAM类型需要通过mem_free()函数删除memp_memory是缓冲池的起始地址,前面已有所讨论; MEMP_MAX是POOL 类型数; memp_tab 用于指向某类POOL 空闲链表的起始节点;memp_num表示各种类型POOL的个数; memp_sizes表示各种类型单个POOL的大小,对于MEMP_PBUF_POOL和MEMP_PBUF型的POOL,其大小是pbuf 头和pbuf可装载数据大小的总和。
lwip之IP(二)1、ip数据包输出(1)输出ip报文err_t ip_output(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, u8_t ttl, u8_t tos, u8_t proto){struct netif *netif;//查找合适的网卡接口,即ip地址在同一网段内if ((netif = ip_route(dest)) == NULL) {return ERR_RTE;}return ip_output_if(p, src, dest, ttl, tos, proto, netif);}//填充IP报文头部字节,并判断是否需要分片,最后发送至链路层err_t ip_output_if(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest,u8_t ttl, u8_t tos,u8_t proto, struct netif *netif){struct ip_hdr *iphdr;ip_addr_t dest_addr;if (dest != IP_HDRINCL) {u16_t ip_hlen = IP_HLEN;if (pbuf_header(p, IP_HLEN)) {return ERR_BUF;}iphdr = (struct ip_hdr *)p->payload;IPH_TTL_SET(iphdr, ttl);IPH_PROTO_SET(iphdr, proto);ip_addr_copy(iphdr->dest, *dest);IPH_VHL_SET(iphdr, 4, ip_hlen / 4);IPH_TOS_SET(iphdr, tos);IPH_LEN_SET(iphdr, htons(p->tot_len));IPH_OFFSET_SET(iphdr, 0);IPH_ID_SET(iphdr, htons(ip_id));++ip_id;if (ip_addr_isany(src)) {ip_addr_copy(iphdr->src, netif->ip_addr);} else {ip_addr_copy(iphdr->src, *src);}IPH_CHKSUM_SET(iphdr, 0);IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, ip_hlen)); } else {/* IP header already included in p */iphdr = (struct ip_hdr *)p->payload;ip_addr_copy(dest_addr, iphdr->dest);dest = &dest_addr;}#if IP_FRAG//判断是否分片if (netif->mtu && (p->tot_len > netif->mtu)) {return ip_frag(p, netif, dest);}#endif /* IP_FRAG */return netif->output(netif, p, dest);}(2)ip报文分片机制分片条件:if (netif->mtu && (p->tot_len > netif->mtu)) {return ip_frag(p, netif, dest);}分片函数ip_frag//静态分片数组定义static u8_t buf[LWIP_MEM_ALIGN_SIZE(IP_FRAG_MAX_MTU + MEM_ALIGNMENT - 1)];err_t ip_frag(struct pbuf *p, struct netif *netif, ip_addr_t *dest) {struct pbuf *rambuf;struct pbuf *header;struct ip_hdr *iphdr;u16_t nfb;u16_t left, cop;u16_t mtu = netif->mtu;u16_t ofo, omf;u16_t last;u16_t poff = IP_HLEN;u16_t tmp;//pbuf类型为PBUF_REFrambuf = pbuf_alloc(PBUF_LINK, 0, PBUF_REF);if (rambuf == NULL) {return ERR_MEM;}rambuf->tot_len = rambuf->len = mtu;rambuf->payload = LWIP_MEM_ALIGN((void *)buf);iphdr = (struct ip_hdr *)rambuf->payload; SMEMCPY(iphdr, p->payload, IP_HLEN);tmp = ntohs(IPH_OFFSET(iphdr));ofo = tmp & IP_OFFMASK;omf = tmp & IP_MF;left = p->tot_len - IP_HLEN;nfb = (mtu - IP_HLEN) / 8;while (left) {last = (left <= mtu - IP_HLEN);tmp = omf | (IP_OFFMASK & (ofo));if (!last) {tmp = tmp | IP_MF;}/* Fill this fragment */cop = last ? left : nfb * 8;poff += pbuf_copy_partial(p, (u8_t*)iphdr + IP_HLEN, cop, poff);/* Correct header */IPH_OFFSET_SET(iphdr, htons(tmp));IPH_LEN_SET(iphdr, htons(cop + IP_HLEN));IPH_CHKSUM_SET(iphdr, 0);IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN));if (last) {pbuf_realloc(rambuf, left + IP_HLEN);}//申请一个PBUF_RAM型的pbuf,以便以太网帧头部预留空间header = pbuf_alloc(PBUF_LINK, 0, PBUF_RAM);if (header != NULL) {pbuf_chain(header, rambuf); //连接两个pbufnetif->output(netif, header, dest);pbuf_free(header);} else {pbuf_free(rambuf);return ERR_MEM;}left -= cop;ofo += nfb;}pbuf_free(rambuf);return ERR_OK;}2、ip报文输入(1)ip_input()该函数的作用是首先判别ip地址是否给本地网口,其次判别该ip 报文是否为分片报文,最后将该报文传输至上次协议。
lwip_recvfrom 用法`lwip_recvfrom` 函数是在轻量级IP 协议栈(LWIP)中用于从套接字接收数据的函数。
LWIP 是一个用于嵌入式系统的开源网络协议栈。
`lwip_recvfrom` 主要用于接收UDP 数据报。
以下是`lwip_recvfrom` 函数的基本用法:```c#include "lwip/sockets.h"ssize_t lwip_recvfrom(int s, void *mem, size_t len, int flags,struct sockaddr *from, socklen_t *fromlen);```参数说明:- `s`: 套接字描述符。
- `mem`: 接收数据的缓冲区指针。
- `len`: 缓冲区长度。
- `flags`: 接收标志,通常为0。
- `from`: 存储发送方地址信息的sockaddr 结构体指针。
- `fromlen`: 存储`from` 结构体长度的指针。
返回值:-返回实际接收到的数据字节数,如果出错则返回-1。
示例用法:```c#include "lwip/sockets.h"// 创建套接字int sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd < 0) {// 处理错误}// 设置本地地址struct sockaddr_in local_addr;local_addr.sin_family = AF_INET;local_addr.sin_port = htons(12345);local_addr.sin_addr.s_addr = INADDR_ANY;// 绑定套接字if (bind(sockfd, (struct sockaddr*)&local_addr, sizeof(local_addr)) < 0) {// 处理错误}// 准备接收缓冲区char buffer[1024];// 设置发送方地址信息struct sockaddr_in client_addr;socklen_t client_addr_len = sizeof(client_addr);// 接收数据ssize_t recv_len = lwip_recvfrom(sockfd, buffer, sizeof(buffer), 0,(struct sockaddr*)&client_addr, &client_addr_len);if (recv_len > 0) {// 处理接收到的数据// client_addr 存储了发送方的地址信息} else {// 处理接收错误}// 关闭套接字close(sockfd);```请注意,这只是一个基本的示例,实际应用中可能需要更多的错误处理和逻辑。
2013-06教学实践产生依赖感,在真正的动手操作机床时产生畏惧感。
所以,教师要摆正数控仿真软件在课堂教学中的位置,发挥其最大“功率”。
3.科学安排教学内容,循序渐进地掌握数控编程与操作技巧数控教学中,在“项目教学法”作为大环境的背景下,教学内容可以分为四个模块。
其一为理论基础模块:主要讲解最基本的数控编程方法和工艺安排。
其二为提高和拓展模块:主要利用数控仿真软件让学生熟悉加工步骤和各个系统的界面。
其三为实践基础模块:主要培养学生实践操作能力与技巧,取得职业等级中级证书或高级证书。
其四为拔高模块:主要讲解其他系统的编程方法和操作步骤,扩大学生的知识面,适应社会发展的需要。
4.选拔高技能型人才,参加各类大赛中职教学中,高技能型人才是在各类大赛中得到培养和锻炼的。
同时各个学校靠各类大赛中得到的成绩来改变整个学校在社会中的地位与价值。
数控技能型人才的培养是一个巨大的工程,从发现人才到培养再到取得成绩至少也要一年的时间。
我们学校在数控人才的培养方式上是非常明确的,利用课余时间进行训练,晚上考虑到安全因素,选手就在基地的机房利用仿真软件进行训练,赛前再加强动手方面的强化训练,这样既能确保选手正常的教学时间,又能为学校培养出更加优秀的人才。
六、数控仿真软件存在的负面效应任何事物的出现都存在着两面性,数控仿真软件是一种模拟的教学,具有强大的发展空间。
但它不可能完全代替教学,尤其是技能训练。
中职学校培养的是脚踏实地的一线操作工,数控仿真软件只是纯粹模拟而非真实的加工,它无法让学生体会到真实切削加工中的感受。
下面举两个例子说明:其一在模拟的环境中主轴转速达到20000r/min,主轴是照样运转而不报警的,但在现实中是永远达不到这个转速的。
其二模拟的环境中主轴转速达到1500r/min,学生感受不到实际产生的效果,但在现实中这个转速很容易让新手产生一种畏惧感,无法平下心来操作机床。
其三看下面某段加工程序:GO1X60Z-20F0.3改写成GO1X60Z-20F0.15,两个程序设置的加工速度不同,实际加工时产生的表面质量有很大的区别,在模拟的环境中是无法体现的。
lwip和tcp函数lwip(Lightweight IP)是一个轻量级的开源TCP/IP协议栈,它被广泛应用在嵌入式系统和物联网设备中。
而TCP(Transmission Control Protocol)是一种面向连接的、可靠的传输层协议,它保证了数据的可靠传输。
在嵌入式系统和物联网设备中,网络通信是非常重要的功能之一。
lwip协议栈提供了一套简洁而高效的API,使得嵌入式开发者能够方便地进行网络通信的开发。
lwip协议栈的核心是TCP/IP协议栈的实现,而TCP协议是其中最重要的一部分。
TCP协议提供了一种可靠的、面向连接的通信方式,确保数据的正确传输。
在lwip中,TCP函数是实现TCP协议的关键。
它提供了一系列函数来实现TCP连接的建立、数据的发送和接收、连接的关闭等功能。
首先是TCP连接的建立。
TCP连接的建立需要通过三次握手来完成。
lwip提供了tcp_connect函数来建立TCP连接。
开发者只需要指定目标IP地址和端口号,就可以调用tcp_connect函数来建立连接。
接下来是数据的发送和接收。
lwip提供了tcp_write和tcp_recv函数来实现数据的发送和接收。
开发者可以使用tcp_write函数将数据写入发送缓冲区,然后通过tcp_output函数将数据发送出去。
而tcp_recv函数则用于接收数据,开发者可以通过注册回调函数来处理接收到的数据。
最后是连接的关闭。
lwip提供了tcp_close函数来关闭TCP连接。
开发者可以调用tcp_close函数来关闭连接,并释放相关资源。
除了以上的基本功能,lwip还提供了其他一些高级功能,如TCP拥塞控制、流量控制等。
开发者可以根据需求使用这些功能来优化网络通信的性能。
总结一下,lwip是一个轻量级的开源TCP/IP协议栈,它提供了一套简洁而高效的API,方便嵌入式开发者进行网络通信的开发。
而TCP函数是lwip中实现TCP协议的关键,它提供了一系列函数来实现TCP连接的建立、数据的发送和接收、连接的关闭等功能。
lwip 路由函数lwIP(lightweight IP)是一个轻量级的嵌入式TCP/IP协议栈,通常用于嵌入式系统中。
lwIP 提供了一组用于实现网络功能的API和协议,包括IP、TCP、UDP、ICMP等。
在lwIP中,路由(routing)是网络通信的重要组成部分。
lwIP路由相关的函数通常与网络栈的核心API一起使用,例如`ip_route()`。
下面是一个简单的lwIP路由函数的例子:```c#include "lwip/ip.h"#include "lwip/netif.h"// 自定义的路由处理函数err_t custom_route(struct pbuf *p, struct netif *inp, const ip_addr_t *dest_ip, const ip_addr_t *src_ip){// 在这里实现自定义的路由逻辑,可以根据需要修改// ...return ERR_OK; // 或者其他合适的错误码}int main(){struct netif netif;struct pbuf *p;ip_addr_t dest_ip, src_ip;// 初始化网络接口netif_add(&netif, /*...*/);// 填充目标IP和源IPIP4_ADDR(&dest_ip, 192, 168, 1, 1);IP4_ADDR(&src_ip, 192, 168, 1, 2);// 构造数据包p = pbuf_alloc(/*...*/);// 调用路由函数err_t result = custom_route(p, &netif, &dest_ip, &src_ip);// 处理结果...// ...return 0;}```在实际应用中,需要根据具体的场景和需求来编写路由处理函数。
lwIP和TCP函数什么是lwIP?lwIP(lightweight IP)是一个轻量级的开源的IP协议栈,适用于嵌入式系统和小型设备。
它提供了TCP/IP协议栈中的核心功能,如IP、TCP、UDP、ICMP等协议的实现,同时具有较低的内存占用和较高的性能。
lwIP具有高度可配置性,可以根据实际需求进行裁剪和优化,使其适用于不同类型的设备和应用场景。
TCP函数TCP(Transmission Control Protocol)是一种面向连接的、可靠的、有序的、基于字节流的传输协议。
TCP在IP协议的基础上提供了面向连接的可靠数据传输功能,通过序号、确认和重传机制来保证数据的可靠性和有序性。
lwIP提供了一系列的TCP函数,用于在嵌入式系统中实现TCP协议的各种功能。
1. tcp_new()函数原型:struct tcp_pcb* tcp_new(void)功能:创建一个新的TCP协议控制块(PCB)。
说明: TCP协议控制块用于维护TCP连接的状态信息,包括本地IP地址、端口号、远程IP地址、端口号等。
通过tcp_new()函数可以创建一个新的TCP协议控制块,并返回该控制块的指针。
2. tcp_bind()函数原型:err_t tcp_bind(struct tcp_pcb* pcb, const ip_addr_t* ipaddr, u16_t port)功能:将TCP协议控制块绑定到指定的IP地址和端口。
说明: tcp_bind()函数用于将一个已创建的TCP协议控制块绑定到一个特定的IP地址和端口。
绑定后的控制块可以监听该IP地址和端口的连接请求。
3. tcp_listen()函数原型:struct tcp_pcb* tcp_listen(struct tcp_pcb* pcb)功能:将TCP协议控制块设置为监听状态。
说明: tcp_listen()函数用于将一个已绑定的TCP协议控制块设置为监听状态,使其可以接受来自客户端的连接请求。
lwIP基本栈接口层(udp部分、pbuf结构)相对于tcp协议,udp协议的处理过程容易很多。
udp协议涉及到函数都要用到udp的协议控制块“udp_tcp”。
这和tcp协议控制块很相似,但是其成员却略有不同。
这个结构体就是udp协议控制块,用于管理其相关操作。
这些成员都不需要我们手动修改(调用相关函数即可自动修改),但我们可以实时查看我们需要的变量。
其成员不多,我们简单介绍一下。
“next”用来将每个协议控制块串联成一个控制块链表;“local_ip”和“dest_ip”分别表示本地和远端机的ip地址;“local_port”和“dest_port”分别表示本地和远端机的端口号;“flags”标识所应用的udp校验策略,或者可以完全关闭udp校验和,或者使用udp简化版校验和只覆盖数据包的一部分;“chksum_len”指出校验和计算数据段的长度;“recv”和“recv_arg”用于指定在接收到数据包时所使用的函数及其参数。
在接口函数中还用到了网络接口的结构体“netif”,这里也简单介绍一下。
物理网络硬件的设备驱动是通过这样的结构体来管理的。
下面简单介绍其成员:“next”全局链表指针;“name”用两个字符的名字按网络类系那个标识网络硬件接口;“num”用来区分相同类型的网络接口;“ip_addr”、“netmask”和“gw”分别表示本地机上ip地址、子网掩码和网关;“input”接收数据包时所调用的设备驱动程序的指针;“output”发送数据包时所调用的设备驱动程序的指针;“state”指针指向网络接口的设备驱动特定状态,由设备驱动来设置。
下面介绍接口函数一、udp.c文件1、void udp_input(struct pbuf *p, struct netif *inp)说明:处理接收到的udp数据包。
参数:p数据包缓存区;inp网络接口。
2、err_t udp_send(struct udp_pcb *pcb, struct pbuf *p)说明:发送udp包。
mem_init( ) 内存堆的初始化函数,主要是告知内存堆的起止地址,以及初始化空闲表,由lwip 初始化时自己调用,该接口为内部私有接口,不对用户层开放mem_malloc( ) 申请分配内存。
将总共需要的字节数作为参数传递给该函数,返回值是指向最新分配的内存的指针,而如果内存没有分配好,则返回值是NULLmem_calloc( ) 是对mem_malloc( )函数的简单包装,他有两个参数,分别为元素的数目和每个元素的大小,这两个参数的乘积就是要分配的内存空间的大小,与mem_malloc()不同的是它会把动态分配的内存清零。
有经验的程序员更喜欢使用mem_ calloc (),memp_num:这个静态数组用于保存各种类型缓冲池的成员数目memp_sizes:这个静态数组用于保存各种类型缓冲池的结构大小memp_tab:这个指针数组用于指向各种类型缓冲池当前空闲节点memp_init():内存池的初始化,主要是为每种内存池建立链表memp_tab,其链表是逆序的,此外,如果有统计功能使能的话,也把记录了各种内存池的数目。
memp_malloc():如果相应的memp_tab链表还有空闲的节点,则从中切出一个节点返回,否则返回空。
memp_free()把释放的节点添加到相应的链表memp_tab头上。
系统是调用内存堆分配函数mem_malloc进行内存分配的。
分配空间的大小包括pbuf结构头大小SIZEOF_STRUCT_PBUF,需要的数据存储空间大小length,还有一个offset系统是调用内存堆分配函数mem_malloc进行内存分配的。
段区域的offset的大小,这段区域用来存储数据的包头,如TCP包头,IP包头等pbuf_free(A)函数来删除pbuf结构PBUF_POOL 类型和PBUF_ROM类型、PBUF_REF类型需要通过memp_free()函数删除,PBUF_RAM类型需要通过mem_free()函数删除memp_memory是缓冲池的起始地址,前面已有所讨论;MEMP_MAX是POOL 类型数; memp_tab 用于指向某类POOL 空闲链表的起始节点;memp_num表示各种类型POOL的个数;memp_sizes表示各种类型单个POOL的大小,对于MEMP_PBUF_POOL和MEMP_PBUF型的POOL,其大小是pbuf 头和pbuf可装载数据大小的总和。
网络接口在LWIP中,是通过一个叫做netif的网络结构体来描述一个硬件网络接口的struct netif {struct netif *next; // 指向下一个netif结构的指针struct ip_addr ip_addr; // IP 地址相关配置struct ip_addr netmask;struct ip_addr gw;err_t (* input)(struct pbuf *p, struct netif *inp); //调用这个函数可以从网卡上取得一个数据包err_t (* output)(struct netif *netif, struct pbuf *p, // IP 层调用这个函数可以向网卡发送structip_addr*ipaddr); //一个数据包err_t (* linkoutput)(struct netif *netif, struct pbuf *p); // ARP模块调用这个函数向网卡发送一个数据包void *state; // 用户可以独立发挥该指针,用于指向用户关心的网卡信息u8_t hwaddr_len; // 硬件地址长度,对于以太网就是MAC地址长度,为6各字节u8_t hwaddr[NETIF_MAX_HWADDR_LEN]; //MAC 地址u16_t mtu; // 一次可以传送的最大字节数,对于以太网一般设为1500u8_t flags; // 网卡状态信息标志位char name[2]; // 网络接口使用的设备驱动类型的种类u8_t num; // 用来标示使用同种驱动类型的不同网络接口};output字段向一个函数该函数的三个参数是pbuf类型、netif 类型和ip_addr类型,返回参数是err_t类型。
其中pbuf代表要发送的数据包。
ipaddr 代表网卡需要将该数据包发送到的地址,该地址应该是接收实际的链路层帧的主机的Ip地址ethernetif_init底层接口初始化函数tcpip_input 函数是向IP层递交数据包的函数netif->next = netif_list; //将初始化后的节点插入链表netif_list netif_list = netif; // netif_list 指向链表头low_level_init(netif); //底层硬件初始化函数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'; //设什么地址用户自由发挥吧,但是不要与其他网络设备的MAC地址重复。
netif->hwaddr[2] = 'R';netif->hwaddr[3] = 'E';netif->hwaddr[4] = 'S';netif->hwaddr[5] = 'T';netif->mtu = 1500//最大允许传输单元netif->flags = NETIF_FLAG_BROADCAST | \NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP;enc28j60_init(netif->hwaddr); //与底层驱动硬件驱动程序密切相关的硬件初始化函数}netif_set_default函数初始化缺省网络接口。
协议栈除了有个netif_list全局变量指向netif网络接口结构的链表,还有个全局变量netif_default全局变量指向缺省的网络接口结构。
以太网的数据接收LWIP使用了一个eth_hdr的数据结构来描述以太网数据包包头的14个字节关于网卡数据包的接收和发送。
LWIP 中实现了接收一个数据包和发送一个数据包函数的框架,这两个函数分别是low_level_input和low_level_outpuPACK_STRUCT_xxx都是与编译器字对齐相关的宏定义htons(ethhdr->type)函数的使用,htons函数的功能是将一个半字长的数据从网络字节顺序转换到我们的处理器支持的字节顺序任何网络的通信都是基于底层硬件链路的,底层的数据链路有着自己的一套寻址机制,在以太网中,往往是通过一个48位的MAC地址来标示不同的网络通信设备的。
而TCP/IP协议的上层是使用IP地址作为各个主机间通信寻址机制的。
ARP缓存表LWIP中描述缓存表项的数据结构叫etharp_entrystruct etharp_entry {#if ARP_QUEUEING //ARP_QUEUEING是编译选项,表示是否允许缓存表项有数据包缓冲队列struct etharp_q_entry *q; // 数据包缓冲队列指针#endifstruct ip_addr ipaddr; // 目标IP地址struct eth_addr ethaddr; // MAC 地址enum etharp_state state; // 描述该entry的状态u8_tctime; // 描述该entry的时间信息struct netif *netif; // 相应网络接口信息};ipaddr 和ethaddr字段就是分别用于存储IP地址和MAC地址的,它们是ARP缓存表项的核心部分了LWIP内核通过数组的方式来创建ARP缓存表,如下,static struct etharp_entry arp_table[ARP_TABLE_SIZE];要发往该表项中IP 地址处的数据包会被连接在表项对应的数据包缓冲队列上,当等到该表项稳定后,这些数据包才会被发送出去。
这就是为什么每个表项需要有数据包缓冲队列指针了。
ctime字段记录表项处于某个状态的时间,当某表项的ctime 值大于规定的表项最大生存值时,该表项会被内核删除目的地址为全 1 的特殊地址是广播地址。
在ARP表项建立前,源主机只知道目的主机的IP 地址,并不知道其MAC地址,所以在数据链路上,源主机只有通过广播的方式将ARP 请求数据包发送出去。
电缆上的所有以太网接口都要接收广播的数据包,并检测数据包是否是发给自己的,这点通过对照目的IP 地址来实现,如果是发给自己的,目的主机需要回复一个ARP应答数据包给源主机,以告诉源主机自己的MAC地址。
对于一个ARP请求来说,除目的端MAC地址外的所有其他的字段都有填充值。
当目的主机收到一份给自己的ARP请求报文后,它就把自己的硬件地址填进去,然后将该请求数据包的源主机信息和目的主机信息交换位置,并把操作字段op 置为2,最后把该新构建的数据包发送回去,这就是ARP响应。
ARP搜索find_entry,该函数主要功能是寻找一个匹配的ARP表项或者创建一个新的ARP表项,并返回该表项的索引号LWIP中有个全局的变量etharp_cached_entry,它始终保存着上次用到的索引号etharp_query,该函数的功能是向给定的IP地址发送一个数据包或者发送一个ARP请求如果给定的IP地址不在ARP表中,则一个新的ARP表项会被创建,此时该表项处于pending 状态,同时一个关于该IP地址的ARP请求包会被广播出去,再同时要发送的数据包会被挂接在该表项的数据缓冲指针上;如果IP地址在ARP表中有相应的表项存在,但该表项处于pending状态,则操作与前者相同,即发送一个ARP请求和挂接数据包;如果IP地址在ARP表中有相应的表项存在,且表项处于stable状态,此时再来判断给定的数据包是否为空,不为空则直接将该数据包发送出去,为空则向该IP 地址发送一个ARP请求update_arp_entry,该函数用于更新ARP缓存表中的表项或者在缓存表中插入一个新的表项。
该函数会在收到一个IP数据包或ARP数据包后被调用arp_table[i].q = q->next; // 缓冲链表表头指向下一个节点ipaddr 和ethaddr分别对应的ip地址和mac地址,update_arp_entry的流程如下:先通过调用find_entry 找到对应ipaddr 对应的表项,并设置相应的arp表项的成员(主要是state,netif,ethaddr,cttime)LWIP利用netif.input指向的函数接收以太网数据包,通常这个函数是ethernet_input。