博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
LWIP学习笔记6——使用 NETCONN 接口编程
阅读量:4092 次
发布时间:2019-05-25

本文共 3998 字,大约阅读时间需要 13 分钟。

使用 NETCONN 接口编程

NETCONN API 使用了操作系统的 IPC 机制, 对网络连接进行了抽象,用户可以像操作文件一样操作网络连接(打开/关闭、读/写数据)。 但是 NETCONN API 并不如操作文件的 API 那样简单易用。举个例子,调用 f_read 函数读文件时,读到的数据会被放在一个用户指定的数组中,用户操作起来很方便,而 NETCONN API 的读数据 API,就没有那么人性化了。 用户获得的不是一个数组,而是一个特殊的数据结构 netbuf,用户如果想使用好它,就需要对内核的 pbuf 和 netbuf 结构体有所了解。

netbuf 结构体:
LwIP 为了更好描述应用线程发送与接收的数据,并且为了更好管理这些数据的缓冲区,LwIP 定义了一个 netbuf 结构体,它是基于 pbuf 上更高一层的封装,记录了主机的 IP 地址与端口号。

struct netbuf {
struct pbuf *p, *ptr; (1) ip_addr_t addr; (2) u16_t port; (3) };

在这里插入图片描述

netbuf 相关函数说明:
netbuf 是 LwIP 描述用户数据很重要的一个结构体,因为 LwIP 是不可能让我们直接操作 pbuf 的,因为分层的思想,应用数据必然是由用户操作的, 因此 LwIP 会提供很多函数接口让用户对 netbuf 进行操作,无论是 UDP 报文还是 TCP 报文段,其本质都是数据,要发送出去的数据都会封装在 netbuf 中,然后通过邮箱发送给内核线程(tcpip_thread 线程),然后经过内核的一系列处理,放入发送队列中,然后调用底层网卡发送函数进行发送,反之,应用线程接收到数据,也是通过 netbuf 进行管理。

netconn 结构体:

在 LwIP 中,如 TCP 连接, UDP 通信,都是需要提供一个编程接口给用户使用的,那么为了描述这样子的一个接口, LwIP 抽象出来一个 nettonn 结构体,它能描述一个连接,供应用程序使用,同时内核的 NETCONN API 接口也对各种连接操作函数进行了统一的封装,这样子,用户程序可以很方便使 netconn 和编程函数,我们暂且将 netconn 称之为连接结体。一个连接结构体中包含的成员变量很多,如描述连接的类型,连接的状态(主要是在TCP 连接中使用),对应的控制块(如 UDP 控制块、 TCP 控制块等等),还有对应线程的消息邮箱以及一些记录的信息。

