【网络编程】web服务器shttpd源码剖析——命令行和文件配置解析

 

hello !大家好呀! 欢迎大家来到我的网络编程系列之web服务器shttpd源码剖析——命令行解析,在这篇文章中,你将会学习到在Linux内核中如何创建一个自己的并发服务器shttpd,并且我会给出源码进行剖析,以及手绘UML图来帮助大家来理解,希望能让大家更能了解网络编程技术!!!

希望这篇文章能对你有所帮助,大家要是觉得我写的不错的话,那就点点免费的小爱心吧!

               

目录

   ​编辑 一.命令行和文件配置

    ​编辑1.1 命令行是什么?

​编辑1.2 文件配置作用

​编辑二.需求分析

​编辑2.1 命令行参数需求分析

​编辑2.2 文件配置需求分析

​编辑三.具体实现

 ​编辑3.1 命令行参数设置与解析

​编辑3.2 文件配置参数设置与解析

​编辑四:源码分析

​编辑4.1 命令行配置以及解析设计

​编辑4.2 文件配置以及解析设计


    一.命令行和文件配置

    1.1 命令行是什么?

在 shttpd 中,命令行界面(CLI)允许用户启动、停止和控制服务器,以及配置服务器的各种参数。

shttpd 命令行的作用主要包括:

  1. 启动和停止服务器:通过命令行,可以启动或停止 shttpd 服务。

  2. 配置服务器:通过命令行参数,可以设置服务器的各种运行参数,比如端口号、文档根目录、日志文件等。

  3. 查看服务器状态:一些命令行参数可以帮助用户查看服务器的当前状态,例如查看服务器版本、查看已加载的模块等。

  4. 执行服务器维护任务:命令行还可以用来执行一些维护任务,如重载配置文件、重启服务等。

  5. 脚本化控制:命令行工具可以与脚本结合使用,允许自动执行重复性任务或集成到更复杂的工作流程中。

  6. 调试和故障排除:在服务器出现问题时,命令行工具可以用来调试和诊断问题。

1.2 文件配置作用

在 shttpd 和其他许多 web 服务器中,文件配置是一种用来设置服务器参数和行为的机制。这些配置通常存储在一个或多个文本文件中,服务器在启动或运行时读取这些文件来决定如何处理客户端请求。文件配置的作用包括:

  1. 持久化设置:与命令行参数相比,文件配置可以将设置持久化保存,这意味着服务器重启后配置仍然有效。

  2. 方便管理:对于复杂的配置,通过文件进行设置比在命令行中输入一系列参数更为方便和直观。

  3. 模块化配置:可以将不同的配置项分门别类地放置在不同的文件中,便于管理和更新。

  4. 灵活性和可扩展性:通过修改配置文件,可以轻松地添加或修改服务器的功能,而无需重新编译服务器软件。

  5. 共享和分发:配置文件可以轻松地复制到其他服务器或共享给其他用户,从而快速设置多个服务器实例。

  6. 环境适应性:可以根据不同的运行环境(如开发、测试、生产环境)使用不同的配置文件,使服务器在不同的环境下运行。

  7. 安全性:可以通过配置文件设置用户权限、安全证书等重要安全参数,保护服务器免受未授权访问。

  8. 错误处理:配置文件可以用来定义错误处理逻辑,如自定义错误页面、日志记录等。

shttpd 的配置文件通常是简单的文本文件,其中包含了服务器的各种设置,如监听的端口、文档根目录、访问控制等。通过编辑这些配置文件,管理员可以自定义服务器的行为,以满足特定的需求。

二.需求分析

2.1 命令行参数需求分析

服务器SHTTPD 可以动态配置启动参数,例如服务器的侦听端口、支持客户端并发访问的数量、超时时间的设置、访问 Web网页的路径等。采用参数配置和文件配置两种支持方式,在优先级上, 参数配置比文件配置的优先级高, 参数配置的选项值会覆盖文件配置的选项。

命令行参数配置:(我们需要启动参数是可以动态配置的)

