lwip-2.1.3自带的httpd网页服务器使用教程(二)使用SSI动态生成网页部分内容

news2024/12/23 15:04:50

上一篇:lwip-2.1.3自带的httpd网页服务器使用教程(一)从SD卡读取网页文件并显示

通过全局数组定义TAG标签列表

(本节例程名称:ssi_test)
电脑上用的Web服务器采用ASP、PHP或JSP动态网页技术后,可以根据HTTP模板(asp、php或jsp文件),动态替换掉网页中的<% %>或<?php ?>标签,生成动态网页。lwip自带的httpd也有类似的功能,动态网页的文件扩展名为.ssi,定界符为<!--#TAG-->,其中TAG是不超过LWIP_HTTPD_MAX_TAG_NAME_LEN长度的自定义名称,替换后的文本长度不超过LWIP_HTTPD_MAX_TAG_INSERT_LEN个字符。因为lwip主要在嵌入式系统中运行,所以httpd的ssi功能实现得比较简单。

SSI功能默认是不开启的。开启SSI的方法是在lwipopts.h中定义下面的宏:

// 配置HTTPD
#define LWIP_HTTPD_SSI 1
#define LWIP_HTTPD_SSI_INCLUDE_TAG 0

LWIP_HTTPD_SSI=1是开启SSI功能的意思,LWIP_HTTPD_SSI_INCLUDE_TAG=0意思是不在最终生成的HTML网页中保留<!--#TAG-->标签。
接下来我们要在C语言程序中定义一下TAG标签列表,和TAG标签替换的内容。
HTTP服务器是在main函数中初始化的,我们在httpd_init()之后新增一个test_init()函数调用:

httpd_init(); // 启动网页服务器
test_init();

test_init函数在新建的test.c中实现:

#include <lwip/apps/httpd.h>
#include <lwip/def.h>
#include <stm32f1xx.h>
#include <string.h>
#include <time.h>
#include "test.h"

ADC_HandleTypeDef hadc3;
static const char *ssi_tags[] = {"light", "temp", "devname", "devtype1", "devtype2", "devtype3", "datetime"};

static double test_adc_read(uint32_t channel)
{
  double voltage;
  uint32_t value;
  ADC_ChannelConfTypeDef adc_channel;
  
  adc_channel.Channel = channel;
  adc_channel.Rank = ADC_REGULAR_RANK_1;
  adc_channel.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;
  HAL_ADC_ConfigChannel(&hadc3, &adc_channel);
  
  HAL_ADC_Start(&hadc3);
  HAL_ADC_PollForConversion(&hadc3, HAL_MAX_DELAY);
  value = HAL_ADC_GetValue(&hadc3);
  voltage = value * 3.3 / 4096;
  return voltage;
}

static u16_t test_ssi_handler(int iIndex, char *pcInsert, int iInsertLen)
{
  struct tm tm;
  time_t t;
  
  // 注意: 不要直接返回snprintf函数的返回值
  // 当iInsertLen空间不够时snprintf返回的是欲写入的字符个数,不是真正写入的个数
  switch (iIndex)
  {
    case 0:
      snprintf(pcInsert, iInsertLen, "%.2fV", test_adc_read(ADC_CHANNEL_5));
      break;
    case 1:
      snprintf(pcInsert, iInsertLen, "%.2fV", test_adc_read(ADC_CHANNEL_4));
      break;
    case 2:
      snprintf(pcInsert, iInsertLen, "STM32F103ZET6");
      break;
    case 3:
    case 5:
      pcInsert[0] = '\0';
      break;
    case 4:
      snprintf(pcInsert, iInsertLen, " selected=\"selected\"");
      break;
    case 6:
      time(&t);
      localtime_r(&t, &tm);
      strftime(pcInsert, iInsertLen, "%Y-%m-%d %H:%M:%S", &tm);
      break;
    default:
      return HTTPD_SSI_TAG_UNKNOWN;
  }
  return strlen(pcInsert);
}