struct netconn {
/** netconn 类型 */ enum netconn_type type; /** 当前 netconn 状态 */ enum netconn_state state; /** LwIP 的控制块指针,如 TCP 控制块、 UDP 控制块 */ union {
struct ip_pcb *ip; struct tcp_pcb *tcp; struct udp_pcb *udp; struct raw_pcb *raw; } pcb; err_t pending_err;/** 这个 netconn 最后一个异步未报告的错误 */ sys_sem_t op_completed; //信号量 /** 消息邮箱,存储接收的数据,直到它们被提取 */ sys_mbox_t recvmbox; /** 用于 TCP 服务器上的请求连接缓冲区 */ sys_mbox_t acceptmbox; /** socket 描述符,用于 Socket API */ #if LWIP_SOCKET int socket; #endif /* LWIP_SOCKET */ /** 标志 */ u8_t flags; #if LWIP_TCP /** 当调用 netconn_write()函数发送的数据不适合发送缓冲区时, 数据会暂时存储在 current_msg 中,等待数据合适的时候进行发送 */ struct api_msg *current_msg; #endif /* LWIP_TCP */ /** 连接相关的回调函数 */ netconn_callback callback; };

netconn 函数接口说明:

netconn_new()

函数 netconn_new ()本质上是一个宏定义,它用来创建一个新的连接结构, 连接结构的类型可以选择为 TCP 或 UDP 等,参数 type 描述了连接的类型,可以为 NETCONN_TCP或 NETCONN_UDP 等, 在这个函数被调用时,会初始化相关的字段,而并不会创建连接。

netconn_bind()

netconn_bind()函数用于将一个 IP 地址及端口号与 netconn 连接结构进行绑定,如果作为服务器端,这一步操作是必然需要的,作为客户端,不需要这一步,系统会自动分配端口号,使用默认网卡发送数据。同样的, 该函数会调用 netconn_apimsg()函数构造一个 API 消息,并且请求内核执行 lwip_netconn_do_bind()函数, 然后通过 netconn 连接结构的信号量进行同步,事实上内核线程的处理也是通过函数调用 xxx_bind(xxx_bing 可以是 udp_bing、 tcp_bing、 raw_bing,具体是哪个函数内核是根据 netconn 的类型决定的) 完成相应控制块的绑定工作。

netconn_connect()

netconn_connect()函数是用于连接服务器的函数,它一般在客户端中调用,将服务器端的 IP 地址和端口号与本地的 netconn 连接结构绑定,当 TCP 协议使用该函数的时候就是进行握手的过程,调用的应用线程将阻塞至握手完成;而对于 UDP 协议来说,调用该函数只是设置 UDP 控制块的目标 IP 地址与目标端口号,其实这个函数也是通过调用netconn_apimsg()函数构造一个 API 消息,并且请求内核执行 lwip_netconn_do_connect()函数, 然后通过 netconn 连接结构的信号量进行同步,在 lwip_netconn_do_connect()函数中,根据 netconn 的类型不同, 调用对应的 xxx_connect()函数进行对应的处理,如果是 TCP 连接,将调用 tcp_connect();如果是 UDP 协议,将调用 udp_connect();如果是RAW,将调用 raw_connect()函数处理。

netconn_recv()

它可以接收一个 UDP 或者 TCP的数据包,从 recvmbox 邮箱中获取pbuf数据包,如果该邮箱中没有数据包,那么线程调用这个函数将会进入阻塞状态以等待消息的到来, 如果在等待 TCP 连接上的数据时,远端主机终止连接,将返回一个终止连接的错误代码(ERR_CLSD),应用程序可以根据错误的类型进行不一样的处理。对应 TCP 连接, netconn_recv()函数将调用 netconn_recv_data_tcp()函数去获取 TCP 连接上的数据,在获取数据的过程中,调用 netconn_recv_data()函数从 recvmbox 邮箱获取pbuf, 然后通过 netconn_tcp_recvd_msg()->netconn_apimsg()函数构造一个 API 消息投递给系统邮箱, 请求内核执行 lwip_netconn_do_recv()函数, 该函数将调用 tcp_recved()函数去更新 TCP 接收窗口,同时netconn_recv()函数将完成 pbuf 数据包封装在 netbuf 中,返回个应用程序; 而对于 UDP 协议、 RAW 连接,将简单多了,将直接调用netconn_recv_data()函数获取数据,完成 pbuf 封装在 netbuf 中,返回给应用程序。

在这里插入图片描述

netconn_send()

该函数会调用 netconn_apimsg()函数构造一个 API 消息,并且请求内核执行 lwip_netconn_do_send()函数, 这个函数会通过消息得到目标 IP 地址与端口号以及 pbuf 数据报等信息, 然后调用 raw_send()/udp_send()等函数发送数据,最后通过 netconn 连接结构的信号量进行同步。

netconn_write()

用于处于稳定连接状态的 TCP 协议发送数据,这个函数的功能是把 dataptr 指针指向的数据放在属于 conn 连接的 TCP 连接的发送队列中, size 参数指定了数据的长度, apiflags 参数有以下几种:

/* 没有标志位(默认标志位) */ #define NETCONN_NOFLAG 0x00 /* 不拷贝数据到内核线程 */ #define NETCONN_NOCOPY 0x00 /* 拷贝数据到内核线程 */ #define NETCONN_COPY 0x01 /* 尽快递交给上层应用 */ #define NETCONN_MORE 0x02 /* 当内核缓冲区满时,不会被阻塞,而是直接返回 */ #define NETCONN_DONTBLOCK 0x04 /* 不自动更新接收窗口,需要调用 netconn_tcp_recvd()函数完成 */ #define NETCONN_NOAUTORCVD 0x08 /* 上层已经收到数据,将 FIN 保留在队列中直到再次调用 */

转载地址:http://rdnii.baihongyu.com/

你可能感兴趣的文章
JS中如何判断一个数字是整数还是小数
查看>>
【JS 函数】递归函数 之 arguments.callee
查看>>
【JS 函数】JS闭包深入了解
查看>>
【react常见问题】Useless constructor no-useless-constructor报错
查看>>
JS中 target和currentTarget的区别
查看>>
安卓移动端固定在底部的按钮被软件盘顶上去的解决方案
查看>>
一篇文章搞懂前置机到底是什么
查看>>
深入理解埋点
查看>>
JS 纯函数及其应用
查看>>
webpack 快速入门教程
查看>>
【vue 实战】 登录/退出实现原理
查看>>
axios 拦截器的用法
查看>>
浏览器跨域常用解决方案总结
查看>>
Css3的Media Query常用方法总结
查看>>
常见前端漏洞及防御方法
查看>>
css3 常见文字处理方法
查看>>
mac 上如何修改 mysql 的 root 密码
查看>>
前端必知的加密方法
查看>>
【前端性能优化】雅虎34条优化军规
查看>>
mpvue 报错 Expected indentation of 2 spaces but found 4解决办法
查看>>