2.2 文件配置需求分析

配置文件的名称为 SHTTPD. conf, 默认路径为“/etc”下。

配置文件的格式如下:

[#注释][空格]关键字[空格]=[空格] value]

配置文件中的一行为#开头的注释或者选项配置, 不支持空行, 关键字右边的值不含有空格。各部分如下定义。

#注释: 一行以#开始表示此行为注释, 程序不对此行进行分析。

空格: 可以为0个或者多个空格。
 

关键字: 可以为如下的字符串, 大小写必须完全匹配。

ListenPort: 侦听端口。

MaxClient: 最大客户端并行访问数。

DocumentRoot: Web 网页根目录。

CGIRoot: CGI程序根目录。

DefaultFile: 默认访问网页名称。

TimeOut: 客户端连接空闲超时时间。

值:用户对关键字选项的配置, 全部为字符串。值中不能有引号、换行符、空格尾的空格将被解释为值的一部分), ListenPort、TimeOut等不支持十六进制的“0x”方

下面为配置文件实例。

#SHTTPD Web服务器配置文件示例
#侦听端口ListenPort=80
#最大并发访问客户端数目MaxClient=8
#Web网页根目录DocumentRoot = /home/www/
#CGI根目录CGIRoot  = /home/www/cgi-bin/
#默认访问文件名DefaultFile  = default. htm
#客户端空闲连接超时时间TimeOut  = 5

其实际过程如下图:
 

三.具体实现

 3.1 命令行参数设置与解析

对于命令行参数的解析,我们使用getopt_long函数来实现快捷解析。

getopt_long 函数是 GNU C 库提供的一个函数,用于解析命令行参数。它是对标准 getopt 函数的扩展,支持长选项和短选项。长选项是指那些以两个破折号 -- 开头,后面跟一个单词或多个单词的选项,而短选项则是以一个破折号 - 开头,后面跟一个字符的选项。

下面是 getopt_long 函数的基本用法和参数解释:

#include <getopt.h>int getopt_long(int argc, char * const argv[],const char *optstring,const struct option *longopts, int *longindex);

参数说明:

  • argc 和 argv:这两个参数通常直接从 main 函数的参数传递过来,分别代表命令行参数的数量和参数数组。

  • optstring:这是一个字符串,用于指定程序接受哪些短选项。每个字符代表一个选项,如果选项后面跟一个冒号 :,则表示该选项后面必须跟一个参数。

  • longopts:这是一个 struct option 数组,用于指定程序接受哪些长选项。struct option 的定义如下:

    struct option {const char *name;   // 长选项的名称,不包括 `--`int has_arg;        // 是否有参数,可以是 no_argument, required_argument, optional_argumentint *flag;          // 如果不为 NULL,则 getopt_long 返回 0,并且将 val 的值赋给 flag 指向的变量int val;            // 如果 flag 为 NULL,则 getopt_long 返回 val 的值
    };
    
  • longindex:如果这个参数不为 NULL,getopt_long 会将当前长选项在 longopts 数组中的索引存储在该指针指向的位置。

  • getopt_long 函数的返回值是下一个选项字符,如果所有选项都解析完毕,则返回 -1。如果遇到未知选项,则返回 ?

那么命令行参数中的选项定义是什么样的呢?

 设置如下形式的参数来提供命令行参数选项的解析, 其中短参数类型为:

"c:d:f:ho:l:m:t:";

对应的长参数类型为:

{"CGIRoot",  required_argument, NULL, 'c'},{"ConfigFile",  required_argument, NULL, 'f'},{"DefaultFile",  required_argument, NULL, 'd'},{"DocumentRoot",  required_argument, NULL, 'o'},{"ListenPort",  required_argument, NULL, 'l'},{"MaxClient",  required_argument, NULL, 'm'},{"TimeOut",  required_argument, NULL, 't'},

3.2 文件配置参数设置与解析

 大致过程可以用以下uml图解释:

四:源码分析

4.1 命令行配置以及解析设计

