LwIP代码收发报流程分析(1)数据包的发送
正如官网上的介绍:lwIP - A Lightweight TCP/IP stack
LwIP 是一个轻量级的 TCP/IP 协议栈,像 Linux 网络协议栈一样,看似复杂的网络数据处理绝大多数都是在协议栈中进行处理。当到达网卡的时候为了节省MCU的资源消耗,MCU应利用 DMA 将到来的数据接收到协议栈中,再根据网络数据的类型分别处理。
LwIP 这个软件包已经为我们做了网络数据包的解析,要我们做的中最重要的操作就是处理好收包和发包,这些针对不同的芯片架构不同的网卡都是不同的,上册的软件只能留出对应的接口等待实现。 对于网卡的驱动,对于网卡的驱动 LWIP 中也有相关的例子在 contrib-2.1.0\examples\ethernetif\ethernetif.c
中。
该文件就是LWIP给出的关于网卡驱动的例子。文件中共有五个函数:
low_level_init() 根据注释可知,该函数会被在一个硬件初始化的时候被 ethernetif_init 调用。
low_level_output() 该函数是用来执行网络数据包发送的函数,这个函数也是我们要实际实现的网卡驱动函数之一。
low_level_input() 和上面一个函数类似,只不过这个函数实际用来接收网络数据包的函数。
ethernetif_input() 这个函数在数据已经准备接收的时候调用,它会调用 low_level_input 来实际的处理数据包的接收。
ethernetif_init() 在这个程序启动的时候调用,实际上它会调用 low_level_init 函数来完成硬件初始化。
接下来我们看一下这些函数是如何被 LWIP 中上层 socket 代码所调用的。
从示例 Ping 代码开始
为了分析 LwIP 代码,我们从 LwIP 软件包的例子中找到一个即常用又比较简单的例子作为目标代码进行分析。
LwIP 中的示例代码在 LWIP\contrib-2.1.0\apps\ping\ping.c
文件当中。其中关于 Ping 功能的逻辑大致如下:
单独创建一个线程用于处理 Ping 功能相关的任务,其任务代码如下:
lwip_socket(AF_INET, SOCK_RAW, IP_PROTO_ICMP);
...
while(1){
ping_send();
...
ping_recv();
}
发包流程
建立 socket 文件描述符:
lwip_socket(AF_INET6, SOCK_RAW, IP_PROTO_ICMP);
为创建的 socket 设置超时时间
lwip_setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
处理并发送 ICMP 报文
ping_send
既然函数叫做 ping_send
static err_t
ping_send(int s, const ip_addr_t *addr)
{
...
/* 申请 ICMP echo 头 + 数据部分的空间 */
iecho = (struct icmp_echo_hdr *)mem_malloc((mem_size_t)ping_size);
/* 填充 ICMP 头内容 */
ping_prepare_echo(iecho, (u16_t)ping_size);
/* 填充 socket 数据结构部分 */
struct sockaddr_in *to4 = (struct sockaddr_in*)&to;
to4->sin_len = sizeof(to4);
to4->sin_family = AF_INET;
inet_addr_from_ip4addr(&to4->sin_addr, ip_2_ip4(addr