第二节 LwIP简介

本专栏使用的是LwIP 2.1.2版本 ,官方下载链接:http://savannah.nongnu.org/projects/lwip/。

LwIP 的优缺点

本专栏以LwIP 2. 1.2 为主要对象进行讲解,后续中出现的LwIP 如果没有特殊声明,均指2.1.2 版本。此时的LwIP 2. 1.2 为最新版本,可能当这本书写完的时候,LwIP 又被更新了,对于学习而言,大家其实不必纠结于是否必须用最新的版本,因为2.1.2 版本和它后面的版本在移植和应用方法上并没有什么区别。

LwIP 全名:Light weight IP,意思是轻量化的TCP/IP 协议,是瑞典计算机科学院(SICS) 的AdamDunkels 开发的一个小型开源的TCP/IP 协议栈。LwIP 的设计初衷是:用少量的资源消耗实现一个较为完整的TCP/IP 协议栈,其中“完整”主要指的是TCP 协议的完整性,实现的重点是在保持TCP 协议主要功能的基础上减少对RAM 的占用。此外LwIP 既可以移植到操作系统上运行,也可以在无操作系统的情况下独立运行。

LwIP 具有主要特性:

  1. 支持ARP 协议(以太网地址解析协议)。
  2. 支持ICMP 协议(控制报文协议),用于网络的调试与维护。
  3. 支持IGMP 协议(互联网组管理协议),可以实现多播数据的接收。
  4. 支持UDP 协议(用户数据报协议)。
  5. 支持TCP 协议(传输控制协议),包括阻塞控制、RTT 估算、快速恢复和快速转发。
  6. 支持PPP 协议(点对点通信协议),支持PPPoE。
  7. 支持DNS(域名解析)。
  8. 支持DHCP 协议,动态分配IP 地址。
  9. 支持IP 协议,包括IPv4、IPv6 协议,支持IP 分片与重装功能,多网络接口下的数据包转发。
  10. 支持SNMP 协议(简单网络管理协议)。
  11. 支持AUTOIP,自动IP 地址配置。
  12. 提供专门的内部回调接口(Raw API),用于提高应用程序性能。
  13. 提供可选择的Socket API、NETCONN API (在多线程情况下使用) 。

LwIP 在嵌入式中使用有以下优点:

  1. 资源开销低,即轻量化。LwIP 内核有自己的内存管理策略和数据包管理策略,使得内核处理数据包的效率很高。另外,LwIP 高度可剪裁,一切不需要的功能都可以通过宏编译选项去掉。LwIP 的流畅运行需要40KB 的代码ROM 和几十KB 的RAM,这让它非常适合用在
    内存资源受限的嵌入式设备中。
  2. 支持的协议较为完整。几乎支持TCP/IP 中所有常见的协议,这在嵌入式设备中早已够用。
  3. 实现了一些常见的应用程序:DHCP 客户端、DNS 客户端、HTTP 服务器、MQTT 客户端、TFTP 服务器、SNTP 客户端等等。
  4. 同时提供了三种编程接口:RAW API、NETCONN API(注:NETCONN API 即为SequentialAPI,为了统一,下文均采用NETCONN API)和Socket API。这三种API 的执行效率、易用性、可移植性以及时空间的开销各不相同,用户可以根据实际需要,平衡利弊,选择合适的API 进行网络应用程序的开发。
  5. 高度可移植。其源代码全部用C 实现,用户可以很方便地实现跨处理器、跨编译器的移植。另外,它对内核中会使用到操作系统功能的地方进行了抽象,使用了一套自定义的API,用户可以通过自己实现这些API,从而实现跨操作系统的移植工作。
  6. 开源、免费,用户可以不用承担任何商业风险地使用它。
  7. 相比于嵌入式领域其它的TCP/IP 协议栈,比如uC-TCP/IP、FreeRTOS-TCP 等,LwIP 的发展历史要更悠久一些,得到了更多的验证和测试。LwIP 被广泛用在嵌入式网络设备中,国内一些物联网公司推出的物联网操作系统,其TCP/IP 核心就是LwIP;物联网知名的WiFi模块ESP8266,其TCP/IP 固件,使用的就是LwIP。

LwIP 尽管有如此多的优点,但它毕竟是为嵌入式而生,所以并没有很完整地实现TCP/IP 协议栈。相比于Linux 和Windows 系统自带的TCP/IP 协议栈,LwIP 的功能不算完整和强大。但对于大多数物联网领域的网络应用程序,LwIP 已经足够了。

LwIP 的文件说明

如何获取LwIP 源码文件