//start from the very beginning,and to create greatness
//@author: Chuangwei Lin
//@E-mail:979951191@qq.com
//@brief: 命令行解析代码和配置文件解析的实现#include "lcw_shttpd.h"//短选项的配置为c:d:f:h:o:l:m:t:
static char* shortopts="c:d:f:h:o:l:m:t:";
//长选项的配置
//option是getop_long的一个结构体参数p531
static struct option longopts[]=
{{"CGIRoot",required_argument,NULL,'c'},{"ConfigFile",required_argument,NULL,'f'},{"DefaultFile",required_argument,NULL,'d'},{"DocumentRoot",required_argument,NULL,'o'},{"ListenPort",required_argument,NULL,'l'},{"MaxClient",required_argument,NULL,'m'},{"TimeOut",required_argument,NULL,'t'},{"Help",no_argument,NULL,'h'},{0,0,0,0},
}
//初始化时服务器的默认配置
extern struct conf_opts conf_para=
{"/usr/local/var/www/cgi-bin/",//CGI根目录"index.html",//默认文件名称"/usr/local/var/www/",//根文件目录"/etc/SHTTPD.conf",//配置文件路径和名称8080, //监听端口4, //最大客户端数量3,//超时时间2//初始化线程数量
};
/******************************************************
函数名:display_usage(void)
参数:无
功能:显示参数输入方法
*******************************************************/
void display_usage(void)
{printf("*******************Chuangwei Lin*******************\n");printf("sHTTPD -l number -m number -o path -c path -d filename -t seconds -o filename\n");printf("sHTTPD --ListenPort number\n");printf(" --MaxClient number\n");printf(" --DocumentRoot) path\n");printf(" --DefaultFile) filename\n");printf(" --CGIRoot path \n");printf(" --DefaultFile filename\n");printf(" --TimeOut seconds\n");printf(" --ConfigFile filename\n");
}
/******************************************************
函数名:conf_readline(int fd, char *buff, int len)
参数:文件描述符,缓冲区,长度
功能:读取配置文件的一行
*******************************************************/
static int conf_readline(int fd, char *buff, int len)
{int n = -1;int i = 0;int begin = 0;memset(buff, 0, len);//清缓冲区for(i =0; i<len;begin?i++:i)//当开头部分不为'\r'或者'\n'时i计数{ //begin真则i++n = read(fd, buff+i, 1);//读一个字符if(n == 0)//文件末尾{*(buff+i) = '\0';break;}else if(*(buff+i) == '\r' ||*(buff+i) == '\n'){//是回车换行if(begin){//为一行*(buff+i) = '\0';    break;}}else{begin = 1;}}return i;
}static char* l_opt_arg;//存输入参数
/******************************************************
函数名:Para_CmdParse(int argc,char* argv[])
参数:argc:参数个数 ,argv:参数的字符串数组,两个参数一般是从main()函数的输入参数中直接传来
功能:命令行解析函数,利用getopt_long函数实现
*******************************************************/
static int Para_CmdParse(int argc,char* argv[])
{int c;int len;int value;//遍历输入参数,设置配置参数while((c=getopt_long(argc,argv,shortopts,longopts,NULL))!=-1){    switch(c)    { //getopt_long()如果有输入参数,则输入参数为optargcase:'c'//CGI跟路径l_opt_arg = optarg;if (l_opt_arg && l_opt_arg[0] != ':'){len = strlen(l_opt_arg);memcpy(conf_para.CGIRoot,l_opt_arg,len+1);//更新}break;case:'d'//默认文件名称l_opt_arg = optarg;if (l_opt_arg && l_opt_arg[0] != ':'){len = strlen(l_opt_arg);memcpy(conf_para.DefaultFile,l_opt_arg,len+1);}break;case:'f'//配置文件名称和路径l_opt_arg = optarg;if (l_opt_arg && l_opt_arg[0] != ':'){len = strlen(l_opt_arg);memcpy(conf_para.ConfigFile,l_opt_arg,len+1);}break;case:'o'//根文件路径l_opt_arg = optarg;if (l_opt_arg && l_opt_arg[0] != ':'){len = strlen(l_opt_arg);memcpy(conf_para.DocumentRoot,l_opt_arg,len+1);}break;case:'l'//侦听端口l_opt_arg = optarg;if (l_opt_arg && l_opt_arg[0] != ':'){len = strlen(l_opt_arg);value = strtol(l_opt_arg,NULL,10);//转化字符串为整形if (value != LONG_MAX && value != LONG_MIN){conf_para.ListenPort = value;//更新}}break;case:'m'//最大客户端数量l_opt_arg = optarg;if (l_opt_arg && l_opt_arg[0] != ':'){len = strlen(l_opt_arg);value = strtol(l_opt_arg,NULL,10);//转化字符串为整形if (value != LONG_MAX && value != LONG_MIN){conf_para.MaxClient = value;//更新}}break;case:'t'//超时时间l_opt_arg = optarg;if (l_opt_arg && l_opt_arg[0] != ':'){len = strlen(l_opt_arg);value = strtol(l_opt_arg,NULL,10);//转化字符串为整形if (value != LONG_MAX && value != LONG_MIN){conf_para.TimeOut = value;//更新}}break;case:'?'printf("Invalid para \n");case:'h'display_usage();break;}}
} 

