lwIP 细节之五:accept 回调函数是何时调用的

使用 lwIP 协议栈进行 TCP 裸机编程,其本质就是编写协议栈指定的各种回调函数。将你的应用逻辑封装成函数,注册到协议栈,在适当的时候,由协议栈自动调用,所以称为回调

注:除非特别说明,以下内容针对 lwIP 2.0.0 及以上版本。

向协议栈注册回调函数有专门的接口,如下所示:

tcp_err(pcb, errf);							//注册 TCP 接到 RST 标志或发生错误回调函数 errf
tcp_connect(pcb, ipaddr, port, connected);	//注册 TCP 建立连接成功回调函数 connecter
tcp_accept(pcb, accept);					//注册 TCP 处于 LISTEN 状态时,监听到有新的连接接入
tcp_recv(pcb, recv);						//注册 TCP 接收到数据回调函数 recv
tcp_sent(pcb, sent);						//注册 TCP 发送数据成功回调函数 sent
tcp_poll(pcb, poll, interval);				//注册 TCP 周期性执行回调函数 poll

本节讲述 accept 函数。

accept 回调函数

在 TCP 控制块中,函数指针 accept 指向用户实现的函数,当监听到有新的连接接入时,由协议栈调用此函数,通知用户接受了新的连接或者通知用户内存不足。
函数指针 accept 的类型为 tcp_accept_fn ,该类型定义在 tcp.h 中:

/** Function prototype for tcp accept callback functions. Called when a new* connection can be accepted on a listening pcb.** @param arg Additional argument to pass to the callback function (@see tcp_arg())* @param newpcb The new connection pcb* @param err An error code if there has been an error accepting.*            Only return ERR_ABRT if you have called tcp_abort from within the*            callback function!*/
typedef err_t (*tcp_accept_fn)(void *arg, struct tcp_pcb *newpcb, err_t err);

协议栈通过宏 TCP_EVENT_ACCEPT(lpcb,pcb,arg,err,ret) 调用 lpcb->accept 指向的函数。宏 TCP_EVENT_ACCEPT 定义在 tcp_priv.h 中:

#define TCP_EVENT_ACCEPT(lpcb,pcb,arg,err,ret)                 \do {                                                         \if((lpcb)->accept != NULL)                                 \(ret) = (lpcb)->accept((arg),(pcb),(err));               \else (ret) = ERR_ARG;                                      \} while (0)

以关键字 TCP_EVENT_ACCEPT 搜索源码,可以搜索到 2 处使用:

TCP_EVENT_ACCEPT(pcb, NULL, pcb->callback_arg, ERR_MEM, err);
TCP_EVENT_ACCEPT(pcb->listener, pcb, pcb->callback_arg, ERR_OK, err);

1 由 tcp_listen_input 函数调用

处于 LISTEN 状态的 TCP 控制块 ,如果收到客户端发送的 SYN 同步标志,表示一个客户端在请求建立连接了。
lwIP 会为这个新连接申请一个 TCP_PCB ,这一过程在 tcp_listen_input 函数中完成的。然而 TCP_PCB 的个数是有限的,如果申请失败,则会调用错误码为 ERR_MEMaccept 回调函数,向用户报告内存分配失败。简化后的代码为:

static void
tcp_listen_input(struct tcp_pcb_listen *pcb)
{// 通过一系列检查 没有错误  	npcb = tcp_alloc(pcb->prio);	// 申请新的 TCP_PCB if (npcb == NULL) {				// 内存错误处理LWIP_DEBUGF(TCP_DEBUG, ("tcp_listen_input: could not allocate PCB\n"));TCP_EVENT_ACCEPT(pcb, NULL, pcb->callback_arg, ERR_MEM, err);return;}// 申请成功,初始化新申请的pcbnpcb->state = SYN_RCVD;// 发送 ACK|SYN 标志return;
}

这里需要注意,申请 TCP_PCB 失败的处理方法,lwIP 2.1.x 版本与 lwIP 1.4.1 不同
再看看 lwIP 1.4.1 的 tcp_listen_input 函数代码(经简化):