static void test_adc_init(void)
{
  GPIO_InitTypeDef gpio;
  
  __HAL_RCC_ADC3_CLK_ENABLE();
  __HAL_RCC_GPIOF_CLK_ENABLE();
  
  gpio.Mode = GPIO_MODE_ANALOG;
  gpio.Pin = GPIO_PIN_6 | GPIO_PIN_7;
  HAL_GPIO_Init(GPIOF, &gpio);
  
  hadc3.Instance = ADC3;
  hadc3.Init.ExternalTrigConv = ADC_SOFTWARE_START;
  HAL_ADC_Init(&hadc3);
}

void test_init(void)
{
  test_adc_init();
  http_set_ssi_handler(test_ssi_handler, ssi_tags, LWIP_ARRAYSIZE(ssi_tags));
}

在test_init函数中,我们调用了lwip httpd提供的http_set_ssi_handler函数设置TAG标签的列表和TAG标签处理函数。
TAG标签处理函数的名称是test_ssi_handler。TAG标签列表由全局数组ssi_tags定义(lwip规定此变量必须为全局变量,不能为局部变量),数组的大小为LWIP_ARRAYSIZE(ssi_tags)。LWIP_ARRAYSIZE是lwip提供的取数组元素个数的函数,定义在<lwip/def.h>头文件中。
ssi_tags数组一共定义了7个TAG标签,下标为0~6。
static const char *ssi_tags[] = {"light", "temp", "devname", "devtype1", "devtype2", "devtype3", "datetime"};
这些标签的替换内容由test_ssi_handler函数定义。函数的原型是:
static u16_t test_ssi_handler(int iIndex, char *pcInsert, int iInsertLen);
其中参数iIndex是当前要处理的TAG标签在ssi_tags数组中的下标号,pcInsert是存放替换后文本的缓冲区,iInsertLen是缓冲区的大小。函数的返回值是替换后文本的实际长度。如果当前不想替换该标签,可以返回HTTPD_SSI_TAG_UNKNOWN。

最后,我们把HTML网页模板info.ssi放入lwip-2.1.3/apps/http/fs文件夹中,并运行lwip-2.1.3/apps/http/makefsdata.exe程序,将网页打包成fsdata.c文件。info.ssi的内容如下。

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>STM32F103ZE ENC28J60</title>
<style type="text/css"> 
<!-- 
body {
	font: 100% Verdana, Arial, Helvetica, sans-serif;
	background: #666666;
	margin: 0;
	padding: 0;
	text-align: center;
	color: #000000;
}
#container { 
	width: 80%;
	background: #FFFFFF;
	margin: 0 auto;
	border: 1px solid #000000;
	text-align: left;
} 
#header { 
	background: #DDDDDD; 
	padding: 0 10px;
} 
#header h1 {
	margin: 0;
	padding: 10px 0;
}
#sidebar1 {
	float: left;
	width: 22%;
	background: #EBEBEB;
	padding: 15px 0;
}
#sidebar2 {
	float: right;
	width: 23%;
	background: #EBEBEB;
	padding: 15px 0;
}
#sidebar1 p, #sidebar1 h3, #sidebar2 p, #sidebar2 h3 {
	margin-left: 10px;
	margin-right: 10px;
}
#mainContent { 
	margin: 0 24% 0 23.5%;
}
#mainContent form {
	line-height: 2em;
}
#footer { 
	padding: 0 10px;
	background: #DDDDDD;
} 
#footer p {
	margin: 0;
	padding: 10px 0;
}
.fltrt {
	float: right;
	margin-left: 8px;
}
.fltlft {
	float: left;
	margin-right: 8px;
}
.clearfloat {
	clear: both;
    height: 0;
    font-size: 1px;
    line-height: 0px;
}
--> 
</style>
</head>