LwIP 的代码已经交给Savannah 托管,LwIP 的项目主页是:http://savannah.nongnu.org/projects/lwip/。这个主页简单地介绍了一下LwIP,然后给出了许多链接,你可以通过这些链接去挖掘更多关于LwIP 的信息。在这里,我们只关注两个地方,如图中的方框所示。

第二节 LwIP简介_第1张图片

点击“Project Homepage”,会得到一个网页,如图2_2 所示。这个网页可以看成是LwIP 的官方说明文档。我们可以通过这个网页获得关于LwIP 的很多信息,包括LwIP 的使用注意、数据的拷贝、系统初始化流程、多线程中要注意的问题、优化方法、内核模块的分类介绍、内核数据结构、内核重要全局变量、内核源码文件等。这些内容专业性比较强,不建议初学时在它上面花费精力,并且里面的很多内容在我们专栏的后续章节中中会有所讲解。在这里,我们只要知道有这么个东西就行了。

第二节 LwIP简介_第2张图片
第二节 LwIP简介_第3张图片

点击“Download Area”,会得到一个网页,如图2_3 所示。通过这个网页,我们可以下载到LwIP所有版本的源代码包和contrib 包。你每点击一个红色字体的资源链接,浏览器就会开启一个ftp连接,帮助你下载想要的文件到电脑中。但是这个页面提供的下载链接,在国内一般是没有响应的。这个网页最下方的黑字内容推荐我们使用另外一个下载页面:http://download-mirror.savannah.gnu.org/releases/。在这个页面下,用户可以下载到所有在Savannah 托管的开源软件,但我们只关心LwIP。利用浏览器的搜索功能,快捷键Ctrl+F,可以快速找到lwip 目录。在这里为了方便,我们直接给出最终的下载链接:http://download-mirror.savannah.gnu.org/releases/lwip/。

可能有人会问,什么是contrib 包,它与源代码包有什么不同?源代码包里面装的主要是LwIP内核的源码文件,而contrib 包里面装的是移植和应用LwIP 的一些demo,即应用示例。contrib包不属于LwIP 内核的一部分,里面的很多内容来自开源社区的贡献,因此contrib 包的版本管理不像内核源码那样严格和规范,但也是很有参考价值的。按理说,LwIP 源码面世越久,开源社区对它的贡献就越大,所以越高版本的contrib 包,提供的应用示例就越丰富,越有参考价值。在大版本区别不大的情况下,建议大家下载最新的contrib 包。后续我们会对contrib 包里面提供的应用示例进行讲解。另外,还有些“.sig”后缀的文件,这是数字签名,大家忽略就好。

LwIP 文件说明

按照上一小节的介绍,我们下载两个包:lwip-2.1.2.zip(源码包)和contrib-2.1.0.zip(contrib 包)。解压以后会得到两个文件夹,如图所示。

在这里插入图片描述

我们先打开“lwip-2.1.2”文件夹,如图所示。

第二节 LwIP简介_第4张图片

该目录的内容为:

  1. CHANGELOG 文件记录了LwIP 在版本升级过程中源代码发生的变化。
  2. COPYING 文件记录了LwIP 这个开源软件的license。一个软件开源,不代表你能无限制地使用它,你需要在使用它的过程中遵守一定的规则,这些规则就是license。大家可以用记事本打开这个COPYING 文件看看它的内容。开源软件的license 有很多种,LwIP 的属于BSD License。LwIP 的开源程度是很高的,你几乎可以无限制地使用它。
  3. FILES 文件用于介绍当前目录下的目录信息。
  4. README 文件对LwIP 进行了一个简单的介绍。
  5. UPGRADING 文件记录了LwIP 每个大版本的更新,会对用户使用和移植LwIP 造成的影响。所谓大版本更新指的是:1.3.x - 1.4.x –2.0.x –2.1.x。小版本更新,比如2.0.1 –2.0.2 –2.0.3,这个过程只是一些bug 的修复和性能的改善,不会对用户的使用造成影响。用户只要将原有工程的目录中与LwIP 相关的旧版本文件替换成新版本的文件,重新编译,就能直接使用。
  6. doc 文件夹里面是关于LwIP 的一些文档,可以看成是应用和移植LwIP 的指南。但是这些文
    档比较零散,不成体系,而且纯文本阅读起来很费劲,阅读意义不是很大。
  7. test 文件夹里面是测试LwIP 内核性能的源码,将它们和LwIP 源码加入到工程中一起编译,调用它们提供的函数,可以获得许多与LwIP 内核性能有关的指标。这种内核性能测试功能,只有非常专业的人士才用的到。
  8. src 文件夹里面就是我们最关心的LwIP 源码文件,下面会详细讲解。

