LWIP(二)——LWIP有3种编程接口,RAW、NETCONN和SOCKET

LWIP提供了三种编程接口:RAW、NETCONN和SOCKET,它们在易用性和效率上有不同权衡。RAW是最底层的接口,适用于无操作系统环境,回调函数是其核心。NETCONN在操作系统中使用IPC机制,提高了处理效率并解耦了内核和应用。SOCKET则提供高级抽象,易于使用且具有良好的可移植性,但在数据递交效率上可能较低。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

1. RAW

2. NETCONN

3、SOCKET


        LWIP有3种编程接口,RAWNETCONNSOCKET。它们的易用性从左到右依次提高,而执行效率从左到右依次降低,用户可以根据实际情况,平衡利弊,选择合适的 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 实现的,所以效率上相较前者要打个折扣。

### lwIP Socket 编程 API 文档与教程 #### 一、lwIP 的两种主要 API 接口概述 lwIP 提供了 Sequential API RAW API 两种应用编程接口用于与 TCP/IP 协议栈交互[^1]。其中,Socket API 构建于 Sequential API 上面,旨在保持与其他平台(如 Unix/Windows)上的套接字 API 兼容性,以便移植已有应用程序到嵌入式环境中[^2]。 #### Socket API 特点 尽管力求兼容,但由于 lwIP 自身特性资源限制的原因,在某些情况下仍需对原有代码做出适当调整以适应新的环境需求。对于大多数开发者来说,使用基于 NETCONN 的高层抽象——即 Socket 或者 Netconn API 来开发网络应用会更加便捷高效[^3]。 #### 三、获取官方文档及学习资料的方法 为了更好地理解掌握 lwIP 中的 Socket 编程技巧,建议访问 lwIP 官方网站下载最新版本的手册指南。此外,GitHub 上也有很多开源项目提供了丰富的实例代码可供参考学习。同时也可以查阅 STM32CubeMX 集成开发工具附带的帮助文件中的相关内容,因为很多微控制器厂商都会针对自家产品定制化集成 lwIP 并给出详细的说明文档。 ```c // 创建一个新的TCP连接并监听端口8080 struct sockaddr_in addr; int sockfd, new_sockfd; sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (sockfd < 0) { perror("socket"); } memset(&addr, &#39;\0&#39;, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = htonl(INADDR_ANY); addr.sin_port = htons(8080); bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)); listen(sockfd, 5); // 设置最大等待队列长度为5 new_sockfd = accept(sockfd, NULL, NULL); // 接受客户端请求建立新连接 ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值