2.6 LwIP的3种编程接口

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

2.6.1 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)基于回调函数开发应用程序时的思维过程比较复杂,利用回调函数去实现复杂的业务逻辑时会很麻烦,而且代码的可读性较差。

2)在操作系统环境中,应用程序代码与内核代码处于同一个线程,虽然能够节省任务间通信和切换任务的开销,但是相应地,应用程序的执行会制约内核程序的执行,不同的应用程序之间也会互相制约。在应用程序执行的过程中,内核程序将不可能得到运行,这会影响网络数据包的处理效率。如果应用程序占用的时间过长,而且恰巧这时又有大量的数据包到达,由于内核代码长期得不到执行,网卡接收缓存里的数据包就持续积累,到最后很可能因为满载而丢弃一些数据包,从而造成丢包的现象。

2.6.2 Netconn API

在操作系统环境中,可以使用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的应用程序处在不同的线程中,我们可以根据任务的重要性分配不同的优先级给这些线程,从而保证重要任务的时效性。分配优先级的原则具体如表2-1所示。

表2-1 线程优先级分配原则

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内核所使用的数据结构有一定的了解。

2.6.3 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实现的,所以在效率上相较前者有所降低。