打开src 文件夹,如图所示。
第二节 LwIP简介_第5张图片

api 文件夹里面装的是NETCONN API 和Socket API 相关的源文件,只有在操作系统的环境中,才能被编译。

apps 文件夹里面装的是应用程序的源文件,包括常见的应用程序,如httpd、mqtt、tftp、sntp、snmp等。

core 文件夹里面是LwIP 的内核源文件,后续会详细讲解。

include 文件夹里面是LwIP 所有模块对应的头文件。

netif 文件夹里面是与网卡移植有关的文件,这些文件为我们移植网卡提供了模板,我们可以直接使用。

LwIP 内核是由一系列模块组合而成的,这些模块包括:TCP/IP 协议栈的各种协议、内存管理模块、数据包管理模块、网卡管理模块、网卡接口模块、基础功能类模块、API 模块。每个模块是由相关的几个源文件和头文件组成的,通过头文件对外声明一些函数、宏、数据类型,使得其它模块可以方便地调用此模块的功能。而构成每个模块的头文件都被组织在了include 目录中,而源文件则根据类型被分散地组织在api、apps、core、netif 目录中。

接下来,我们介绍一下core 文件夹,如图 所示。

第二节 LwIP简介_第6张图片

我们逐一介绍一下这些源文件的功能。

ipv4 文件夹里面是与IPv4 模块相关的源文件,它们实现了IPv4 协议规定的对数据包的各种操作。ipv4 文件夹中还包括一些并非属于IP 协议,但会受IP 协议影响的协议源文件,包括DHCP、ARP、ICMP、IGMP。

ipv6 文件夹里面是与IPv6 模块相关的源文件,它们实现了IPv6 协议规定的对数据包的各种操作。ipv6 文件夹中还包括一些并非属于IP 协议,但会受IP 协议影响的协议源文件,包括DHCP、ARP、ICMP、IGMP。

altcp.c、altcp_alloc.c、altcp_tcp.c 等文件是应用程序分层TCP 连接API,从TCPIP 线程使用,是一个抽象层,可以模拟应用程序的tcp 回调API,同时防止直接链接,这样,应用程序可以使用其他应用程序层协议在TCP 之上而不知道细节(例如TLS,代理连接),此类接口我们并没有怎么使用,或者如果选择使用安全的加密传输的话,可以配合mbed TLS 使用。

def.c 文件定义了一些基础类函数,比如主机序和网络序的转换、字符串的查找和比较、整数转换成字符串等,这些函数会被LwIP 内核的很多模块所调用。在include 目录里面的def.h 文件对外声明了def.c 所实现的函数,同时定义了许多宏,能实现一些基础操作,比如取最大值、取最小值、计算数组长度等,这些宏同样也被内核的许多模块所调用。我们经常可以看到某个内核的源文件在开始的地方#include “def.h”。

dns.c 文件实现了域名解析的功能,有了它,用户就可以在知道服务器域名的情况下,获得该服务器的IP 地址。很多时候我们只记得服务器域名而不记得服务器IP 地址,例如“www.baidu.com”就是一个域名,通过dns 功能,我们就可以得到与服务器域名对应的IP 地址,这给用户使用带来很大的方便。

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 模块可以满足他们的需求。

stat.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 内核的绝大部分。

timeouts.c 定义了LwIP 内核的超时处理机制。LwIP 内核中多个模块的实现需要借助超时处理机制,包括ARP 表项的时间统计、IP 分片报文的重装、TCP 的各种定时器、实现各种应用层协议需要的超时处理。

udp.c 文件实现了UDP 协议,包括对UDP 连接的操作和UDP 数据包的操作。

查看LwIP 的说明文档

关于LwIP 的官方说明文档:http://www.nongnu.org/lwip/2_1_x/index.html,我就简单带大家浏览一下。打开连接,我们可以看到LwIP 的Overview(概述),这里就简单看看即可,我们可以点击左侧的“Common pitfalls”,查看一下LwIP 常见的陷阱,可能在使用中会遇到,到时候注意一下即可,在前面的章节中,我们也提到过,LwIP 可以工作在无操作系统环境也可以工作在有操作系统的环境中,Common pitfalls 中提到Mainloop Mode(主函数轮询模式)与OS Mode(操作系统模式)需要注意的一些事情,具体见图。

第二节 LwIP简介_第7张图片

此外,我们还可以点击左侧的“Modules”,查看一些模块相关的说明,以及例子,比如有无操作系统相关的,如,还有基础配置,如LwIP 的内存管理模块,数据包缓冲区等会是在“Modules –>Infrastructure”页面中,具体见图。

第二节 LwIP简介_第8张图片