<body>
<div id="container">
  <div id="header">
    <h1>STM32F103ZE ENC28J60</h1>
  </div>
  <div id="sidebar1">
    <h3>传感器信息</h3>
    <p>光敏电阻: <!--#light--></p>
    <p>热敏电阻: <!--#temp--></p>
  </div>
  <div id="sidebar2">
    <h3>其他信息</h3>
    <p>stm32有自带的以太网模块,为什么还要用ENC28J60?</p>
    <p>首先,很多STM32的型号都是不带内置网卡的。其次,ENC28J60相对成熟,很多人因为有现成的ENC28J60方案,所以直接使用。</p>
  </div>
  <div id="mainContent">
    <h2>器件搜索</h2>
    <form name="form1" method="get">
      <label>
        器件名称:
        <input name="devname" type="text" id="devname" value="<!--#devname-->">
      </label><br>
      <label>
        器件类型: 
        <select name="devtype" id="devtype">
          <option value="1"<!--#devtype1-->>单片机芯片</option>
          <option value="2"<!--#devtype2-->>网络芯片</option>
          <option value="3"<!--#devtype3-->>音频芯片</option>
        </select>
      </label><br>
      <input type="submit" value="搜索">
    </form>
    <h1>搜索结果</h1>
    <p>暂无任何器件</p>
  </div>
  <br class="clearfloat" />
  <div id="footer">
    <p><b>当前时间: </b><!--#datetime--></p>
  </div>
</div>
</body>
</html>

程序运行结果:

可以看到,我们成功在网页中显示了光敏电阻和热敏电阻的电压值,以及当前时间。程序还指定了表单里面的文本框的显示文本和下拉菜单框的选中项。

扩展阅读:小梅哥AC620开发板NIOS II LWIP实现HTTP网页控制数码管的显示内容

通过回调函数直接处理所有标签

(本节例程名称:ssi_test2)
如果网页比较多的话,把所有网页用到的标签名都放到ssi_tags全局数组中也不太现实。lwip允许我们开启LWIP_HTTPD_SSI_RAW选项,不用定义ssi_tags全局数组,直接在test_ssi_handler回调函数里面判断标签名就行。

// 配置HTTPD
#define LWIP_HTTPD_SSI 1
#define LWIP_HTTPD_SSI_INCLUDE_TAG 0
#define LWIP_HTTPD_SSI_RAW 1

开启LWIP_HTTPD_SSI_RAW选项后,test_ssi_handler的第一个参数就变成字符串了,ssi_tags全局数组就可以删了。
static u16_t test_ssi_handler(const char *ssi_tag_name, char *pcInsert, int iInsertLen);

static u16_t test_ssi_handler(const char *ssi_tag_name, char *pcInsert, int iInsertLen)
{
  struct tm tm;
  time_t t;
  
  // 注意: 不要直接返回snprintf函数的返回值
  // 当iInsertLen空间不够时snprintf返回的是欲写入的字符个数,不是真正写入的个数
  if (strcmp(ssi_tag_name, "light") == 0)
    snprintf(pcInsert, iInsertLen, "%.2fV", test_adc_read(ADC_CHANNEL_5));
  else if (strcmp(ssi_tag_name, "temp") == 0)
    snprintf(pcInsert, iInsertLen, "%.2fV", test_adc_read(ADC_CHANNEL_4));
  else if (strcmp(ssi_tag_name, "devname") == 0)
    snprintf(pcInsert, iInsertLen, "STM32F103ZET6");
  else if (strcmp(ssi_tag_name, "devtype1") == 0 || strcmp(ssi_tag_name, "devtype3") == 0)
    snprintf(pcInsert, iInsertLen, "");
  else if (strcmp(ssi_tag_name, "devtype2") == 0)
    snprintf(pcInsert, iInsertLen, " selected=\"selected\"");
  else if (strcmp(ssi_tag_name, "datetime") == 0)
  {
    time(&t);
    localtime_r(&t, &tm);
    strftime(pcInsert, iInsertLen, "%Y-%m-%d %H:%M:%S", &tm);
  }
  else
    return HTTPD_SSI_TAG_UNKNOWN;
  return strlen(pcInsert);
}

void test_init(void)
{
  test_adc_init();
  http_set_ssi_handler(test_ssi_handler, NULL, 0);
}

分多次替换同一个HTTP连接的同一个标签的内容

