lwip raw、netcoon、socket三种接口编程的区别

目录

一、前言

二、LWIP 简介

三、LWIP RAW 编程

1.概念与原理

2.编程模型与流程

3.示例代码

4.优点与缺点 

四、LWIP NETCONN 编程

1.概念与原理

2.编程模型与流程

3.示例代码

4.优点与缺点 

五、LWIP SOCKET 编程

1.概念与原理

2.编程模型与流程

3.示例代码

4.优点与缺点 

六、三种接口编程的区别总结

1.抽象层次

2.编程难度

3.性能

4.可移植性

5.适用场景

七、总结


一、前言

        在嵌入式网络编程中,LWIP(Lightweight IP)是一个轻量级的 TCP/IP 协议栈,广泛应用于 STM32 等微控制器平台。LWIP 提供了多种编程接口,其中 RAW、NETCONN 和 Socket 是比较常用的三种。本文将详细介绍这三种接口编程的区别,帮助开发者更好地选择适合自己项目的编程方式。

二、LWIP 简介

        LWIP 是一个开源的轻量级 TCP/IP 协议栈,专门为嵌入式系统设计。它具有占用内存少、运行效率高、可移植性强等特点,非常适合在资源有限的微控制器上使用。LWIP 支持多种网络协议,包括 TCP、UDP、IP、ICMP 等,可以满足大多数嵌入式网络应用的需求。

三、LWIP RAW 编程

1.概念与原理

        RAW API 是 LWIP(Lightweight IP)提供的最底层的网络编程接口。它直接基于协议控制块(PCB,Protocol Control Block)进行操作,与 TCP/IP 协议栈的交互最为紧密。例如,对于 TCP 编程,开发者需要直接操作 TCP 的 PCB 来管理连接状态、发送和接收数据等操作。这种方式可以让开发者深入理解协议栈的工作机制,实现对网络通信的精细控制。

2.编程模型与流程

  • 创建连接:以 TCP 为例,通过tcp_new函数创建一个 TCP 协议控制块,然后可以使用tcp_bind函数将其绑定到指定的本地端口,再使用tcp_listen函数使该连接进入监听状态(对于服务器端)。对于客户端,还需要使用tcp_connect函数来建立与服务器的连接。
  • 数据收发:在接收数据方面,需要注册一个接收回调函数,当有数据到达时,LWIP 会调用这个回调函数。在回调函数中,通过操作pbuf(协议缓冲区)来处理接收到的数据。发送数据时,使用tcp_write函数将数据写入发送缓冲区,然后通过tcp_output函数将数据发送出去。
  • 连接关闭:使用tcp_close函数来关闭 TCP 连接,并且通常还需要调用tcp_abort函数在一些异常情况下(如错误处理)强制关闭连接并释放相关资源。

 

3.示例代码

简单的 TCP 服务器接收数据并打印