4.2 文件配置以及解析设计

/******************************************************
函数名:Para_CmdParse(int argc,char* argv[])
参数:argc:参数个数 ,argv:参数的字符串数组,两个参数一般是从main()函数的输入参数中直接传来
功能:命令行解析函数,利用getopt_long函数实现
*******************************************************/
static int Para_CmdParse(int argc,char* argv[])
{int c;int len;int value;//遍历输入参数,设置配置参数while((c=getopt_long(argc,argv,shortopts,longopts,NULL))!=-1){    switch(c)    { //getopt_long()如果有输入参数,则输入参数为optargcase:'c'//CGI跟路径l_opt_arg = optarg;if (l_opt_arg && l_opt_arg[0] != ':'){len = strlen(l_opt_arg);memcpy(conf_para.CGIRoot,l_opt_arg,len+1);//更新}break;case:'d'//默认文件名称l_opt_arg = optarg;if (l_opt_arg && l_opt_arg[0] != ':'){len = strlen(l_opt_arg);memcpy(conf_para.DefaultFile,l_opt_arg,len+1);}break;case:'f'//配置文件名称和路径l_opt_arg = optarg;if (l_opt_arg && l_opt_arg[0] != ':'){len = strlen(l_opt_arg);memcpy(conf_para.ConfigFile,l_opt_arg,len+1);}break;case:'o'//根文件路径l_opt_arg = optarg;if (l_opt_arg && l_opt_arg[0] != ':'){len = strlen(l_opt_arg);memcpy(conf_para.DocumentRoot,l_opt_arg,len+1);}break;case:'l'//侦听端口l_opt_arg = optarg;if (l_opt_arg && l_opt_arg[0] != ':'){len = strlen(l_opt_arg);value = strtol(l_opt_arg,NULL,10);//转化字符串为整形if (value != LONG_MAX && value != LONG_MIN){conf_para.ListenPort = value;//更新}}break;case:'m'//最大客户端数量l_opt_arg = optarg;if (l_opt_arg && l_opt_arg[0] != ':'){len = strlen(l_opt_arg);value = strtol(l_opt_arg,NULL,10);//转化字符串为整形if (value != LONG_MAX && value != LONG_MIN){conf_para.MaxClient = value;//更新}}break;case:'t'//超时时间l_opt_arg = optarg;if (l_opt_arg && l_opt_arg[0] != ':'){len = strlen(l_opt_arg);value = strtol(l_opt_arg,NULL,10);//转化字符串为整形if (value != LONG_MAX && value != LONG_MIN){conf_para.TimeOut = value;//更新}}break;case:'?'printf("Invalid para \n");case:'h'display_usage();break;}}
} 
/******************************************************
函数名:Para_FileParse(char* file)
参数:文件
功能:文件配置解析函数
*******************************************************/
void Para_FileParse(char* file)
{#define LINELENGTH 256char line[LINELENGTH];//读取缓冲区char *name = NULL,*value = NULL;//用于获取关键字和值int fd = -1;//文件描述符int n = 0;fd = open(file,O_RDONLY);//只读方式打开配置文件if (-1 == fd)//错误检查{goto EXITPara_FileParse;//退出}//命令格式如下//[#注释|[空格]关键字[空格]=[空格]value]//while((n = conf_readline(fd,line,LINELENGTH))!= 0)//每次读取一行{char *pos = line;//文件位置指针while(isspace(*pos)){pos++;//跳过一行开头部分的空格}if(*pos == '#')//如果是注释{continue;//那就读取下一行}name = pos;//此时的位置就是关键字的开头while(!isspace(*pos) && *pos != '=')//不是空格也不是’=‘,则继续读直到读完关键字{pos++;}*pos = '\0';//得到关键字while(isspace(*pos))//再次跳过值前面的空格{pos++;}value = pos;while(!isspace(*pos) && *pos != '\r' && *pos != '\n')//读到结束{pos++;}pos = '\0';//得到值//根据关键字,将值赋给配置文件的结构int ivalue;if(strncmp("CGIRoot",name,7)){memcpy(conf_para.CGIRoot,value,strlen(value)+1);}else if(strncmp("DefaultFile",name,11)){memcpy(conf_para.DefaultFile,value,strlen(value)+1);}else if(strncmp("DocumentRoot",name,12)){memcpy(conf_para.DocumentRoot,value,strlen(value)+1);}else if(strncmp("ListenPort",name,10)){ivalue = strtol(value,NULL,10);//转化字符串为整形conf_para.ListenPort = ivalue;}else if(strncmp("MaxClient",name,9)){ivalue = strtol(value,NULL,10);//转化字符串为整形conf_para.MaxClient = ivalue;}else if(strncmp("TimeOut",name,7)){ivalue = strtol(value,NULL,10);//转化字符串为整形conf_para.TimeOut = ivalue;}}close(fd);//关闭文件
EXITPara_FileParse:return ;
}
/******************************************************
函数名:display_para()
参数:无
功能:显示配置的参数
*******************************************************/
static void display_para()
{printf("*******************Chuangwei Lin*******************\n");printf("sHTTPD ListenPort: %d\n",conf_para.ListenPort);printf(" MaxClient: %d\n", conf_para.MaxClient);printf(" DocumentRoot: %s\n",conf_para.DocumentRoot);printf(" DefaultFile:%s\n",conf_para.DefaultFile);printf(" CGIRoot:%s \n",conf_para.CGIRoot);printf(" DefaultFile:%s\n",conf_para.DefaultFile);printf(" TimeOut:%d\n",conf_para.TimeOut);printf(" ConfigFile:%s\n",conf_para.ConfigFile);
}
/******************************************************
函数名:Para_Init(int argc, char *argv[])
参数:参数个数,和参数字符串
功能:初始化配置
*******************************************************/
void Para_Init(int argc, char *argv[])
{//解析命令行输入参数Para_CmdParse(argc, argv);//解析配置文件配置参数 if(strlen(conf_para.ConfigFile)){Para_FileParse(conf_para.ConfigFile);}display_para();return ;//返回配置参数
}

   好啦!到这里这篇文章就结束啦,关于实例代码中我写了很多注释,如果大家还有不懂得,可以评论区或者私信我都可以哦!! 感谢大家的阅读,我还会持续创造网络编程相关内容的,记得点点小爱心和关注哟!  

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

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