(本节例程名称:ssi_test3)
在实际应用中,有的时候某个标签替换的内容很长,默认的LWIP_HTTPD_MAX_TAG_INSERT_LEN=192字节的空间根本装不下。虽然可以将LWIP_HTTPD_MAX_TAG_INSERT_LEN的值改大,但是这样会增大内存消耗。我们可以开启LWIP_HTTPD_SSI_MULTIPART选项,把一段长文本拆成很多段,多次替换。
由于需要执行多次替换,如果每次刷新网页,替换的内容都不相同的话,那么两个人同时访问这张网页就会出问题,会发生相互干扰。为了防止相互干扰,我们可以开启LWIP_HTTPD_FILE_STATE选项,每一次新打开一个连接的时候,就分配一段内存,生成好要替换的内容。替换的时候直接发送已生成的内容就行了。
通常情况下打开了LWIP_HTTPD_SSI_MULTIPART选项,也要同时打开LWIP_HTTPD_FILE_STATE选项。不过,两者也可以单独使用。
开启LWIP_HTTPD_FILE_STATE选项后需要实现下面两个函数。
void *fs_state_init(struct fs_file *file, const char *name);
void fs_state_free(struct fs_file *file, void *state);
fs_state_init函数根据网页名称name创建并填充自定义结构体并返回。
fs_state_free函数用于释放fs_state_init函数创建的结构体所占用的内存。

开启LWIP_HTTPD_FILE_STATE或LWIP_HTTPD_SSI_MULTIPART选项后,test_ssi_handler函数的参数也会发生改变。
当LWIP_HTTPD_FILE_STATE=0且LWIP_HTTPD_SSI_MULTIPART=0时:
static u16_t test_ssi_handler(const char *ssi_tag_name, char *pcInsert, int iInsertLen);

当LWIP_HTTPD_FILE_STATE=0且LWIP_HTTPD_SSI_MULTIPART=1时:
static u16_t test_ssi_handler(const char *ssi_tag_name, char *pcInsert, int iInsertLen, u16_t current_tag_part, u16_t *next_tag_part);
第一次调用回调函数时,current_tag_part的值为0。后续调用回调函数时,current_tag_part的值由前一次调用时函数内设置的*next_tag_part的值决定。
在回调函数内,如果没有给*next_tag_part赋值,那么*next_tag_part的值为HTTPD_LAST_TAG_PART,表明当前输出的是标签内容的最后一段文本,后续不再为此标签调用此回调函数。如果给*next_tag_part赋值了,且不等于HTTPD_LAST_TAG_PART,那么还会有下一次函数调用。

当LWIP_HTTPD_FILE_STATE=1且LWIP_HTTPD_SSI_MULTIPART=0时:
static u16_t test_ssi_handler(const char *ssi_tag_name, char *pcInsert, int iInsertLen, void *connection_state);
connection_state是之前fs_state_init函数创建的自定义结构体。

当LWIP_HTTPD_FILE_STATE=1且LWIP_HTTPD_SSI_MULTIPART=1时:
static u16_t test_ssi_handler(const char *ssi_tag_name, char *pcInsert, int iInsertLen, u16_t current_tag_part, u16_t *next_tag_part, void *connection_state);

我们来修改一下刚才的工程。

// 配置HTTPD
#define LWIP_HTTPD_FILE_STATE 1
#define LWIP_HTTPD_SSI 1
#define LWIP_HTTPD_SSI_INCLUDE_TAG 0
#define LWIP_HTTPD_SSI_MULTIPART 1
#define LWIP_HTTPD_SSI_RAW 1

因为我们的C语言源文件test.c用的是GB2312编码,为了防止C语言里面的汉字输出到网页上后乱码,我们也要把网页文件的编码改成GB2312。
在Dreamweaver CS3里面,在“修改”菜单下选择“页面属性”命令,在“标题/编码”选项下将编码修改为“简体中文(GB2312)”就行了。
Dreamweaver会自动将网页里面meta标签的charset属性值修改为gb2312。
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">

将网页另存为info.ssi,放入lwip-2.1.3/apps/http/fs文件夹中,再次运行lwip-2.1.3/apps/http/makefsdata.exe程序,更新fsdata.c。
在Keil中修改test.c文件:

#include <lwip/apps/fs.h>
#include <lwip/apps/httpd.h>
#include <lwip/mem.h>
#include <stm32f1xx.h>
#include <string.h>
#include <time.h>
#include "test.h"

struct page_state
{
  char datetime[50];
  char othermsg[1500];
  u16_t othermsg_len;
};

void *fs_state_init(struct fs_file *file, const char *name)
{
  char part[50];
  int i, value;
  struct page_state *state;
  struct tm tm;
  time_t t;
  
  if (strcmp(name, "/info.ssi") == 0)
  {
    state = mem_malloc(sizeof(struct page_state));
    if (state == NULL)
      return NULL;
    printf("%s: new state(0x%p)\n", __func__, state);
    
    time(&t);
    localtime_r(&t, &tm);
    strftime(state->datetime, sizeof(state->datetime), "%Y-%m-%d %H:%M:%S", &tm);
    
    i = 1;
    state->othermsg[0] = '\0';
    while (i != -1)
    {
      value = rand();
      snprintf(part, sizeof(part), "第%d个随机数的值是%d。", i, value);
      if (strlen(state->othermsg) + strlen(part) + 1 <= sizeof(state->othermsg))
      {
        strcat(state->othermsg, part);
        i++;
      }
      else
        i = -1;
    }
    state->othermsg_len = strlen(state->othermsg);
    return state;
  }
  else
    return NULL;
}

void fs_state_free(struct fs_file *file, void *state)
{
  if (state != NULL)
  {
    printf("%s: delete state(0x%p)\n", __func__, state);
    mem_free(state);
  }
}

static u16_t test_ssi_handler(const char *ssi_tag_name, char *pcInsert, int iInsertLen, u16_t current_tag_part, u16_t *next_tag_part, void *connection_state)
{
  struct page_state *state = connection_state;
  uint16_t len;
  
  if (state == NULL)
    return HTTPD_SSI_TAG_UNKNOWN;
  
  // 注意: 不要直接返回snprintf函数的返回值
  // 当iInsertLen空间不够时snprintf返回的是欲写入的字符个数,不是真正写入的个数
  if (strcmp(ssi_tag_name, "light") == 0)
    snprintf(pcInsert, iInsertLen, "%.2fV", test_adc_read(ADC_CHANNEL_5));
  else if (strcmp(ssi_tag_name, "temp") == 0)
    snprintf(pcInsert, iInsertLen, "%.2fV", test_adc_read(ADC_CHANNEL_4));
  else if (strcmp(ssi_tag_name, "devname") == 0)
    snprintf(pcInsert, iInsertLen, "STM32F103ZET6");
  else if (strcmp(ssi_tag_name, "devtype1") == 0 || strcmp(ssi_tag_name, "devtype3") == 0)
    snprintf(pcInsert, iInsertLen, "");
  else if (strcmp(ssi_tag_name, "devtype2") == 0)
    snprintf(pcInsert, iInsertLen, " selected=\"selected\"");
  else if (strcmp(ssi_tag_name, "datetime") == 0)
    snprintf(pcInsert, iInsertLen, "%s", state->datetime);
  else if (strcmp(ssi_tag_name, "othermsg") == 0)
  {
    len = state->othermsg_len - current_tag_part;
    if (len > iInsertLen - 1)
    {
      // 本次如果发不完, 下次还得接着发, *next_tag_part需要赋值
      len = iInsertLen - 1;
      *next_tag_part = current_tag_part + len;
    }
    else
    {
      // 本次发得完, 就没有下次了, *next_tag_part就不用赋值
    }
    memcpy(pcInsert, state->othermsg + current_tag_part, len);
    pcInsert[len] = '\0';
    printf("%s(0x%p, %s): pos=%u~%u, len=%u, tot_len=%u\n", __func__, state, ssi_tag_name, current_tag_part, current_tag_part + len - 1, len, state->othermsg_len);
    return len;
  }
  else
    return HTTPD_SSI_TAG_UNKNOWN;
  return strlen(pcInsert);
}

