Nginx模块开发之http过滤器filter
文章目录
- 什么是过滤模块
- Nginx相关数据结构介绍
- ngx_module_t的数据结构
- ngx_http_module_t数据结构
- ngx_command_s数据结构
- 相关宏定义
- filter(过滤器)实现
- Nginx模块开发流程
- Nginx 模块执行
- 具体实现流程
- create_loc_conf
- merge_loc_conf
- postconfiguration
- 修改header信息
- 修改body信息
- 示例代码
- 编写config文件
- 编译模块到Nginx源码中
- 执行效果
- 总结
什么是过滤模块

Nignx是一个代理服务器, 他前端被客户端请求,后端连接服务器。这里涉及的数据处理大概有
- 客户端请求数据,Nginx直接返回(handler 模块)
- 客户端请求数据,Nginx转发给服务器(upstream 模块)
- 服务器返回数据,Nginx转发给客户端(filter 模块)
Nginx相关数据结构介绍
ngx_module_t的数据结构
struct ngx_module_s {ngx_uint_t ctx_index; //是哪个进程ngx_uint_t index; //进程idchar *name;ngx_uint_t spare0;ngx_uint_t spare1;ngx_uint_t version; //版本号const char *signature; //签名证书void *ctx;//上下文ngx_command_t *commands;//命令ngx_uint_t type;// nginx模块类型ngx_int_t (*init_master)(ngx_log_t *log);//ngx_int_t (*init_module)(ngx_cycle_t *cycle); //模块启动时候ngx_int_t (*init_process)(ngx_cycle_t *cycle); //进程启动时候ngx_int_t (*init_thread)(ngx_cycle_t *cycle);void (*exit_thread)(ngx_cycle_t *cycle);void (*exit_process)(ngx_cycle_t *cycle);void (*exit_master)(ngx_cycle_t *cycle);uintptr_t spare_hook0;uintptr_t spare_hook1;uintptr_t spare_hook2;uintptr_t spare_hook3;uintptr_t spare_hook4;uintptr_t spare_hook5;uintptr_t spare_hook6;uintptr_t spare_hook7;
};
typedef struct ngx_module_s ngx_module_t;
ngx_http_module_t数据结构
typedef struct {void **main_conf;void **srv_conf;void **loc_conf;
} ngx_http_conf_ctx_t;typedef struct {ngx_int_t (*preconfiguration)(ngx_conf_t *cf); // 解析配置文件之前ngx_int_t (*postconfiguration)(ngx_conf_t *cf); // 解析配置文件完成之后
// **_main_ **解析配置文件中http关键字的内部void *(*create_main_conf)(ngx_conf_t *cf); char *(*init_main_conf)(ngx_conf_t *cf, void *conf);
// **_srv_ **解析配置文件中server关键字的内部void *(*create_srv_conf)(ngx_conf_t *cf); char *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, void *conf);
// **_loc_ **解析配置文件中location关键字的内部void *(*create_loc_conf)(ngx_conf_t *cf);char *(*merge_loc_conf)(ngx_conf_t *cf, void *prev, void *conf);
} ngx_http_module_t;
ngx_command_s数据结构
struct ngx_command_s {ngx_str_t name;ngx_uint_t type;char *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);ngx_uint_t conf;ngx_uint_t offset;void *post;
};
相关宏定义
#define NGX_MODULE_V1 \NGX_MODULE_UNSET_INDEX, NGX_MODULE_UNSET_INDEX, \NULL, 0, 0, nginx_version, NGX_MODULE_SIGNATURE#define NGX_MODULE_V1_PADDING 0, 0, 0, 0, 0, 0, 0, 0#define NGX_HTTP_MODULE 0x50545448 /* "HTTP" 模块*//* 以下宏定义为了去确定该项配置属于哪个类目下
比如service
比如location
*/
#define NGX_HTTP_MAIN_CONF 0x02000000
#define NGX_HTTP_SRV_CONF 0x04000000
#define NGX_HTTP_LOC_CONF 0x08000000
#define NGX_HTTP_UPS_CONF 0x10000000
#define NGX_HTTP_SIF_CONF 0x20000000
#define NGX_HTTP_LIF_CONF 0x40000000
#define NGX_HTTP_LMT_CONF 0x80000000
filter(过滤器)实现
Nginx模块开发流程
(1)定义一个模块名,ngx_module_t,选择好http模块NGX_HTTP_MODULE。
(2)定义cmd命令,有多少条cmd写多少条cmd,ngx_command_t。
(3)定义用来解析http block,ngx_http_module_t。
(4)执行过程实现添加模块。
Nginx 模块执行
(1)初始化。当进程启动的时候进行的模块初始化。
(2)解析conf文件。解析conf文件中模块的相关命令和设置。
(3)Nginx启动之后,有命令或请求到来时,处理请求的流程。
开发模块时,需要实现的主要是这三个流程的功能。
具体实现流程
create_loc_conf
内存池中分配一片kong空间,用以存储配置文件中指令对应的值
// void *(*create_loc_conf)(ngx_conf_t *cf);
// 解析conf文件location关键字之前的动作
void *ngx_http_fly_filter_create_loc_conf(ngx_conf_t *cf)
{ngx_http_filter_conf_t *conf = ngx_palloc(cf->pool, sizeof(ngx_http_filter_conf_t));if (conf == NULL)return NULL;conf->enable = NGX_CONF_UNSET;return conf;
}
merge_loc_conf
char *ngx_http_fly_filter_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
{ngx_http_filter_conf_t *prev = (ngx_http_filter_conf_t*)parent;//如果prefix中是on,那么next->enable的值就为1,这个是在ngx_conf_set_flag_slot//函数中设置的,即可以理解为将配置//文件中的on或者off转换为nginx内存中的1或者0ngx_http_filter_conf_t *next = (ngx_http_filter_conf_t*)child;ngx_conf_merge_value(next->enable, prev->enable, 0);return NGX_CONF_OK;
}
其中 ngx_conf_merge_value
#define ngx_conf_merge_value(conf, prev, default) \if (conf == NGX_CONF_UNSET) { \conf = (prev == NGX_CONF_UNSET) ? default : prev; \
postconfiguration
- 解析完毕conf文件后执行该指令,设置运行时候的回调函数
- 使用头插法,将header_filter 与 body_filter插入filter队列的头部
// ngx_int_t (*postconfiguration)(ngx_conf_t *cf);
// 解析完配置文件之后的动作,也就是解析完http关键字模块之后
ngx_int_t ngx_http_fly_filter_init(ngx_conf_t *cf)
{// 模块的初始化// http { }// O->O->O->O// 多个模块的头插法,取出最前面的模块//top指向第一个,next指向第二个ngx_http_next_header_filter = ngx_http_top_header_filter;ngx_http_top_header_filter = ngx_http_fly_header_filter;ngx_http_next_body_filter = ngx_http_top_body_filter;ngx_http_top_body_filter = ngx_http_fly_body_filter;return NGX_OK;
}
修改header信息
这里仅仅修改要回发的内容长度,由于修改了body内容,那么header中的length字段自然要做出相应的修改
static ngx_str_t prefix = ngx_string("<h2>FLY. </h2>");
ngx_int_t ngx_http_fly_header_filter(ngx_http_request_t *r) {if (r->headers_out.status != NGX_HTTP_OK) {// 不正常返回,则进行nextreturn ngx_http_next_header_filter(r);}//r->headers_out.content_type.len == sizeof("text/html")r->headers_out.content_length_n += prefix.len;return ngx_http_next_header_filter(r);
}
修改body信息
ngx_int_t ngx_http_fly_body_filter(ngx_http_request_t *r, ngx_chain_t *chain) {/** 关于ngx_chain_t:* 在nginx中,有一个数据链,存放要发送的数据。* O->O->O->O* 每次send的是ngx_chain_t中的一个ngx_buf_t*/// 添加一个chain bufferngx_buf_t *b = ngx_create_temp_buf(r->pool, prefix.len);b->start = b->pos = prefix.data;b->last = b->pos + prefix.len;ngx_chain_t *c1 = ngx_alloc_chain_link(r->pool);c1->buf = b;c1->next = chain;return ngx_http_next_body_filter(r, c1);}
示例代码
这里主要实现了在返回的网页中添加一个内容。里面在重点地方附上了详细注释。
ngx_http_filter_module.c
#include <ngx_config.h>
#include <ngx_http.h>
#include <ngx_core.h>typedef struct {ngx_flag_t enable;
}ngx_http_filter_conf_t;static ngx_str_t prefix = ngx_string("<h2>FLY. </h2>");static ngx_http_output_header_filter_pt ngx_http_next_header_filter;
static ngx_http_output_body_filter_pt ngx_http_next_body_filter;// ngx_int_t (*ngx_http_output_header_filter_pt)(ngx_http_request_t *r)
// 添加头,header
ngx_int_t ngx_http_fly_header_filter(ngx_http_request_t *r) {if (r->headers_out.status != NGX_HTTP_OK) {// 不正常返回,则进行nextreturn ngx_http_next_header_filter(r);}//r->headers_out.content_type.len == sizeof("text/html")r->headers_out.content_length_n += prefix.len;return ngx_http_next_header_filter(r);
}// ngx_int_t (*ngx_http_output_body_filter_pt)(ngx_http_request_t *r, ngx_chain_t *chain)
// 添加内容,body
ngx_int_t ngx_http_fly_body_filter(ngx_http_request_t *r, ngx_chain_t *chain) {/** 关于ngx_chain_t:* 在nginx中,有一个数据链,存放要发送的数据。* O->O->O->O* 每次send的是ngx_chain_t中的一个ngx_buf_t*/// 添加一个chain bufferngx_buf_t *b = ngx_create_temp_buf(r->pool, prefix.len);b->start = b->pos = prefix.data;b->last = b->pos + prefix.len;ngx_chain_t *c1 = ngx_alloc_chain_link(r->pool);c1->buf = b;c1->next = chain;return ngx_http_next_body_filter(r, c1);}// ngx_int_t (*postconfiguration)(ngx_conf_t *cf);
// 解析完配置文件之后的动作,也就是解析完http关键字模块之后
ngx_int_t ngx_http_fly_filter_init(ngx_conf_t *cf)
{// 模块的初始化// http { }// O->O->O->O// 多个模块的头插法,取出最前面的模块ngx_http_next_header_filter = ngx_http_top_header_filter;ngx_http_top_header_filter = ngx_http_fly_header_filter;ngx_http_next_body_filter = ngx_http_top_body_filter;ngx_http_top_body_filter = ngx_http_fly_body_filter;return NGX_OK;
}// void *(*create_loc_conf)(ngx_conf_t *cf);
// 解析conf文件location关键字之前的动作
void *ngx_http_fly_filter_create_loc_conf(ngx_conf_t *cf)
{ngx_http_filter_conf_t *conf = ngx_palloc(cf->pool, sizeof(ngx_http_filter_conf_t));if (conf == NULL)return NULL;conf->enable = NGX_CONF_UNSET;return conf;
}// char *(*merge_loc_conf)(ngx_conf_t *cf, void *prev, void *conf);
// 解析完配置文件location关键字之后的动作
// 模块可能在多个地方定义,这个函数合并所有的值一起使用
char *ngx_http_fly_filter_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
{ngx_http_filter_conf_t *prev = (ngx_http_filter_conf_t*)parent;ngx_http_filter_conf_t *next = (ngx_http_filter_conf_t*)child;// 合并enable的值ngx_conf_merge_value(next->enable, prev->enable, 0);return NGX_CONF_OK;
}/*
struct ngx_command_s {
ngx_str_t name;
ngx_uint_t type;
char *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
ngx_uint_t conf;
ngx_uint_t offset;
void *post;
};
*//*
// conf文件命令解析
char *ngx_http_fly_filter_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{char *p = conf;// 对应 ngx_http_fly_filter_create_loc_conf函数的conf->enable = NGX_CONF_UNSET;ngx_flag_t *flag = (p + cmd->offset);return NGX_CONF_OK;
}
*/// conf文件中的每一行都是一个指令指令
ngx_command_t ngx_http_fly_filter_module_cmd[] = {{//命令名称,比如listen,定义了就可以在conf文件中使用,注意不能和其他的起冲突ngx_string("predix"),// 指示name命令放的位置在哪里以及可以带多少个参数,NGX_CONF_FLAGE表示开关标志// predix on/offNGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_FLAG,// 命令解析,可以使用nginx内部的也可以自己实现ngx_conf_set_flag_slot,//ngx_http_fly_filter_set_slot,NGX_HTTP_LOC_CONF_OFFSET,// offsetof获取enable在结构体中的偏移位置offsetof(ngx_http_filter_conf_t,enable),NULL,},ngx_null_command
};// 用来解析对应的conf文件,其实表示的就是模块定义中的上下文
static ngx_http_module_t ngx_http_fly_filter_module_ctx = {NULL,ngx_http_fly_filter_init,NULL,NULL,NULL,NULL,ngx_http_fly_filter_create_loc_conf,ngx_http_fly_filter_merge_loc_conf
};// 模块定义
ngx_module_t ngx_http_fly_filter_module = {NGX_MODULE_V1,&ngx_http_fly_filter_module_ctx,ngx_http_fly_filter_module_cmd,// http的ascii值,指示是什么模块NGX_HTTP_MODULE,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NGX_MODULE_V1_PADDING // 填充};
编写config文件
创建:
touch config
内容:
ngx_addon_name=ngx_http_fly_filter_module
HTTP_FILTER_MODULES="$HTTP_FILTER_MODULES ngx_http_fly_filter_module"
NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_filter_module.c"
包含三部分信息,一个是模块的名称这里名称需要与代码中的定义的模块名称ngx_module_t一致;第二部分是指定模块的类型和名称,这里定义的是一个filter模块;最后是指定模块源文件路径。
注意,config文件要和模块的代码在相同目录。
编译模块到Nginx源码中
(1)配置中添加模块:
./configure --prefix=/usr/local/nginx --with-http_realip_module
--with-http_addition_module --with-http_gzip_static_module
--with-http_secure_link_module --with-http_stub_status_module
--with-stream --with-pcre=/home/fly/workspace/pcre-8.41
--with-zlib=/home/fly/workspace/zlib-1.2.11
--with-openssl=/home/fly/workspace/openssl-1.1.0g
--add-module=/mnt/hgfs/sourcecode_learning/ngx_http_filter_module
注意模块路径要正确。出现如下表示成功:
configuring additional modules
adding module in /mnt/hgfs/sourcecode_learning/ngx_http_filter_module+ ngx_http_fly_filter_module was configured
creating objs/Makefile
(2)查看是否添加模块到动态代码中:
vim objs/ngx_modules.c
(3)编译:
make
sudo make install
执行效果
编译安装完成后,在conf文件中添加模块的开关(predix on):
worker_processes 4;events {worker_connections 1024;
}http {upstream backend {server 192.168.7.146:8889;server 192.168.7.146:8890;}server {listen 8888;location / {proxy_pass http://backend;}}server {listen 8889;}server {listen 8890;predix on;}server {listen 8891;}
}
执行Nginx:
sudo /usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/fly.conf
在网页输入IP和端口,执行效果如下:

可以看到,返回的网页中多出来添加的内容(FLY.)。
总结
- Nginx中http模块非常多,每个模块都会有ngx_http_module_t,为了防止解析过程出现冲突,Nginx编译的时候会把所有的模块都集中起来,组织到/obj/ngx_module.c(以数组的方式)。
- 在编译模块时,需要编写config文件,这个文件最好不要使用笔记本编辑(notepad),容易造成编码方式的错误。
- 网页中常见的广告(其实里面存储了图片、链接、名称信息)等等其实就是通过nginx过滤器模块去实现的。
相关文章:
Nginx模块开发之http过滤器filter
文章目录 什么是过滤模块Nginx相关数据结构介绍ngx_module_t的数据结构ngx_http_module_t数据结构ngx_command_s数据结构 相关宏定义filter(过滤器)实现Nginx模块开发流程Nginx 模块执行具体实现流程create_loc_confmerge_loc_confpostconfiguration修改…...
26 - 原型模式与享元模式:提升系统性能的利器
原型模式和享元模式,前者是在创建多个实例时,对创建过程的性能进行调优;后者是用减少创建实例的方式,来调优系统性能。这么看,你会不会觉得两个模式有点相互矛盾呢? 其实不然,它们的使用是分场…...
【Web安全】sqlmap的使用笔记及示例
【Web安全】sqlmap的使用笔记 文章目录 【Web安全】sqlmap的使用笔记1. 目标2. 脱库2.1. 脱库(补充) 3. 其他3.1. 其他(补充) 4. 绕过脚本tamper讲解 1. 目标 操作作用必要示例-u指定URL,检测注入点sqlmap -u http://…...
机器学习第12天:聚类
文章目录 机器学习专栏 无监督学习介绍 聚类 K-Means 使用方法 实例演示 代码解析 绘制决策边界 本章总结 机器学习专栏 机器学习_Nowl的博客-CSDN博客 无监督学习介绍 某位著名计算机科学家有句话:“如果智能是蛋糕,无监督学习将是蛋糕本体&a…...
若依框架导出下载pdf/excel以及导入打印等
一、打印文件 // 报表打印 handlePdf(row) {wayAPI(row.billcode).then((res) > {var binaryData [];binaryData.push(res);let url window.URL.createObjectURL(new Blob(binaryData, {type: "application/pdf"})); window.open("/static/pdf/web/v…...
汇编-PROC定义子过程(函数)
过程定义 过程用PROC和ENDP伪指令来声明, 并且必须为其分配一个名字(有效的标识符) 。目前为止, 我们所有编写的程序都包含了一个main过程, 例如: 当要创建的过程不是程序的启动过程时, 就用RET指令来结束它。RET强制…...
服务器主机安全的重要性及防护策略
在数字化时代,服务器主机安全是任何组织都必须高度重视的问题。无论是大型企业还是小型企业,无论是政府机构还是个人用户,都需要确保其服务器主机的安全,以防止数据泄露、网络攻击和系统瘫痪等严重后果。 一、服务器主机安全的重…...
PDF转成图片
使用开源库Apache PDFBox将PDF转换为图片 依赖 <dependency><groupId>org.apache.pdfbox</groupId><artifactId>fontbox</artifactId><version>2.0.4</version> </dependency> <dependency><groupId>org.apache…...
Qt无边框设计
//指定窗口为无边框 this->setWindowFlags(Qt::FramelessWindowHint | Qt::WindowMinMaxButtonsHint);重写鼠标事件: void mousePressEvent(QMouseEvent* event) override; void mouseMoveEvent(QMouseEvent* event) override;定义位置: QPoint dif…...
规则引擎Drools使用,0基础入门规则引擎Drools(二)高级语法
文章目录 系列文章索引五、规则属性1、enabled属性2、dialect属性3、salience属性4、no-loop属性5、activation-group属性6、agenda-group属性7、auto-focus属性8、timer属性9、date-effective属性10、date-expires属性 六、Drools高级语法1、global全局变量2、query查询3、fun…...
C语言二十三弹---求第N项斐波那契数列的值
C语言求第N项斐波那契数列的值 定义:斐波那契数列指的是这样一个数列:1,1,2,3,5,8,13,21,34,55,89…自然中的斐波那契数列࿰…...
Pickcode:教孩子们编码的新视觉语言
Pickcode 通过视觉课程、聊天机器人、游戏和绘图来教授编程。 Pickcode 是一种新的语言和编辑器,可以直观地指导用户编写代码来制作聊天机器人、动画图画和游戏。Pickcode 旨在让用户在学习更高级的语言之前能够充满信心地开始学习编码。 Pickcode 可视化编程语言…...
乐划锁屏插画大赏热度持续,进一步促进价值内容的创造与传播
锁屏,原本只是为了防止手机在口袋里“误触”而打造的功能,现如今逐渐成为文化传播领域的热门入口。乐划锁屏不断丰富锁屏内容和场景玩法,通过打造“乐划锁屏插画大赏”系列活动为广大内容创作者提供了更多展示自我的机会,丰富平台内容。 从2020年到2023年,乐划锁屏插画大赏已连…...
【ArcGIS Pro微课1000例】0034:矢量数据几何校正案例(Spatial Adjustment)
本案例讲解矢量数据几何校正,根据一个矢量数据去校正另外一个矢量数据。 文章目录 一、加载实验数据二、空间校正三、注意事项一、加载实验数据 在ArcGIS Pro中加载数据效果如下: design:需要校正的数据图层plan+roadcenter:目标图层可以看到,design图层没有在正确的位置…...
2023亚太杯数学建模B题:玻璃温室中的微气候法规,思路模型代码论文
问题B 玻璃温室中的微气候法规 赛题思路:思路获取见文末名片,第一时间更新 温室作物的产量受到各种气候因素的影响,包括温度、湿度和风速[1]。其中,适 宜的温度和风速是植物生长[2]的关键。为了调节玻璃温室内的温度、风速等气…...
Eclipse常用设置-乱码
在用Eclipse进行Java代码开发时,经常会遇到一些问题,记录下来,方便查看。 一、properties文件乱码 常用的配置文件properties里中文的乱码,不利于识别。 处理流程:Window -> Preferences -> General -> Ja…...
MySQL面试,MySQL事务,MySQL锁,MySQL集群,主从,MySQL分区,分表,InnoDB
文章目录 数据库-MySQLMySQL主从、集群模式简单介绍1、主从模式 Replication2、集群模式3、主从模式部署注意事项 UNION 和 UNION ALL 区别分库分表1.垂直拆分2、水平拆分 MySQL有哪些数据类型1、整数类型**,2、实数类型**,3、字符串类型**,4…...
HarmonyOS应用开发者认证题目满分指南
为了帮助大家快速的上手HarmonyOS应用程序开发,官方制作了一些免费的课程:HarmonyOS第一课。每个课程后面都有一些练习题,下面就是这些题目的满分答案。 【习题】运行Hello World工程 判断题 1.DevEco Studio是开发HarmonyOS应用的一站式集…...
openssl+ SM2 + linux 签名校验开发实例(C++)
文章目录 一、SM2校验理论基础二、SM2签名校验开发实例(C) 一、SM2校验理论基础 SM2的校验过程是使用椭圆曲线上的公钥验证签名的有效性。以下是SM2校验的理论基础相关知识点: SM2签名算法: SM2的校验基于椭圆曲线数字签名算法&a…...
有关Vue、微信小程序、UniApp中的CSS中的宽度width单位、自适应
在Vue中,可以使用以下单位来设置宽度(width) 像素(px):最常用的单位,表示一个绝对长度单位。例如,width: 200px; 表示宽度为200像素。百分比(%):…...
Python爬虫实战:研究MechanicalSoup库相关技术
一、MechanicalSoup 库概述 1.1 库简介 MechanicalSoup 是一个 Python 库,专为自动化交互网站而设计。它结合了 requests 的 HTTP 请求能力和 BeautifulSoup 的 HTML 解析能力,提供了直观的 API,让我们可以像人类用户一样浏览网页、填写表单和提交请求。 1.2 主要功能特点…...
synchronized 学习
学习源: https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖,也要考虑性能问题(场景) 2.常见面试问题: sync出…...
【JavaEE】-- HTTP
1. HTTP是什么? HTTP(全称为"超文本传输协议")是一种应用非常广泛的应用层协议,HTTP是基于TCP协议的一种应用层协议。 应用层协议:是计算机网络协议栈中最高层的协议,它定义了运行在不同主机上…...
HTML 列表、表格、表单
1 列表标签 作用:布局内容排列整齐的区域 列表分类:无序列表、有序列表、定义列表。 例如: 1.1 无序列表 标签:ul 嵌套 li,ul是无序列表,li是列表条目。 注意事项: ul 标签里面只能包裹 li…...
vue3 定时器-定义全局方法 vue+ts
1.创建ts文件 路径:src/utils/timer.ts 完整代码: import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...
【配置 YOLOX 用于按目录分类的图片数据集】
现在的图标点选越来越多,如何一步解决,采用 YOLOX 目标检测模式则可以轻松解决 要在 YOLOX 中使用按目录分类的图片数据集(每个目录代表一个类别,目录下是该类别的所有图片),你需要进行以下配置步骤&#x…...
WEB3全栈开发——面试专业技能点P2智能合约开发(Solidity)
一、Solidity合约开发 下面是 Solidity 合约开发 的概念、代码示例及讲解,适合用作学习或写简历项目背景说明。 🧠 一、概念简介:Solidity 合约开发 Solidity 是一种专门为 以太坊(Ethereum)平台编写智能合约的高级编…...
学习STC51单片机32(芯片为STC89C52RCRC)OLED显示屏2
每日一言 今天的每一份坚持,都是在为未来积攒底气。 案例:OLED显示一个A 这边观察到一个点,怎么雪花了就是都是乱七八糟的占满了屏幕。。 解释 : 如果代码里信号切换太快(比如 SDA 刚变,SCL 立刻变&#…...
有限自动机到正规文法转换器v1.0
1 项目简介 这是一个功能强大的有限自动机(Finite Automaton, FA)到正规文法(Regular Grammar)转换器,它配备了一个直观且完整的图形用户界面,使用户能够轻松地进行操作和观察。该程序基于编译原理中的经典…...
基于Java+MySQL实现(GUI)客户管理系统
客户资料管理系统的设计与实现 第一章 需求分析 1.1 需求总体介绍 本项目为了方便维护客户信息为了方便维护客户信息,对客户进行统一管理,可以把所有客户信息录入系统,进行维护和统计功能。可通过文件的方式保存相关录入数据,对…...