相关文章

单链表的应用

文章目录 目录1. 单链表经典算法OJ题目1.1 [移除链表元素](https://leetcode.cn/problems/remove-linked-list-elements/description/)1.2 [链表的中间节点](https://leetcode.cn/problems/middle-of-the-linked-list/description/)1.3 [反转链表](https://leetcode.cn/problem…

CTFHUB-技能树-Web前置技能-文件上传(无验证,JS前端验证,前端验证)

CTFHUB-技能树-Web前置技能-文件上传&#xff08;无验证&#xff0c;JS前端验证&#xff0c;前端验证—.htaccess&#xff09; 文章目录 CTFHUB-技能树-Web前置技能-文件上传&#xff08;无验证&#xff0c;JS前端验证&#xff0c;前端验证—.htaccess&#xff09;文件上传无验…

GPT-3.5和GPT-Plus的区别

GPT-3.5和GPT-Plus都是OpenAI开发的大型语言模型,但它们之间有一些区别: GPT-3.5就是大家熟知的ChatGPT GPT-Plus 是Open AI 的更强的AI模型GPT-4版本。两者区别是&#xff1a; 模型规模:GPT-Plus是GPT-3的一个更大版本,参数量更多。而GPT-3.5是GPT-3的一个优化版本,在参数量…

✌粤嵌—2024/3/11—跳跃游戏

代码实现&#xff1a; 方法一&#xff1a;递归记忆化 int path; int used[10000];bool dfs(int *nums, int numsSize) {if (path numsSize - 1) {return true;}for (int i 1; i < nums[path]; i) {if (used[path i]) {continue;}path i;used[path] 1;if (dfs(nums, num…

双指针的引入和深入思考(持续更新中)

目录 1.引入双指针 2.使用场景 3.例题引入 1.引入双指针 当我们需要维护某个区间性质的或者是求满足某些性质的区间的长度时&#xff0c;对于一个区间是由左右端点的&#xff0c;我们有简单的枚举左右端点的O()的时间的做法&#xff0c;当时在大多数题目中是不可行的&#…

百度OCR身份证识别C++离线SDKV3.0 C#对接

百度OCR身份证识别C离线SDKV3.0 C#对接 目录 说明 效果 问题 项目 代码 下载 说明 自己根据SDK封装了动态库&#xff0c;然后C#调用。 SDK 简介 本 SDK 适应于于 Windows 平台下的⾝份证识别系统,⽀持 C接⼜开发的 SDK,开发者可在VS2015 下⾯进⾏开发&#xff08;推荐…

Day08React——第八天

useEffect 概念&#xff1a;useEffect 是一个 React Hook 函数&#xff0c;用于在React组件中创建不是由事件引起而是由渲染本身引起的操作&#xff0c;比如发送AJAx请求&#xff0c;更改daom等等 需求&#xff1a;在组件渲染完毕后&#xff0c;立刻从服务器获取频道列表数据…

Appium的使用:混合APP切换上下文

网上别的文章说要把移动端的webview设置成调试模式,才能看到下图信息。 但我这里是直接在Android Studio新建了一个空白活动,然后放的webview控件,写的webview代码,直接部署到模拟器上,在确定adb可以连接到模拟器后,在桌面浏览器输入chrome://inspect/#devices后就可以看…

【代码】Python3|Requests 库怎么继承 Selenium 的 Headers (2024,Chrome)

本文使用的版本&#xff1a; Chrome 124Python 12Selenium 4.19.0 版本过旧可能会出现问题&#xff0c;但只要别差异太大&#xff0c;就可以看本文&#xff0c;因为本文对新老版本都有讲解。 文章目录 1 难点解析和具体思路2 注意事项2.1 PDF 资源获取时注意事项2.2 Capabiliti…

IntelliJ IDEA配置类注释模板和方法注释模板

配置类注释模板和方法注释模板 IDEA模板预定义变量类注释模方法注释模板方法参数优化 IDEA模板 在IDEA中&#xff0c;自带的注释模板可能不满足自身需求或者不满意&#xff0c;此时可以通过配置IDEA模板来解决。 预定义变量 内置模板是可编辑的&#xff0c;除了静态文本、代码和…

关于Git的一些基础用法

关于Git的一些基础用法 1. 前言2. 使用GitHub/gitee创建项目2.1 创建账号2.2 创建项目2.3 下载仓库到本地2.4 提交代码到远端仓库2.5 查看日志2.6 同步远端仓库和本地仓库 1. 前言 首先说一个冷知识&#xff08;好像也不是很冷&#xff09;&#xff0c;Linux和git的创始人是同…

Python贡献度分析(帕累托分析)

贡献度分析又称帕累托分析&#xff0c;它的原理是帕累托法则&#xff0c;又称20/80定律。同样的投入放在不同的地方会产生不同的效益。例如&#xff0c;对一个公司来讲&#xff0c;80%的利润常常来自于20%最畅销的产品&#xff0c;而其他80%的产品只产生了20%的利润 对餐饮企业…

【Leetcode每日一题】 分治 - 颜色分类(难度⭐⭐)(57)

1. 题目解析 题目链接&#xff1a;75. 颜色分类 这个问题的理解其实相当简单&#xff0c;只需看一下示例&#xff0c;基本就能明白其含义了。 2.算法原理 算法思路解析 本算法采用三指针法&#xff0c;将数组划分为三个区域&#xff0c;分别用于存放值为0、1和2的元素。通过…

Promise模块化编程ES6新特性

文章目录 Promise&模块化编程1.Promise基本介绍2.快速入门1.需求分析2.原生ajax jQuery3.Promise使用模板 3.课后练习1.原生ajax jQuery2.promise 4.模块化编程基本介绍5.CommonJS基本介绍6.ES5模块化编程1.题目2.示意图3.代码实例—普通导入导出function.jsuse.js 4.代码…

Spring容器结构

文章目录 1.基本介绍1.Spring5官网2.API文档3.Spring核心学习内容4.几个重要概念 2.快速入门1.需求分析2.入门案例1.新建Java项目2.导入jar包3.编写Monster.java4.src下编写Spring配置文件1.创建spring配置文件&#xff0c;名字随意&#xff0c;但是需要放在src下2.创建Spring …

C语言-指针

1. 指针是什么 指针理解的2个要点&#xff1a; 1.1. 指针是内存中一个最小单元的编号&#xff0c;也就是地址 1.2 平时口语中说的指针&#xff0c;通常指的是指针变量&#xff0c;是用来存放内存地址的变量 总结&#xff1a;指针就是地址&#xff0c;口…

电力系统卫星授时信号安全隔离装置防护方案

电力系统是国家关键基础设施&#xff0c; 电力安全关系国计民生&#xff0c; 是国家安全的重要保障&#xff0c; 与政治安全、经济安全、 网络安全、社会安全等诸多领域密切关联。电网运行情况瞬息万变&#xff0c;为了在其发生事故时能够及时得到处理&#xff0c;需要统一的时…

2.6 类型安全配置属性

无论是Propertes配置还是YAML配置&#xff0c;最终都会被加载到Spring Environment中。 Spring提供了注解Value以及EnvironmentAware接口来将Spring Environment 中的数据注入到属性上&#xff0c;SpringBoot对此进一步提出了类型安全配置属性(Type-safeConfiguration Propert…

【华为笔试题汇总】2024-04-17-华为春招笔试题-三语言题解(Python/Java/Cpp)

&#x1f36d; 大家好这里是KK爱Coding &#xff0c;一枚热爱算法的程序员 ✨ 本系列打算持续跟新华为近期的春秋招笔试题汇总&#xff5e; &#x1f4bb; ACM银牌&#x1f948;| 多次AK大厂笔试 &#xff5c; 编程一对一辅导 &#x1f44f; 感谢大家的订阅➕ 和 喜欢&#x1f…

华为海思校园招聘-芯片-数字 IC 方向 题目分享——第五套

华为海思校园招聘-芯片-数字 IC 方向 题目分享——第五套 (共9套&#xff0c;有答案和解析&#xff0c;答案非官方&#xff0c;仅供参考&#xff09;&#xff08;共九套&#xff0c;每套四十个选择题&#xff09; 部分题目分享&#xff0c;完整版获取&#xff08;WX:didadida…