程序运行结果:

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

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

相关文章

Verdi之状态机状态查询nState

目录 5. nState 5.1如何打开状态机视图 5.2 如何在状态机视图中添加状态转移条件 5.3 如何查看状态机个状态对应的相关代码 5.4 查看具体的状态机状态转移时间点 5.5 查找自定义状态 5.6 查找某个状态执行的次数 5. nState 5.1如何打开状态机视图 1. 将设计文件导入Ver…

人脸识别学习

1、人脸识别如何做到一次学习 &#xff08;1&#xff09;、数据收集&#xff1a;收集包含多个人脸图像的数据集&#xff0c;每个人脸图像都有对应的标签或身份信息。 &#xff08;2&#xff09;、图像预处理&#xff1a;对数据集中的每个人脸图像进行预处理&#xff0c;包括图…

pytorch的配置从此不在愁

anaconda配置 原视频的配置网站&#xff1a;Anaconda pytorch&#xff1a; 【从入门到进阶】PyTorch深度学习框架完结合集&#xff01;草履虫都能学会的计算机视觉实战&#xff08;PyTorch安装/深度学习实战/神经网络/人工智能&#xff09;_哔哩哔哩_bilibili 上面的视频 …

开放麒麟操作系统能打动嵌入式软件工程师吗?

国产操作系统大部分客户其实是toB的&#xff0c;内核以外的源码也是不开源的&#xff0c;比如麒麟系、统信UOS等&#xff0c;个人学习、小企业基本不会用。7月5日开放麒麟操作系统v1.0版正式发布&#xff0c;不同点是这个操作系统从内核到桌面系统源代码都是开源的&#xff0c;…

SpringCloud Alibaba——Nacos1.x配置中心长轮询机制

目录 一、Nacos1.x配置中心长轮询机制二、Nacos1.x配置中心长轮询机制流程图解 一、Nacos1.x配置中心长轮询机制 客户端会轮询向服务端发出一个长连接请求&#xff0c;这个长连接最多30s就会超时&#xff0c;服务端收到客户端的请求会先判断当前是否有配置更新&#xff0c;有则…

网络协议 — LLDP 数据链路发现协议

目录 文章目录 目录LLDPLLDPDUEthernet II LLDPDUSNAP LLDPDULLDPDU TLVs基本 TLV802.1 定义的 TLV802.3 定义的 TLV802.3 定义的 LLDP-MED TLV LLDP 消息流程LLDP 协议栈 LLDP LLDP&#xff08;Link Layer Discovery Protocol&#xff0c;链路层发现协议&#xff09;是定义在…

Windows环境下安装Nacos

文章目录 一、什么是Nacos1. 主要特点&#xff1a;1.1 服务发现和注册&#xff1a;1.2 配置管理&#xff1a;1.3 服务管理&#xff1a;1.4 多语言支持&#xff1a;1.5 高可用性和扩展性&#xff1a; 二、Windows下安装单机版Nacos1. 安装包下载&#xff1a;2. 目录文件说明&…

基于torchvision的CV迁移学习

前面我们用过了cifar10&#xff0c;这里因为我们模型的体量更大&#xff0c;他能够理解更加复杂的数据集&#xff0c;所以这里我们就使用更加复杂的数据集叫做cifar100&#xff0c;顾名思义就是它是一个100分类的图像数据集&#xff0c;分类数据更多&#xff0c;复杂度更多。 …

哈希表(hashtable)的数据插入、查找和遍历

文章目录 前言一、哈希二、哈希的具体实现2.1 准备工作2.2 插入数据2.3 输出哈希表2.4 在哈希表中寻找数据2.5 销毁哈希表 三、 哈希表的调用总结 前言 本期主要和大家介绍一下哈希算法&#xff0c;这里主要给出哈希算法的实现方法&#xff1b; 一、哈希 hash是一种算法: 哈希…

Linux进程理解【环境变量】

Linux进程理解【环境变量】 提到环境变量&#xff0c;大家可能有些陌生&#xff0c;如果编写过Java就知道&#xff0c;编写Java需要提前安装JDK&#xff0c;这个操作就是配置Java的编码环境&#xff0c;在Linux中当然也少不了环境变量&#xff0c;下面我们就一起来看看 文章目…