static err_t
tcp_listen_input(struct tcp_pcb_listen *pcb)
{// 通过一系列检查 没有错误  	npcb = tcp_alloc(pcb->prio);	// 申请新的 TCP_PCB if (npcb == NULL) {				// 内存错误处理LWIP_DEBUGF(TCP_DEBUG, ("tcp_listen_input: could not allocate PCB\n"));return ERR_MEM;}// 申请成功,初始化新申请的pcb// 发送 ACK|SYN 标志return ERR_OK;
}

可以看到, lwIP 1.4.1 版本 tcp_listen_input 函数具有返回值,如果申请 TCP_PCB 失败,则返回 ERR_MEM 错误码。而 lwIP 2.1.x 版本 tcp_listen_input 函数不具有返回值(返回类型为 void ),其次,lwIP 2.1.x 版本处理内存错误是通过调用 accept 回调函数来实现的。宏展开代码(简化后)如下所示,注意第二个参数为 NULL ,错误码为 ERR_MEM

if(pcb->accept != NULL)pcb->accept(pcb->callback_arg, NULL, ERR_MEM);

这个功能最早是由 Simon Goldschmidt 在 2016-03-23 提交的,提交记录为:

	tcp: call accept-callback with ERR_MEM when allocating a pcb fails onpassive open to inform the application about this errorATTENTION: applications have to handle NULL pcb in accept callback!

tcp:在被动打开分配 pcb 失败时,使用 ERR_MEM 参数调用 accept 回调函数,以通知应用程序有关此错误
注意:应用程序必须在 accept 回调中处理 pcb 句柄为 NULL 的情况!

这就告诉我们一个重要的信息:lwIP 2.1.x 版本的 accept 回调函数编写方式与 lwIP 1.4.1 版本不同。lwIP 2.1.x 版本的 accept 回调函数 必须 在 accept 回调中处理 pcb 句柄为 NULL 的情况!!举个例子。
lwIP 1.4.1 版本的 accept 回调函数可以这么写:

/* 客户端连接时, 回调此函数 */
static err_t telnet_accept(void *arg, struct tcp_pcb *pcb, err_t err)
{char * p_link_info = "已连接到Telnet!\r\n";tcp_recv(pcb,telnet_recv);tcp_err(pcb,NULL);pcb->so_options |= SOF_KEEPALIVE;  //增加保活机制tcp_write(pcb, p_link_info, strlen(p_link_info), TCP_WRITE_FLAG_COPY);return ERR_OK;
}

而 lwIP 2.1.x 版本的accept 回调函数需要这么写:

/* 客户端连接时, 回调此函数 */
static err_t telnet_accept(void *arg, struct tcp_pcb *pcb, err_t err)
{char * p_link_info = "已连接到Telnet!\r\n";if(pcb == NULL){if(err == ERR_MEM)// 处理 TCP 连接个数不足,可选return ERR_OK;}tcp_recv(pcb,telnet_recv);tcp_err(pcb,NULL);pcb->so_options |= SOF_KEEPALIVE;  //增加保活机制tcp_write(pcb, p_link_info, strlen(p_link_info), TCP_WRITE_FLAG_COPY);return ERR_OK;
}

这里对 pcb 句柄是否为 NULL 做了处理,如果检测到 NULL,accpet 回调函数需要提前退出!。

2 由 tcp_process 函数调用

处于 SYN_RCVD 状态的 TCP 控制块,如果接收的正确的 ACK 标志,则调用错误码为 ERR_OKaccept 回调函数,向用户报告接受了新的连接。简化后的代码为:

static err_t
tcp_process(struct tcp_pcb *pcb)
{switch (pcb->state) {case SYN_RCVD:if (flags & TCP_ACK) {/* expected ACK number? */if (TCP_SEQ_BETWEEN(ackno, pcb->lastack + 1, pcb->snd_nxt)) {pcb->state = ESTABLISHED;/* Call the accept function. */TCP_EVENT_ACCEPT(pcb->listener, pcb, pcb->callback_arg, ERR_OK, err);if (err != ERR_OK) {/* If the accept function returns with an error, we abort the connection. */if (err != ERR_ABRT) {tcp_abort(pcb);}return ERR_ABRT;}tcp_receive(pcb);} }break;}return ERR_OK;
}






读后有收获,资助博主养娃 - 千金难买知识,但可以买好多奶粉 (〃‘▽’〃)
千金难买知识,但可以买好多奶粉

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

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

相关文章

阿木实验室普罗米修斯项目环境配置

引言 普罗米修斯项目其实只是个大ROS功能包, 里面每个模块就是每个ROS功能包,比如控制模块,视觉模块等等。对PX4配置的与这个一样,另外他是使用自己的P系列无人机(我个人是P450),所…

Objection

本文作者:杉木涂鸦智能安全实验室 Home objection - 基于frida的命令行hook工具食用手册 实用FRIDA进阶:内存漫游、hook anywhere、抓包-安全客 - 安全资讯平台 Objection是一个基于Frida的命令行hook工具,用于移动设备的运行时探索。它可…

Idea的Marketplace下载不了插件,idea下不了插件

Idea的Marketplace下载不了插件 解决方案(配置代理) 附一张成功界面 2.问题复现 3.问题原因和解决方式:下载安装IDEA之后HTTP Proxy没有进行相关配置的问题,解决方式如下 1.首先打开file->setting->Appearance & B…

Maxscript入门教程:Print与Format命令

本文对Maxscript中的“Print”和“Format”命令之间的差异进行了一些小小的研究,得出的结论主要的差异是它们的结果中的引号“”。 “Print”很简单,直接使用,在调试时非常有用。为了工作,它只需要一个字符串(这是两个…

[学习笔记]在CentOS7中用Docker方式安装Jenkins

文章目录 原理:创建Docker网桥网络安装DinD创建镜像构建镜像运行容器 原理: Docker in Docker (以下简称 DinD)可以在 Container 中直接运行一个 Docker Daemon ,然后使用 Container 中的 Docker CLI 工具操作容器。其…

最详细的Selenium+Pytest自动化测试框架实战

前言 selenium自动化 pytest测试框架 本章你需要 一定的python基础——至少明白类与对象,封装继承 一定的selenium基础——本篇不讲selenium, 测试框架简介 测试框架有什么优点呢: 代码复用率高,如果不使用框架的话&#xff…

【模拟】LeetCode-48. 旋转图像

旋转图像。 给定一个 n n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。 你必须在 原地 旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。 示例 1: 输入:matrix [[1,2,3],[4,5,6]…

武汉凯迪正大—门尼粘度试验机

武汉凯迪正大KY-6004型门尼粘度仪用于胶料粘度和硫化指数的测定。试样在一定的温度和压力下,转子以一定的旋转力矩对试样加以一定的剪切力,仪器测出橡胶的反剪切力矩,是再生胶、橡胶、电线电缆行业常用的仪器之一。 武汉凯迪正大KY-6004型门…

科技与艺术相结合,虚拟人裸眼3D动画亮相城市商圈

随着元宇宙概念的火爆,虚拟制作技术的快速发展,虚拟人可以将虚拟世界与现实世界相结合,为用户带来沉浸式体验。如虚拟人壬子希以裸眼3D动画的形式亮相城市商圈,助力文旅以科技与艺术相结合的形式,展现城市文化与科技成…

【SpringBoot】入门精简

目录 一、初识 SpringBoot 1.1 介绍 1.2 项目创建 1.3 目录结构 1.4 修改配置 二、SpringBoot 集成 2.1 集成 Mybatis框架 2.2 集成 Pagehepler分页插件 2.3 集成 Druid数据库连接池 2.4 集成 Log日志管理 一、初识 SpringBoot 1.1 介绍 Spring Boot是一个用于简化Sp…

软件开发流程分析

软件开发流程分析 相关概念1 原型设计2 产品设计3 交互设计4 代码实现详细步骤 相关概念 前端:自研API,调用第三放API 后端:自研API,第三方API 数据库:Mysql,数据采集,数据迁移 服务器&#xf…

nuitka Unknown property box-shadow,transition,transform

nuitka 打包后,控制台的错误解决方法 nuitka --standalone --show-memory --show-progress --nofollow-imports --follow-import-toneed --output-dirout --windows-icon-from-ico./static/test.ico mainUI2.py 由于Qt样式表不是CSS,QSS基于CSS2.1&…