目录
1. RAW
2. NETCONN
3、SOCKET
LWIP有3种编程接口,RAW、NETCONN和SOCKET。它们的易用性从左到右依次提高,而执行效率从左到右依次降低,用户可以根据实际情况,平衡利弊,选择合适的 API 进行网络应用程序的开发。
1. RAW
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/CallbackAPI 相关的章节中可以看到, 利用回调函数去实现复杂的业务逻辑时, 会很麻烦,而且代码的可读性较差。
(2) 在操作系统环境中, 应用程序代码与内核代码处于同一个线程,虽然能够节省任务间通信和切换任务的开销,但是相应地,应用程序的执行会制约内核程序的执行,不同的应用程序之间也会互相制约。 在应用程序执行的过程中,内核程序将不可能得到运行,这会影响网络数据包的处理效率。如果应用程序占用的时间过长,而且碰巧这时又有大量的数据包到达, 由于内核代码长期得不到执行,网卡接收缓存里的数据包就持续积累,到最后很可能因为满载而丢弃一些数据包,从而造成丢包的现象。
2. NETCONN
在操作系统环境中,可以使用 NETCONN API 或者 Socket API 进行网络应用程序的开发。 NETCONN API 是基于操作系统的 IPC 机制(即信号量和邮箱机制) 实现的, 它的设计将 LwIP 内核代码和网络应用程序分离成了独立的线程。如此一来, LwIP 内核线程就只负责数据包的 TCP/IP 封装和拆封,而不用进行数据的应用层处理,大大提高了系统对网络数据包的处理效率。
前面提到,使用 RAW/Callback API 会造成内核程序和网络应用程序、 不同网络应用程序之间的相互制约,如果使用 NETCONN API 或者 Socket API,这种制约将不复存在。
在操作系统环境中, LwIP 内核会被实现为一个独立的线程, 名为 tcpip_thread,使用NETCONN API 或者 Socket API 的应用程序处在不同的线程中,我们可以根据任务的重要性,分配不同的优先级给这些线程,从而保证重要任务的时效性, 分配优先级的原则具体见下表。
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 内核所使用数据结构有一定的了解。
3、SOCKET
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 实现的,所以效率上相较前者要打个折扣。