当然,还有很重要的一些用户常用的API 函数,也是在“Modules”中可以找到,例如Raw API,NETCONN API 和Socket API 等,具体见图。

第二节 LwIP简介_第9张图片

此外还有一些“Applications”应用层相关的说明,如HTTP、MQTT、TFTP 等,具体见图。

第二节 LwIP简介_第10张图片

还有一些数据结构相关的说明,当我们在程序中看到哪个数据结构不懂的,都可以在这里找到对应的说明,也是比较重要的,LwIP 本质就是对数据的处理,其中也使用了大量的数据结构,有空可以多研究研究它,具体见图。

第二节 LwIP简介_第11张图片

当然,我们也能通过函数名字的首字母来查找函数的作用,具体见图。

第二节 LwIP简介_第12张图片

使用vscode 查看源码

查看文件中的符号列表(函数列表)

LwIP 的源码很庞大,我们使用微软的开源软件——vs code 查看源码,并且快速找到源码的函数与定义,首先我们先安装vs code,我们可以在https://code.visualstudio.com/download 中下载时候自己电脑的vs code 版本,然后安装即可。

然后打开我们的源码文件夹,右键,选择Open with Code,这样子就能直接在vs code 打开我们整个文件夹的源码了,具体见图。

第二节 LwIP简介_第13张图片

在vs code 中,就显示了我们打开的源码,LwIP 那么多文件,我们怎么去快速找到源码文件中的某个函数呢?很简单,比如我们知道某个函数的名字的话,可以直接搜索的,这点就不必我多说,但是有时候,我们不记得某个函数的名字,只知道它在哪个文件,或者只知道在好几个文件中的某一个,那么我们就需要一个个去查找这个函数了,vs code 提供很强大的功能,就是可以快速查文件中的符号列表和函数列表,我们首先打开一个源码文件,比如tcp.c,然后我们通过快捷键“Shift+Ctrl+O”即可打开对应源码文件的符号列表和函数列表,通过查看这些列表,就能知道该源码文件中是否有我们需要的函数或者宏定义等,具体见图。

第二节 LwIP简介_第14张图片

第二节 LwIP简介_第15张图片

函数定义跳转

vs code 看源码是非常方便的,比如,我们可以通过F12 按键进行跳转到定义,通过“Alt+F12”速览定义,或者通过快捷键“Ctrl+F12”进行Go to Declaration,这些操作还是很方便的,当然啦,我们也能通过鼠标右键,进行选择,具体见图。如果在查看函数之后,想返回跳转前的位置,只需要通过快捷键“Alt+ 键盘的←(前后左右的左按键)”跳回即可。

第二节 LwIP简介_第16张图片

LwIP 源码里的example

(后面LwIP 的基础例程主要直接使用或参考源码里的example 即可)

我们打开之前下载好的contrib-2.1.0 文件夹,如图所示。

第二节 LwIP简介_第17张图片

我们先讲解一下这个目录:

  1. addons 目录。LwIP 中很多模块的实现,都是可以由用户干预的,比如校验和、TCP 初始序列号。LwIP 的内核代码,通过宏编译选项的设置,可以将内核中某些模块的实现方法配置成LwIP默认的方法,或者用户自定义的方法。用户自定义的方法通常需要用户在钩子函数中实现。在实际应用中,我们采用内核默认的方法就足够了,只有在非常特定的场合下,为了性能、资源开销等因素的考虑,我们可能会需要自己实现相关的模块,或者说编写相应的钩子函数。那么这时该怎么办呢?addons 目录下的内容就为我们提供了参考。对于初学者,没必要关心这个目录。

  2. apps 目录里实现了很多应用层协议。LwIP 源码包中也有apps 目录,但源码包中apps 目录下的应用程序全部用RAW/Callback API 实现,属于内核代码的一部分。而此apps 目录里的应用程序可以是由三种API 中的任何一种实现的。读者可以把它看成是内核源码所提供的应用程序的一个补充。

  3. examples 目录里是一些LwIP 的应用示例。在使用LwIP 开发应用程序时会出现的典型问题,比如如何移植网卡、如何使用LwIP 的API、如何使用源码中提供的应用程序,对于这些问题,这个目录为我们提供了参考。我们在后续的章节中,会使用这个目录中的例子来讲解LwIP 的应用程序。

  4. ports 目录里是一些移植文件,它可以帮助我们将LwIP 移植到某个具体的操作系统中。目前这个目录所提供的移植文件,只支持FreeRTOS、UNIX、Win32。我们会在后续的章节中讲解如何移植LwIP。

LwIP 的三种编程接口