#include "lwip/tcp.h"
#include "lwip/err.h"
#include <stdio.h>// 定义服务器监听端口
#define SERVER_PORT 8080
// 接收缓冲区大小
#define RX_BUFFER_SIZE 1024// TCP接收回调函数
static err_t tcp_server_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
{if (err == ERR_OK && p!= NULL){char buffer[RX_BUFFER_SIZE];int copied_bytes = 0;struct pbuf *q = p;while (q!= NULL){int bytes_to_copy = (q->len < (RX_BUFFER_SIZE - copied_bytes))? q->len : (RX_BUFFER_SIZE - copied_bytes);memcpy(&buffer[copied_bytes], q->payload, bytes_to_copy);copied_bytes += bytes_to_copy;q = q->next;}// 打印接收到的数据printf("Received data: %s", buffer);pbuf_free(p);}else if (err == ERR_OK && p == NULL){// 客户端正常关闭连接}else{// 出现错误,关闭连接tcp_close(tpcb);}return ERR_OK;
}void tcp_server_raw()
{struct tcp_pcb *server_pcb;err_t err;// 创建TCP协议控制块server_pcb = tcp_new();if (server_pcb == NULL){printf("Error creating TCP PCB\n");return;}// 绑定端口err = tcp_bind(server_pcb, IP_ADDR_ANY, SERVER_PORT);if (err!= ERR_OK){printf("Error binding TCP socket: %d\n", err);tcp_close(server_pcb);return;}// 设置为监听状态server_pcb = tcp_listen(server_pcb);if (server_pcb == NULL){printf("Error listening for connections\n");tcp_close(server_pcb);return;}// 设置接收回调函数tcp_recv(server_pcb, tcp_server_recv);
}

4.优点与缺点 

  • 优点
    • 提供了对协议栈的最大控制权限,能够实现高度定制化的网络应用。对于需要深入优化网络性能、实现特殊协议功能或者对资源使用要求极为严格的场景非常有用。
    • 占用资源相对较少,因为没有高层抽象带来的额外开销,代码执行效率可能较高。
  • 缺点
    • 编程难度较大,要求开发者对 TCP/IP 协议栈有深入的理解,需要手动处理很多协议细节,如连接状态管理、缓冲区操作等,代码编写较为复杂,容易出错。
    • 可移植性和代码复用性相对较差,因为 RAW API 与 LWIP 协议栈的耦合度很高,在不同的 LWIP 版本或者其他协议栈环境下可能需要大量修改代码。

四、LWIP NETCONN 编程

1.概念与原理

        NETCONN 是 LWIP 提供的一种介于 RAW API 和 Socket API 之间的中间层次的网络编程接口。它对底层协议细节进行了一定程度的封装,提供了一种相对简单的方式来进行网络连接和数据传输。NETCONN API 基于消息传递机制,通过创建和操作网络连接对象来实现网络通信,隐藏了一些底层的协议控制块操作。

2.编程模型与流程

  • 创建连接:对于 TCP 服务器端,首先使用netconn_new函数创建一个NETCONN_TCP_LISTEN类型的网络连接对象,然后通过netconn_bind函数将其绑定到指定端口,再使用netconn_listen函数使连接进入监听状态。客户端则创建NETCONN_TCP类型的连接对象,并使用netconn_connect函数连接到服务器的 IP 地址和端口。
  • 数据收发:使用netconn_write函数发送数据,该函数会自动处理数据缓冲和发送操作。接收数据时,使用netconn_read函数,它会阻塞当前任务(如果没有数据到达),直到接收到数据或者出现错误。
  • 连接关闭:使用netconn_close函数关闭网络连接,这个函数会自动释放与连接相关的内部资源。

3.示例代码

简单的 TCP 服务器接收数据并打印

#include "lwip/netconn.h"
#include "lwip/err.h"
#include <stdio.h>#define SERVER_PORT 8080
#define RX_BUFFER_SIZE 1024void tcp_server_netconn()
{struct netconn *conn, *newconn;err_t err;char buffer[RX_BUFFER_SIZE];// 创建TCP监听连接conn = netconn_new(NETCONN_TCP);if (conn == NULL){printf("Error creating TCP listen connection\n");return;}// 绑定到指定端口err = netconn_bind(conn, NULL, SERVER_PORT);if (err!= ERR_OK){printf("Error binding TCP connection: %d\n", err);netconn_delete(conn);return;}// 开始监听netconn_listen(conn);while (1){// 接受客户端连接err = netconn_accept(conn, &newconn);if (err == ERR_OK){// 接收客户端数据err = netconn_read(newconn, buffer, sizeof(buffer), -1);if (err == ERR_OK){// 打印接收到的数据printf("Received data: %s", buffer);}// 关闭客户端连接netconn_close(newconn);netconn_delete(newconn);}}// 关闭服务器监听连接netconn_close(conn);netconn_delete(conn);
}

4.优点与缺点 

  • 优点
    • 相对 RAW API 而言,编程难度降低,抽象程度较高,开发者不需要深入了解协议控制块的细节,能够更快速地开发网络应用。它提供了比较方便的接口用于数据收发和连接管理。
    • 代码结构相对清晰,对于简单的网络应用开发效率较高,并且在一定程度上提高了代码的可维护性。
  • 缺点
    • 性能可能不如 RAW API,因为其内部的封装和抽象会带来一定的开销,例如在数据传输过程中可能会涉及更多的缓冲和消息传递操作。
    • 虽然比 RAW API 的可移植性好一些,但仍然与 LWIP 协议栈有一定的依赖关系,在非 LWIP 环境下可能无法使用。

五、LWIP SOCKET 编程

1.概念与原理

        LWIP 中的 Socket 编程是基于 BSD - sockets(Berkeley Sockets)模型实现的。它是一种广泛应用于网络编程的标准接口,提供了一种与操作系统网络接口类似的编程方式。Socket API 在 LWIP 协议栈之上构建了一个高层次的抽象层,使得开发者可以使用熟悉的socketbindlistenacceptsendrecv等函数来进行网络编程,隐藏了 LWIP 协议栈的底层细节。

2.编程模型与流程

  • 创建套接字:使用socket函数创建一个套接字,指定协议族(如AF_INET表示 IPv4)、套接字类型(如SOCK_STREAM表示 TCP,SOCK_DGRAM表示 UDP)和协议(一般为 0,表示使用默认协议)。
  • 绑定与监听(服务器端):对于服务器端,通过bind函数将套接字绑定到特定的 IP 地址和端口号。然后使用listen函数让套接字进入监听状态,等待客户端连接。
  • 连接服务器(客户端):客户端使用connect函数指定服务器的 IP 地址和端口号来建立与服务器的连接(对于 TCP)。对于 UDP,虽然没有连接建立的过程,但也可以使用connect函数指定目标地址,这样后续的sendrecv操作会更方便。
  • 数据收发:使用sendrecv函数(或者它们的变体,如sendtorecvfrom用于 UDP)进行数据的发送和接收。这些函数直接操作底层的套接字,需要开发者更加注意数据的格式、长度和缓冲区管理等细节。
  • 连接关闭:使用close函数关闭套接字,释放相关资源。在关闭之前,需要确保数据已经发送或接收完成,避免数据丢失或连接异常。

3.示例代码

#include "lwip/sockets.h"
#include "lwip/netdb.h"
#include <stdio.h>#define SERVER_PORT 8080
#define RX_BUFFER_SIZE 1024void tcp_server_socket()
{int server_socket, client_socket;struct sockaddr_in server_addr, client_addr;socklen_t client_addr_len;char buffer[RX_BUFFER_SIZE];int bytes_received;// 创建服务器套接字server_socket = socket(AF_INET, SOCK_STREAM, 0);if (server_socket < 0){printf("Error creating server socket\n");return;}// 配置服务器地址结构体server_addr.sin_family = AF_INET;server_addr.sin_port = htons(SERVER_PORT);server_addr.sin_addr.s_addr = htonl(IP_ADDR_ANY);// 绑定套接字到指定端口和IP地址if (bind(server_socket, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0){printf("Error binding server socket\n");close(server_socket);return;}// 将套接字设置为监听状态if (listen(server_socket, 5) < 0){printf("Error listening on server socket\n");close(server_socket);return;}while (1){client_addr_len = sizeof(client_addr);// 接受客户端连接client_socket = accept(server_socket, (struct sockaddr *)&client_addr, &client_addr_len);if (client_socket < 0){printf("Error accepting client connection\n");continue;}// 接收客户端数据bytes_received = recv(client_socket, buffer, sizeof(buffer), 0);if (bytes_received > 0){// 打印接收到的数据printf("Received data: %s", buffer);}// 关闭客户端套接字close(client_socket);}// 关闭服务器套接字(正常情况下不会执行到这里)close(server_socket);
}

4.优点与缺点 

  • 优点
    • 编程接口最为通用,与传统的网络编程接口(如 Unix/Linux 下的 Socket 编程)高度相似,对于有网络编程经验的开发者来说非常容易上手。
    • 具有良好的可移植性,在不同的操作系统和网络环境下(只要支持 Socket 接口),代码的改动相对较小。
    • 可以方便地与其他基于 Socket 编程的软件或库进行集成,便于构建复杂的网络应用系统。
  • 缺点
    • 由于其高层次的抽象,可能会损失一些性能,特别是在对性能要求极高的场景下,与 RAW API 相比可能存在差距。
    • 对于 LWIP 协议栈的一些特殊功能或优化可能无法直接利用,因为 Socket API 隐藏了很多底层细节。

六、三种接口编程的区别总结

1.抽象层次

  • RAW API 是最底层的接口,直接操作协议控制块,抽象层次最低。
  • NETCONN API 处于中间层次,对底层协议进行了一定程度的封装,提供了相对简单的网络连接对象操作方式。
  • Socket API 是最高层次的抽象,基于 BSD - sockets 模型,与传统的网络编程接口相似,隐藏了 LWIP 协议栈的底层细节。

2.编程难度

  • RAW API 编程难度最大,需要开发者对 TCP/IP 协议栈有深入的理解,手动处理很多协议细节。
  • NETCONN API 编程难度适中,相对 RAW API 来说,抽象程度较高,开发者不需要深入了解协议控制块的细节。
  • Socket API 编程难度最小,对于有网络编程经验的开发者来说非常容易上手,因为它与传统的网络编程接口相似。

3.性能

  • RAW API 性能可能最高,因为没有高层抽象带来的额外开销,能够实现对协议栈的精细控制。
  • NETCONN API 性能介于 RAW API 和 Socket API 之间,内部的封装和抽象会带来一定的开销。
  • Socket API 性能可能最低,由于高层次的抽象,可能会涉及更多的缓冲和系统调用等操作。

4.可移植性

  • RAW API 可移植性最差,与 LWIP 协议栈的耦合度很高,在不同的 LWIP 版本或者其他协议栈环境下可能需要大量修改代码。
  • NETCONN API 可移植性较好,比 RAW API 更容易在不同的 LWIP 版本之间移植,但仍然与 LWIP 协议栈有一定的依赖关系。
  • Socket API 可移植性最好,与传统的网络编程接口相似,在不同的操作系统和网络环境下(只要支持 Socket 接口),代码的改动相对较小。

5.适用场景

  • RAW API 适用于需要深入优化网络性能、实现特殊协议功能或者对资源使用要求极为严格的场景。
  • NETCONN API 适用于对编程难度有一定要求,但又不想过于深入底层协议细节的场景,例如简单的网络应用开发。
  • Socket API 适用于有网络编程经验的开发者,以及需要与其他基于 Socket 编程的软件或库进行集成的场景。

七、总结

        综上所述,STM32 中 LWIP 的 RAW、NETCONN 和 Socket 三种接口编程各有优缺点,开发者应根据具体的项目需求和自身的编程经验来选择合适的编程方式。如果对性能要求极高,并且有深入了解 TCP/IP 协议栈的能力,可以选择 RAW API;如果希望在一定程度上降低编程难度,同时又不想牺牲太多性能,可以选择 NETCONN API;如果有网络编程经验,并且注重代码的可移植性和通用性,可以选择 Socket API

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/61747.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【XGlassTerminal.js】快速 构建 炫酷 终端 网页 以及 Linux 模拟器 在线!!

XGlassTerminal.js XGlassTerminal.js 是一个用于构建前端终端样式的 JavaScript 库。它允许开发者轻松地创建一个具有终端风格的用户界面&#xff0c;并对用户输入的命令进行事件处理。 该库提供了丰富的功能&#xff0c;包括文本添加、命令处理、点击事件绑定等。 同时还支…

车辆传动系统的simulink建模与仿真,分析加速和刹车两个工况

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 5.完整工程文件 1.课题概述 车辆传动系统的simulink建模与仿真,分析加速和刹车两个工况。模型包括车辆模块&#xff0c;传动模块&#xff0c;发动机模块&#xff0c;换挡模块&#xff0c;刹车油门输入模块…

宝塔配置定时任务详解

文章目录 宝塔配置定时任务详解一、引言二、配置定时任务1、登录宝塔面板2、添加定时任务2.1、步骤 3、配置任务3.1、设置任务名称和执行周期3.2、设置执行命令 4、保存并测试 三、使用示例1、备份数据库2、清理日志文件 四、总结 宝塔配置定时任务详解 一、引言 在服务器管理…

【C++笔记】map和set的使用

【C笔记】map和set的深度剖析 &#x1f525;个人主页&#xff1a;大白的编程日记 &#x1f525;专栏&#xff1a;C笔记 文章目录 【C笔记】map和set的深度剖析前言一.set1.1 序列式容器和关联式容器1.2 set系列的使用1.3 set类的介绍1.4 set的构造和迭代器1.5 set的增删查1.6…

springboot+mybatis对接使用postgresql中PostGIS地图坐标扩展类型字段

方案一&#xff08;完全集成和自动解析&#xff09;&#xff1a; <dependency><groupId>org.postgresql</groupId><artifactId>postgresql</artifactId></dependency> 使用 org.postgresql.geometric包下的 PGpoint 类来接收数据库中POINT…

《只狼》运行时提示“mfc140u.dll文件缺失”是什么原因?“找不到mfc140u.dll文件”要怎么解决?教你几招轻松搞定

《只狼》运行时提示“mfc140u.dll文件缺失”的科普与解决方案 作为一名软件开发从业者&#xff0c;在游戏开发和维护过程中&#xff0c;我们经常会遇到各种运行时错误和系统报错。今天&#xff0c;我们就来探讨一下《只狼》这款游戏在运行时提示“mfc140u.dll文件缺失”的原因…

华为HarmonyOS 让应用快速拥有账号能力 -- 3 获取用户手机号

场景介绍 当应用对获取的手机号时效性要求不高时&#xff0c;可使用Account Kit提供的手机号授权与快速验证能力&#xff0c;向用户发起手机号授权申请&#xff0c;经用户同意授权后&#xff0c;获取到手机号并为用户提供相应服务。以下只针对Account kit提供的手机号授权与快…

linux环境人大金仓数据库修改密码

1.进入人大金仓安装目录 cd /home/opt/Kingbase/ES/V9/Server/bin2.连接数据库 ./ksql -U system -d mydb -h 127.0.0.1 -p 54321-u 用户名 -d 数据库名 -h ip地址 -p 端口号 3.修改密码 ALTER USER system WITH PASSWORD 密码;

使用R语言进行美国失业率时空分析(包括绘图)

今天写一篇利用R语言&#xff0c;针对面板数据的简单分析与绘图。让我们直接开始把。 一、数据准备 这次的示例数据非常简单&#xff0c;只有一个shp格式的美国区县矢量数据&#xff0c;我们在QGIS中打开数据查看一下它的属性表。事实上我们需要的数据都在属性表的字段中。 二…

单片机几大时钟源

在单片机中&#xff0c;MSI、HSI和HSE通常指的是用于内部晶振配置的不同功能模块&#xff1a; MSI (Master Oscillator System Interface)&#xff1a;这是最低级的一种时钟源管理单元&#xff0c;它控制着最基本的系统时钟&#xff08;SYSCLK&#xff09;&#xff0c;一般由外…

前端开发 之 15个页面加载特效上【附完整源码】

文章目录 一&#xff1a;彩球环绕加载特效1.效果展示2.HTML完整代码 二&#xff1a;跷跷板加载特效1.效果展示2.HTML完整代码 三&#xff1a;两个圆形加载特效1.效果展示2.HTML完整代码 四&#xff1a;半环加载特效1.效果展示2.HTML完整代码 五&#xff1a;音乐波动加载特效1.效…

Spring入园须知

序 聊 Spring&#xff0c;先从发展历史谈起&#xff0c;对整个生态有个大致认识&#xff0c;最后再看下 Spring 依赖的基础机制——IoC 和 AOP&#xff0c;就达到入门须知的目的了。毕竟 Spring 太大了&#xff0c;如果把话题扯得太宽泛太细节&#xff0c;很可能会迷失在 Spri…

作品截图_

openstack project create --domain default --description "姓氏" xingopenstack user create --domain default --password-prompt --description "名字" mingziopenstack role create --description "姓名首字母" xmzopenstack role add --pr…

使用API管理Dynadot域名,设置默认域名服务器ip信息

前言 Dynadot是通过ICANN认证的域名注册商&#xff0c;自2002年成立以来&#xff0c;服务于全球108个国家和地区的客户&#xff0c;为数以万计的客户提供简洁&#xff0c;优惠&#xff0c;安全的域名注册以及管理服务。 Dynadot平台操作教程索引&#xff08;包括域名邮箱&…

聚合支付系统官方个人免签系统三方支付系统稳定安全高并发

系统采用fastadmin框架独立全新开发&#xff0c;安全稳定,系统支持代理、商户、码商等业务逻辑。 针对最近一-些JD&#xff0c;TB等业务定制&#xff0c;子账号业务逻辑API 非常详细&#xff0c;方便内置对接! 注意&#xff1a;系统没有配置文档很使用教程&#xff0c;不清楚…

vue结合canvas动态生成水印效果

在 Vue 项目中添加水印可以通过以下几种方式实现&#xff1a; 方法一&#xff1a;使用 CSS 直接通过 CSS 的 background 属性实现水印&#xff1a; 实现步骤 在需要添加水印的容器中设置背景。使用 rgba 设置透明度&#xff0c;并通过 background-repeat 和 background-size…

S32K324 信息安全开发-Secure Debug原理及其实现

文章目录 前言Secure debug原理LC(Life Cycle)Application debug key/password (ADKP)固定密钥的实现方案一机一密实现方案AUTH_MODE的区别代码实现ADKP写入确认认证方式写入LC总结前言 车载信息安全对于MCU的要求越来越高,debug口作为直接刷写调试程序的通道,对其进行保护是…

iptables之地址转换

1、自定义链 iptables -N hello #在filter表中创建一个自定义链&#xff0c;链名hello,自定义链名可以任意大小写 iptables -E hello happy #修改自定义的链名 iptables -t filter -I happy -s 192.168.206.30 -p icmp -j REJECT #禁止192.168.206.30ping本机 自定义没有策略&a…

[241129] Docker Desktop 4.36 发布:企业级管理功能、WSL 2 增强 | Smile v4.0.0 发布

目录 Docker Desktop 4.36 发布&#xff1a;企业级管理功能、WSL 2 和 ECI 增强Smile v4.0.0 发布&#xff01;Java 机器学习库迎来重大升级 Docker Desktop 4.36 发布&#xff1a;企业级管理功能、WSL 2 和 ECI 增强 Docker Desktop 4.36 带来了强大的更新&#xff0c;简化了…

Linux信号集、信号的阻塞信号驱动

一、信号集 信号的三种方式&#xff0c;即使是忽略&#xff0c;也仍然打断了进程的进行&#xff08;相当于是捕捉了信号&#xff0c;执行的了空函数什么都没做&#xff09;&#xff0c;可如果在访问重要资源时不希望被打断呢&#xff1f; 可以用阻塞&#xff0c;即产生了信号却…