什么是LwIP
定义:lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。
既然LwIP是简易版的TCP/IP,那有没有其它的类似协议栈呢
有的
通过chatgpt,可以方便的获知有哪些同类型的协议栈
| lwIP | lwIP (lightweight IP) is a widely used open-source TCP/IP stack designed for embedded systems and low-resource devices. It is known for its small memory footprint and efficient operation. lwIP is written in C and supports various protocols such as IPv4, IPv6, TCP, UDP, ICMP, DHCP, and DNS. |
| uIP | uIP is another lightweight open-source TCP/IP stack designed for embedded systems. It is written in C and is known for its simplicity and small code size. uIP supports IPv4, TCP, UDP, and ICMP protocols and is often used in resource-constrained devices. |
| Contiki | Contiki is an open-source operating system specifically designed for the Internet of Things (IoT) and wireless sensor networks. It includes its own TCP/IP stack called uIPv6, which is a lightweight implementation of IPv6, TCP, UDP, and other protocols. Contiki is written in C and provides a range of networking features for IoT applications. |
| lwIP for FreeRTOS | lwIP is also available as a port for the FreeRTOS real-time operating system. This combination provides a lightweight TCP/IP stack suitable for embedded systems running FreeRTOS. It offers support for various protocols and features, including IPv4, IPv6, TCP, UDP, DHCP, and DNS. |
| CycloneTCP | CycloneTCP is an open-source TCP/IP stack written in C. It is designed to be portable and efficient, making it suitable for embedded systems and IoT applications. CycloneTCP supports IPv4, IPv6, TCP, UDP, ICMP, DHCP, DNS, and other protocols. |
lwIP源码学习
通过
http://savannah.nongnu.org/projects/lwip/
可以获取到LwIP的源码文件
LwIP的源码文件结构
| src | This directory contains the core components of lwIP, including the main initialization code, memory management, timers, and system abstraction layer. Key files in this directory include init.c, mem.c, timers.c, and sys.c. |
| src/api | The api directory contains the implementation of the various network protocols and APIs provided by lwIP. This includes files for TCP (tcp.c), UDP (udp.c), IP (ip.c), and other protocols such as ICMP (icmp.c) and DHCP (dhcp.c). |
| src/core | The core directory contains the implementation of the core protocols and functions of lwIP. It includes files for handling packet processing, routing, and fragmentation. Key files in this directory include ipv4.c, ipv6.c, udp.c, tcp.c, and netif.c. |
| src/include | The include directory contains header files that define the APIs, data structures, and constants used throughout lwIP. These headers are included in the source files to provide access to the necessary functions and definitions. |
| src/netif | The netif directory contains files related to network interface management. This includes the implementation of network interface drivers, such as Ethernet (ethernetif.c) or Wi-Fi (wifiif.c). It also includes files for handling link state changes, IP address assignment, and packet reception and transmission. |
| src/apps | The apps directory contains example applications and utility functions that demonstrate the usage of lwIP. This includes files for implementing HTTP server functionality, DNS client, and other application-specific protocols. |
| src/arch | The arch (architecture) directory contains architecture-specific code and adaptations for different platforms or operating systems. It includes files such as arch.c and arch/perf.c that provide platform-specific implementations and optimizations. |
| src/netif/ppp | The ppp directory contains the Point-to-Point Protocol (PPP) implementation for lwIP. It includes files for handling PPP connection setup, authentication, and packet encapsulation. |
| src/netif/slipif | The slipif directory contains the Serial Line Internet Protocol (SLIP) implementation for lwIP. It includes files for handling SLIP packet encapsulation and transmission over serial interfaces. |
| src/netif/ethernet | The ethernet directory contains Ethernet-specific code and drivers for lwIP. It includes files for handling Ethernet frame processing, MAC address management, and link state detection. |
| src/netif/wlan | The wlan directory contains Wi-Fi-specific code and drivers for lwIP. It includes files for handling Wi-Fi frame processing, MAC address management, and link state detection. |
src是LwIP的源码文件,可以通过上述表格大致知道其内容
LwIP内核是由一系列模块组合而成的,这些模块包括:TCP/IP协议栈的各种协议、内存管理模块、数据包管理模块、网卡管理模块、网卡接口模块、基础功能类模块、API模块。每个模块是由相关的几个源文件和头文件组成的,通过头文件对外声明一些函数、宏、数据类型,使得其它模块可以方便的调用此模块的功能。
src/core
将源码下载到本地,然后通过vs code查看,首先是core的学习