LwIP 提供了三种编程接口,分别为RAW/Callback API、NETCONN API、SOCKET API。它们的易用性从左到右依次提高,而执行效率从左到右依次降低,用户可以根据实际情况,平衡利弊,选择合适的API 进行网络应用程序的开发。以下内容将分别介绍这三种API。

RAW/Callback API

LwIP 提供了三种编程接口,分别为RAW/Callback API、NETCONN API、SOCKET API。它们的易用性从左到右依次提高,而执行效率从左到右依次降低,用户可以根据实际情况,平衡利弊,选择合适的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,可以提高应用程序的效率、节省内存开销。

RAW/Callback API 的优点是显著的,但缺点也是显著的:

  1. 基于回调函数开发应用程序时的思维过程比较复杂。在后面与RAW/Callback API 相关的章节中可以看到,利用回调函数去实现复杂的业务逻辑时,会很麻烦,而且代码的可读性较差。
  2. 在操作系统环境中,应用程序代码与内核代码处于同一个线程,虽然能够节省任务间通信和切换任务的开销,但是相应地,应用程序的执行会制约内核程序的执行,不同的应用程序之间也会互相制约。在应用程序执行的过程中,内核程序将不可能得到运行,这会影响网络数据包的处理效率。如果应用程序占用的时间过长,而且碰巧这时又有大量的数据包到达,由于内核代码长期得不到执行,网卡接收缓存里的数据包就持续积累,到最后很可能因为满载而丢弃一些数据包,从而造成丢包的现象。

NETCONN API

在操作系统环境中,可以使用NETCONN API 或者Socket API 进行网络应用程序的开发。NETCONNAPI 是基于操作系统的IPC 机制(即信号量和邮箱机制)实现的,它的设计将LwIP 内核代码和网络应用程序分离成了独立的线程。如此一来,LwIP 内核线程就只负责数据包的TCP/IP封装和拆封,而不用进行数据的应用层处理,大大提高了系统对网络数据包的处理效率。

前面提到,使用RAW/Callback API 会造成内核程序和网络应用程序、不同网络应用程序之间的相互制约,如果使用NETCONN API 或者Socket API,这种制约将不复存在。

在操作系统环境中,LwIP 内核会被实现为一个独立的线程,名为tcpip_thread,使用NETCONNAPI 或者Socket API 的应用程序处在不同的线程中,我们可以根据任务的重要性,分配不同的优先级给这些线程,从而保证重要任务的时效性,分配优先级的原则具体见表格。

第二节 LwIP简介_第18张图片

NETCONN API 使用了操作系统的IPC 机制,对网络连接进行了抽象,用户可以像操作文件一样操作网络连接(打开/关闭、读/写数据)。但是NETCONN API 并不如操作文件的API 那样简单易用。举个例子,调用f_read 函数读文件时,读到的数据会被放在一个用户指定的数组中,用户操作起来很方便,而NETCONN API 的读数据API,就没有那么人性化了。用户获得的不是一个数组,而是一个特殊的数据结构netbuf,用户如果想使用好它,就需要对内核的pbuf 和netbuf 结构体有所了解,我们会在后续的章节中对它们进行讲解。NETCONN API 之所以采取这种不人性的设计,是为了避免数据包在内核程序和应用程序之间发生拷贝,从而降低程序运行效率。当然,用户如果不在意数据递交时的效率问题,也可以把netbuf 中的数据取出来拷贝到一个数组中,然后去处理这个数组。

简单来说,NETCONN API 的优缺点是:

  1. 相较于RAW/Callback API,NETCONN API 简化了编程工作,使用户可以按照操作文件的方式来操作网络连接。但是,内核程序和网络应用程序之间的数据包传递,需要依靠操作系统的信号量和邮箱机制完成,这需要耗费更多的时间和内存,另外还要加上任务切换的时间开销,效率较低。

  2. 相较于Socket API,NETCONN API 避免了内核程序和网络应用程序之间的数据拷贝,提高了数据递交的效率。但是,NETCONN API 的易用性不如Socket API 好,它需要用户对LwIP 内核所使用数据结构有一定的了解。

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 的系统中,就要注意这一点。

相较于NETCONN API,Socket API 具有更好的易用性。使用Socket API 编写的程序可读性好,便于维护,也便于移植到其它的系统中。Socket API 在内核程序和应用程序之间存在数据的拷贝,这会降低数据递交的效率。另外,LwIP 的Socket API 是基于NETCONN API 实现的,所以效率上相较前者要打个折扣。


参考资料:LwIP 应用开发实战指南—基于野火STM32

你可能感兴趣的:(网络,tcp/ip,网络协议,stm32,lwip)