SpringBoot 统一功能的处理

SpringBoot 统一功能的处理 文章目录 SpringBoot 统一功能的处理1. 用户登录权限校验1.1 最初用户登录验证1.2 Spring AOP 统一用户登录验证的问题1.3 SpringAOP 拦截器1.3.1 实现自定义拦截器1.3.2 将自定义拦截器加入到系统配置 1.4 拦截器实现原理1.4.1 实现流程图1.4.2 实现…

LeetCode:3. 无重复字符的最长子串

&#x1f34e;道阻且长&#xff0c;行则将至。&#x1f353; &#x1f33b;算法&#xff0c;不如说它是一种思考方式&#x1f340; 算法专栏&#xff1a; &#x1f449;&#x1f3fb;123 题解目录 一、&#x1f331;[3. 无重复字符的最长子串](https://leetcode.cn/problems/l…

分享维修一例DELL R540服务器黄灯无法开机故障

DELL PowerEdge R540服务器故障维修案例&#xff1a;&#xff08;看到文章就是缘分&#xff09; 客户名称&#xff1a;东莞市某街道管理中心 故障机型&#xff1a;DELL R540服务器 故障问题&#xff1a;DELL R540服务器无法开机&#xff0c;前面板亮黄灯&#xff0c;工程师通过…

私有GitLab仓库 - 本地搭建GitLab私有代码仓库并随时远程访问

文章目录 前言1. 下载Gitlab2. 安装Gitlab3. 启动Gitlab4. 安装cpolar内网穿透5. 创建隧道配置访问地址6. 固定GitLab访问地址6.1 保留二级子域名6.2 配置二级子域名 7. 测试访问二级子域名 前言 GitLab 是一个用于仓库管理系统的开源项目&#xff0c;使用Git作为代码管理工具…

javaee jstl表达式

jstl是el表达式的扩展 使用jstl需要添加jar包 package com.test.servlet;import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map;import javax.servlet.ServletException; import javax.servlet…

【C++】面向对象三大特性之继承

【C】面向对象三大特性之继承 继承的概念继承基类成员访问方式的变化子类到父类对象之间赋值兼容转换继承中的作用域子类的默认成员函数继承和友元、静态成员的关系菱形继承和菱形的虚拟继承虚拟继承解决二义性和数据冗余 继承的概念 继承&#xff1a;是面向对象程序设计使代码…

解析Android VNDK/VSDK Snapshot编译框架

1.背景 背景一&#xff1a; 为解决Android版本碎片化问题&#xff0c;引入Treble架构&#xff0c;它提供了稳定的新SoC供应商接口&#xff0c;引入HAL 接口定义语言&#xff08;HIDL/Stable AIDL&#xff0c;技术栈依然是Binder)&#xff0c;它指定了 vendor HAL 和system fr…

动态规划01背包之416分割等和子集(第10道)

题目&#xff1a; 给你一个 只包含正整数 的 非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集&#xff0c;使得两个子集的元素和相等。 示例&#xff1a; 解法&#xff1a; 先复习一下01背包问题&#xff1a; dp[i][j]的含义&#xff1a;从下标为[0-i]的物品里…

【Spring Boot】Spring Boot的系统配置 — 日志配置

日志配置 日志对于系统监控、故障定位非常重要&#xff0c;比如当生产系统发生问题时&#xff0c;完整清晰的日志记录有助于快速定位问题。接下来介绍Spring Boot对日志的支持。 1.Spring Boot日志简介 Spring Boot自带spring-boot-starter-logging库实现系统日志功能&#…

基于linux下的高并发服务器开发(第一章)- 目录操作函数

09 / 目录操作函数 &#xff08;1&#xff09;int mkdir(const char* pathname,mode_t mode); #include <sys/stat.h> #include <sys/types.h>int mkdir(const char *pathname, mode_t mode); 作用&#xff1a;创建一个目录 参数&#xff1a; pat…