在此对部分头文件的功能进行描述:
def.c
定义了一些基础类函数,如主机序和网络序的转换、字符串的查找和比较、整数转换成字符串等。这些函数会被LwIP内核中的很多模块所调用。include中的def.h文件对外声明了def.c所实现的函数,同时定义了许多宏,能实现一些基础操作,比如取最大值、取最小值、计算数组长度等,这些宏同时也被内核的许多模块调用。我们经常可以看到某个内核的源文件在开始处就写上#include “def.h”。
inet_chksum.c
提供了LwIP所需的校验和功能,在IP、UDP、TCP的实现中,需要计算校验和。
init.c
对LwIP的用户宏配置进行了检查,会将配置错误和不合理的地方通过编译器的#error和#warning功能标出。另外,init.c文件中定义了lwip_init初始化函数,这个函数会依次对LwIP的各个模块进行初始化。
ip.c
实现了IP相关的函数,但只是封装了ipv4和ipv6文件夹中的函数。
mem.c
实现了动态内存池管理机制,使得LwIP内核的各个模块可以灵活的申请和释放内存。
memp.c
实现了静态内存堆管理机制,使得LwIP内核的各个模块可以快速地申请和释放内存。
netif.c
实现了关于网卡的操作,比如注册/删除网卡、启用/禁用网卡、设置网卡的IP地址等。netif.c与include目录中的netif.h文件共同构成了LwIP的netif模块,它对网卡进行了抽象,使得LwIP内核可以方便地管理多个特性各异的物理网卡。
pbuf.c
实现了LwIP对网络数据包的各种操作。网络数据包在LwIP内核中以pbuf结构体的形式存在,这提高了LwIP内核对数据包的处理速率,也提高了数据包在各层之间的递交效率。pbuf结构体也是我们使用RAW/Callback API进行网络应用程序开发的关键。
raw.c
实现了一个传输层协议的框架,我们可以在该文件的基础上修改和添加代码,实现自定义的传输层协议。与UDP/TCP一样,它可以与IP层直接进行交互,这类似于RAW Socket。在实际应用中,我们常使用UDP和TCP作为传输层协议,但有时,底层网络开发人员会认为UDP的可靠性太差,或者TCP虽然可靠性强,但是很费时间和内存,他们需要根据实际情况,权衡利弊,定义自己的传输层协议。LwIP的raw模块可以满足他们的需求。
stats.c
实现了LwIP内核的统计功能,使用户可以实时的查看LwIP内核对网络数据包的处理情况。
sys.c
和sys.h文件构成了LwIP的sys模块,提供了与临界区相关的操作。
tcp.c、tcp_in.c 和tcp_out.c
共同实现了TCP,包括对TCP连接的操作、对TCP数据包的输入输出操作和TCP定时器,它们和include目录中名称带tcp的头文件共同构成了LwIP的TCP模块。TCP模块的实现是LwIP的最大特点,它以很小的资源开销几乎实现了TCP中规定的全部内容。TCP是非常复杂的协议,这几个与TCP模块相关的文件占据了LwIP内核的绝大部分。
timeout.c
定义了LwIP内核的超时处理机制。LwIP内核中多个模块的实现需要借助超时处理机制完成,包括ARP表项的时间统计、IP分片报文的重装、TCP的各种定时器、实现各种应用层协议需要的超时处理。
LwIP的官方说明文档
https://www.nongnu.org/lwip/2_1_x/index.html
contrib
contrib中放的是移植和应用LwIP的一些demo,即应用示例,里面很多内容来自开源社区。
其中的文件夹:

addons
LwIP中很多模块的实现都是可以由用户干预的,比如校验和、TCP初始化序列号。LwIP的内核代码通过宏编译选项的设置,可以将内核中某些模块的实现方法配置成LwIP默认的方法或者用户自定义的方法。用户自定义的方法通常需要用户在钩子函数中实现。在实际应用中,我们采用内核默认的方法就足够了,只有在特定的场合下,为了顾及性能、资源开销等,我们可能需要自己实现相关的模块,或者编写相应的钩子函数。此时,addons文件夹中的内容就为我们提供了参考。
apps
实现了很多应用层协议。LwIP源码包中也有apps文件夹,但源码包中apps文件夹下的应用程序全部用RAW/Callback API实现,属于内核代码的一部分。而此apps文件夹中提供的应用程序可以是三种API的任意一种实现的,因此可以将此apps看作是对内核源码提供的应用程序的补充。
examples
提供一些LwIP的应用示例。对于使用LwIP开发应用程序时可能出现的典型问题,比如如何移植网卡、如何使用LwIP的API、如何使用源码中提供的应用程序,这个目录提供了参考。
ports
提供一些移植文件,可以帮助我们将LwIP移植到某个具体的操作系统中。
LwIP的三种编程接口
LwIP提供了3种编程接口,分别为RAW/Callback API、Netconn API、Socket API。它们的易用性从左到右依次提高,而执行效率从左到右依次降低,用户可以根据实际情况选择合适的API进行网络应用程序的开发。
RAW/Callback API
RAW/Callback API是指内核回调型的API,这在许多通信协议的c语言实现中都有所应用。
RAW/Callback API是LwIP的一大特色,在没有操作系统支持的裸机环境中,只能使用这种API进行开发,同时这种API也可以用在操作系统环境中。
回调:
假设你新建了一个TCP或者UDP连接,想等它们接收到数据以后处理它们,这时需要把处理该数据的操作封装成一个函数,然后将这个函数的指针注册到LwIP的内核中。LwIP内核会在需要时检测该连接是否收到数据,如果收到了数据,内核会在第一时间调用注册函数,这个过程称为“回调”,这个注册函数被称为“回调函数”。回调函数中有你想要的业务逻辑,在这个函数中,可以自由的处理接收到的数据,也可以发送数据,也就是说,这个回调函数就是你的应用程序。至此,我们可以发现,在回调编程中,LwIP内核把数据交给应用程序的过程只是一次简单的函数调用,这是非常节省时间和空间资源的。每个回调函数实际上只是一个普通的c函数,这个函数在TCP/IP内核中被调用。每一个回调函数都作为一个参数传递给当前的TCP或者UDP连接。为了保存程序的特定状态,可以向回调函数传递一个指定的状态,并且这个指定的状态是独立于TCP/IP协议栈的。
在有操作系统的环境中,如果使用RAW/Callback API,用户的应用程序就以回调函数的形式成为了内核代码的一部分,用户应用程序和内核程序会处于同一个线程之中,这就省去了任务间通信和切换任务的开销。
RAW/Callback API的优点
1.可以在没有操作系统的环境中使用。
2.在有操作系统的环境中使用时,对比另外两种API,可以提高应用程序的效率,节省内存开销。
缺点
1.基于回调函数开发应用程序时的思维过程比较复杂,利用回调函数去实现复杂的业务逻辑时会很麻烦,而且代码的可读性较差。
2.在操作系统环境中,应用程序代码与内核代码处于同一线程,虽然能够节省任务间通信和切换任务的开销,但是相应地,应用程序的执行会制约内核程序的执行,不同的应用程序之间也会相互制约。在应用程序执行的过程中,内核程序将暂停运行,这会影响网络数据包的处理效率。如果应用程序占用的时间过长,而且恰巧这时又有大量的数据包到达,由于内核代码长时间得不到运行,网卡接收缓存里的数据包就程序积累,最后满载丢包。
Netconn API
在操作系统环境中,可以使用Netconn API 或者Socket API进行网络应用程序的开发。Netconn API是基于操作系统的IPC机制(即信号量和邮箱机制)实现的,它将LwIP内核代码和网络应用程序分离成了独立的线程。如此一来,LwIP内核线程就只负责数据包的TCP/IP封装和拆封,而不用进行数据的应用层处理,大大提高了系统对网络数据包的处理效率。
在操作系统环境中,LwIP内核会被实现为一个独立的线程,名为tcpip_thread,使用Netconn API 或者Socket API的应用程序处在不同的线程中,我们可以根据任务的重要性分配不同的优先级给这些线程,从而保证重要任务的时效性。分配优先级的原则如下表:
| 线程 | 优先级 |
|---|---|
| LwIP内核线程tcpip_thread | 0 |
| 重要的网络应用程序 | 1 |
| 不太重要而且处理数据比较耗时的网络应用程序 | 2 |
Netconn API使用了操作系统的IPC机制,对网络连接进行了抽象,用户可以像操作文件一样操作网络连接(打开/关闭、读/写数据)。
但是Netconn API并不像操作文件的API那样简单易用。举个例子,调用f_read函数读文件时,读到的数据会被放在一个用户指定的数组中,用户操作起来很方便,而Netconn API的读数据API时,用户获取的不是数组而是一个特殊的数据结构netbuf,用户如果想使用好它,就需要对内核的pbuf和netbuf结构体有所了解,我们会在后续章节中对其进行讲解。Netconn API之所以采取这种设计,是为了避免数据包在内核程序和应用程序之间发生复制,从而降低程序运行效率。如果用户不在于数据递交时的效率问题,也可以把netbuf中的数据取出来复制到一个数组,然后对数组进行处理。
优点
- 用户可以按照操作文件的方式来操作网络连接,简化了编程工作。
- 避免了内核程序和网络应用程序之间的数据复制,提高了数据递交的效率。
Netconn API的缺点
- 内核程序和网络应用程序之间的数据包传递,需要依赖操作系统的信号量和邮箱机制完成,这需要耗费更多的时间和内存,另外还要加上任务切换的时间开销,效率较低。
- 需要用户对LwIP内核所使用的数据结构有一定的了解,易用性不如Socket API好。
Socket API
Socket即套接字,它对网络连接进行了高级的抽象,使用户可以像操作文件一样操作网络连接,十分易用。许多网络开发人员最早接触的就是Socket编程,Socket已经成为网络编程的标准。在不同的系统中,运行着不同的TCP/IP,但是只要它实现了Socket接口,那么用Socket编写的网络应用程序就能在其中运行,可见用Socket编写的网络应用程序具有很好的可移植性。
不同的系统有一套自己的Socket接口。Windows系统中支持的是WinSock,UNIX/Linux系统中支持的是BSD Socket,它们虽然风格不一致,但大同小异。LwIP中的Socket API是BSD Socket,但是LwIP并没有也没办法实现全部的BSD Socket,如果开发人员想要移植UNIX/Linux系统中的网络应用程序到使用LwIP的系统中,就要注意这一点。
优缺点
更好的易用性、可读性、便于维护、便于移植。
Socket API在内核程序和应用程序之间存在数据的复制,这会降低数据递交的效率。另外,LwIP的Socket API是基于Netconn API实现的,所以在效率上相较前者有所降低。
6826

被折叠的 条评论
为什么被折叠?



