当前位置: 首页 > news >正文

【在Linux世界中追寻伟大的One Piece】多路转接epoll(续)

目录

1 -> epoll的工作方式

1.1 -> 水平触发(Level Triggered)工作模式

1.2 -> 边缘触发(Edge Triggered)工作模式

2 -> 对比LT与ET

3 -> 理解ET模式和非阻塞文件描述符

4 -> epoll的使用场景

5 -> epoll示例

5.1 -> epoll服务器(LT模式)

5.2 -> epoll服务器(ET模式)


1 -> epoll的工作方式

epoll有2中工作方式:

  • 水平触发(LT)
  • 边缘触发(ET)

加入有这样一个例子:

  • 已经把一个tcp_socket添加到epoll描述符。
  • 这个时候socket的另一端被写入了2KB的数据。
  • 调用epoll_wait,并且它会返回。说明它已经准备好读取操作。
  • 然后调用read,只读取了1KB的数据。
  • 继续调用epoll_wait……

1.1 -> 水平触发(Level Triggered)工作模式

epoll默认状态下就是LT工作模式。 

  • 当epoll检测到socket上事件就绪的时候,可以不立刻进行处理。或者只处理一部分。
  • 如上面的例子,由于只读了1K的数据,缓冲区中还剩1K的数据,在第二次调用epoll_wait时,epoll_wait仍然会立刻返回并通知socket读事件就绪。
  • 直到缓冲区上所有的数据都被处理完,epoll_wait才不会立刻返回。
  • 支持阻塞读写和非阻塞读写

1.2 -> 边缘触发(Edge Triggered)工作模式

如果我们在第一步将socket添加到epoll描述符的时候使用了EPOLLET标志,epoll进入ET工作模式。

  • 当epoll检测到socket上事件就绪时,必须立刻处理。
  • 如上面的例子,虽然只读了1K的数据,缓冲区还剩1K的数据,在第二次调用epoll_wait的时候,epoll_wait不会再返回了。
  • 也就是说,ET模式下,文件描述符上的事件就绪后,只有一次处理机会。
  • ET的性能比LT的性能更高(epoll_wait返回的次数少了很多)。Nginx默认采用ET模式使用epoll。
  • 只支持非阻塞的读写

select和poll其实也是工作在LT模式下。epoll既可以支持LT,也可以支持ET。

2 -> 对比LT与ET

LT是epoll的默认行为

使用ET能够减少epoll触发的次数。但是代价就是强逼着程序员一次响应就绪过程中就把所有的数据都处理完。

相当于一个文件描述符就绪后,不会反复被提示就绪,看起来就比LT更高效一些。但是在LT情况下如果也能做到每次就绪的文件描述符都立刻处理,不让这个就绪被反复提示的话,其实性能也是一样的。

另一方面,ET的代码复杂程度更高了。

3 -> 理解ET模式和非阻塞文件描述符

使用ET模式的epoll,需要将文件描述设置为非阻塞。这个不是接口上的要求,而是“工程实践”上的要求。

假设一个场景:服务器接收到一个10k的请求,会向客户端返回一个应答数据。如果客户端收不到应答,不会发送第二个10K的请求。

如果服务端写的代码是阻塞式的read,并且一次只read1K数据的话(read不能保证一次就把所有的数据都读出来,参考man手册的说明,可能被信号打断),剩下的9K数据就会待在缓冲区中。

此时由于epoll是ET模式,并不会认为文件描述符读就绪。epoll_wait就不会再次返回。剩下的9K数据会一直在缓冲区中。直到下一次客户端再给服务器写数据。

但是问题来了。

  • 服务器只读到1K个数据,要10K读完才会给客户端返回响应数据。
  • 客户端要读到服务器的响应,才会发送下一个请求。
  • 客户端发送了下一个请求,epoll_wait才会返回,才能去读缓冲区中剩余的数据。

所以,为了解决上述问题(阻塞read不一定能一下把完整的请求读完),于是就可以使用非阻塞轮训的方式来读缓冲区,保证一定能把完整的请求都读出来。

而如果是LT没这个问题。只要缓冲区中的数据没读完,就能够让epoll_wait返回文件描述符读就绪。

4 -> epoll的使用场景

epoll的高性能,是有一定的特定场景的。如果场景选择的不适宜,epoll的性能可能适得其反。

  • 对于多连接,且多连接中只有一部分连接比较活跃时,比较适合使用epoll。

例如,典型的一个需要处理上万个客户端的服务器,例如各种互联网APP的入口服务器,这样的服务器就很适合epoll。

如果只是系统内部,服务器和服务器之间进行通信,只有少数的几个连接,这种情况下用epoll就并不适合。具体要根据需求和场景特点来决定使用哪种IO模型。

5 -> epoll示例

5.1 -> epoll服务器(LT模式)

tcp_epoll_server.hpp

///
// 封装一个 Epoll 服务器, 只考虑读就绪的情况
///#pragma once
#include <vector>
#include <functional>
#include <iostream>
#include <sys/epoll.h>
#include "tcp_socket.hpp"typedef std::function<void(const std::string&, std::string*resp)> Handler;class Epoll 
{
public:Epoll() {epoll_fd_ = epoll_create(10);}~Epoll() {close(epoll_fd_);}bool Add(const TcpSocket& sock) const {int fd = sock.GetFd();printf("[Epoll Add] fd = %d\n", fd);epoll_event ev;ev.data.fd = fd;ev.events = EPOLLIN;int ret = epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, fd, &ev);if (ret < 0) {perror("epoll_ctl ADD");return false;}return true;}bool Del(const TcpSocket& sock) const {int fd = sock.GetFd();printf("[Epoll Del] fd = %d\n", fd);int ret = epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, fd, NULL);if (ret < 0) {perror("epoll_ctl DEL");return false;}return true;}bool Wait(std::vector<TcpSocket>* output) const {output->clear();epoll_event events[1000];int nfds = epoll_wait(epoll_fd_, events, sizeof(events) / sizeof(events[0]), -1);if (nfds < 0) {perror("epoll_wait");return false;}// [注意!] 此处必须是循环到 nfds, 不能多循环for (int i = 0; i < nfds; ++i) {TcpSocket sock(events[i].data.fd);output->push_back(sock);}return true;}private:int epoll_fd_;
};class TcpEpollServer 
{
public:TcpEpollServer(const std::string& ip, uint16_t port) : ip_(ip),port_(port) {}bool Start(Handler handler) {// 1. 创建 socketTcpSocket listen_sock;CHECK_RET(listen_sock.Socket());// 2. 绑定CHECK_RET(listen_sock.Bind(ip_, port_));// 3. 监听CHECK_RET(listen_sock.Listen(5));// 4. 创建 Epoll 对象, 并将 listen_sock 加入进去Epoll epoll;epoll.Add(listen_sock);// 5. 进入事件循环for (;;) {// 6. 进行 epoll_waitstd::vector<TcpSocket> output;if (!epoll.Wait(&output)) {continue;}// 7. 根据就绪的文件描述符的种类决定如何处理for (size_t i = 0; i < output.size(); ++i) {if (output[i].GetFd() == listen_sock.GetFd()) {// 如果是 listen_sock, 就调用 acceptTcpSocket new_sock;listen_sock.Accept(&new_sock);epoll.Add(new_sock);}else {// 如果是 new_sock, 就进行一次读写std::string req, resp;bool ret = output[i].Recv(&req);if (!ret) {// [注意!!] 需要把不用的 socket 关闭// 先后顺序别搞反. 不过在 epoll 删除的时候其实就已经关闭 socket 了epoll.Del(output[i]);output[i].Close();continue;}handler(req, &resp);output[i].Send(resp);} // end for} // end for (;;)}return true;}private:std::string ip_;uint16_t port_;
};

dict_server.cc只需要将server对象的类型改成TcpEpollServer即可。

5.2 -> epoll服务器(ET模式)

基于LT版本稍加修改即可。

  1. 修改tcp_socket.hpp,新增非阻塞读和非阻塞写接口。
  2. 对于accept返回的new_sock加上EPOLLET这样的选项。

注意:

此代码暂时未考虑listen_sock ET的情况。如果将listen_sock设为ET,则需要非阻塞轮询的方式accept。否则会导致同一时刻大量的客户端同时连接的时候,只能accept一次的问题。

tcp_socket.hpp

// 以下代码添加在 TcpSocket 类中
// 非阻塞 IO 接口bool SetNoBlock() 
{int fl = fcntl(fd_, F_GETFL);if (fl < 0) {perror("fcntl F_GETFL");return false;}int ret = fcntl(fd_, F_SETFL, fl | O_NONBLOCK);if (ret < 0) {perror("fcntl F_SETFL");return false;}return true;
}bool RecvNoBlock(std::string* buf) const 
{// 对于非阻塞 IO 读数据, 如果 TCP 接受缓冲区为空, 就会返回错误// 错误码为 EAGAIN 或者 EWOULDBLOCK, 这种情况也是意料之中, 需要重试// 如果当前读到的数据长度小于尝试读的缓冲区的长度, 就退出循环// 这种写法其实不算特别严谨(没有考虑粘包问题)buf->clear();char tmp[1024 * 10] = { 0 };for (;;) {ssize_t read_size = recv(fd_, tmp, sizeof(tmp) - 1, 0);if (read_size < 0) {if (errno == EWOULDBLOCK || errno == EAGAIN) {continue;}perror("recv");return false;}if (read_size == 0) {// 对端关闭, 返回 falsereturn false;}tmp[read_size] = '\0';*buf += tmp;if (read_size < (ssize_t)sizeof(tmp) - 1) {break;}}return true;
}bool SendNoBlock(const std::string& buf) const 
{// 对于非阻塞 IO 的写入, 如果 TCP 的发送缓冲区已经满了, 就会出现出错的情况// 此时的错误号是 EAGAIN 或者 EWOULDBLOCK. 这种情况下不应放弃治疗// 而要进行重试ssize_t cur_pos = 0; // 记录当前写到的位置ssize_t left_size = buf.size();for (;;) {ssize_t write_size = send(fd_, buf.data() + cur_pos,left_size, 0);if (write_size < 0) {if (errno == EAGAIN || errno == EWOULDBLOCK) {// 重试写入continue;}return false;}cur_pos += write_size;left_size -= write_size;// 这个条件说明写完需要的数据了if (left_size <= 0) {break;}}return true;
}

tcp_epoll_server.hpp

///
// 封装一个 Epoll ET 服务器
// 修改点:
// 1. 对于 new sock, 加上 EPOLLET 标记
// 2. 修改 TcpSocket 支持非阻塞读写
// [注意!] listen_sock 如果设置成 ET, 就需要非阻塞调用 accept 了
///#pragma once#include <vector>
#include <functional>
#include <sys/epoll.h>
#include "tcp_socket.hpp"typedef std::function<void(const std::string&, std::string* resp)> Handler;class Epoll 
{
public:Epoll() {epoll_fd_ = epoll_create(10);}~Epoll() {close(epoll_fd_);}bool Add(const TcpSocket& sock, bool epoll_et = false) const {int fd = sock.GetFd();printf("[Epoll Add] fd = %d\n", fd);epoll_event ev;ev.data.fd = fd;if (epoll_et) {ev.events = EPOLLIN | EPOLLET;}else {ev.events = EPOLLIN;}int ret = epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, fd, &ev);if (ret < 0) {perror("epoll_ctl ADD");return false;}return true;}bool Del(const TcpSocket& sock) const {int fd = sock.GetFd();printf("[Epoll Del] fd = %d\n", fd);int ret = epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, fd, NULL);if (ret < 0) {perror("epoll_ctl DEL");return false;}return true;}bool Wait(std::vector<TcpSocket>* output) const {output->clear();epoll_event events[1000];int nfds = epoll_wait(epoll_fd_, events, sizeof(events) / sizeof(events[0]), -1);if (nfds < 0) {perror("epoll_wait");return false;}// [注意!] 此处必须是循环到 nfds, 不能多循环for (int i = 0; i < nfds; ++i) {TcpSocket sock(events[i].data.fd);output->push_back(sock);}return true;}private:int epoll_fd_;
};class TcpEpollServer 
{
public:TcpEpollServer(const std::string& ip, uint16_t port) : ip_(ip),port_(port) {}bool Start(Handler handler) {// 1. 创建 socketTcpSocket listen_sock;CHECK_RET(listen_sock.Socket());// 2. 绑定CHECK_RET(listen_sock.Bind(ip_, port_));// 3. 监听CHECK_RET(listen_sock.Listen(5));// 4. 创建 Epoll 对象, 并将 listen_sock 加入进去Epoll epoll;epoll.Add(listen_sock);// 5. 进入事件循环for (;;) {// 6. 进行 epoll_waitstd::vector<TcpSocket> output;if (!epoll.Wait(&output)) {continue;}// 7. 根据就绪的文件描述符的种类决定如何处理for (size_t i = 0; i < output.size(); ++i) {if (output[i].GetFd() == listen_sock.GetFd()) {// 如果是 listen_sock, 就调用 acceptTcpSocket new_sock;listen_sock.Accept(&new_sock);epoll.Add(new_sock, true);}else {// 如果是 new_sock, 就进行一次读写std::string req, resp;bool ret = output[i].RecvNoBlock(&req);if (!ret) {// [注意!!] 需要把不用的 socket 关闭// 先后顺序别搞反. 不过在 epoll 删除的时候其实就已经关闭 socket 了epoll.Del(output[i]);output[i].Close();continue;}handler(req, &resp);output[i].SendNoBlock(resp);printf("[client %d] req: %s, resp: %s\n",output[i].GetFd(),req.c_str(), resp.c_str());} // end for} // end for (;;)}return true;}private:std::string ip_;uint16_t port_;
};

感谢各位大佬支持!!!

互三啦!!!

相关文章:

【在Linux世界中追寻伟大的One Piece】多路转接epoll(续)

目录 1 -> epoll的工作方式 1.1 -> 水平触发(Level Triggered)工作模式 1.2 -> 边缘触发(Edge Triggered)工作模式 2 -> 对比LT与ET 3 -> 理解ET模式和非阻塞文件描述符 4 -> epoll的使用场景 5 -> epoll示例 5.1 -> epoll服务器(LT模式) 5.2…...

【不写for循环】玩玩行列

利用numpy的并行操作可以比纯用Python的list快很多&#xff0c;不仅如此&#xff0c;代码往往精简得多。 So, 这篇来讲讲进阶的广播和花哨索引操作&#xff0c;少写几个for循环&#xff08;&#xff09;。 目录 一个二维的例题 一个三维的例题 解法一 解法二 更难的三维例题…...

【Nginx】反向代理Https时相关参数:

在Nginx代理后台HTTPS服务时&#xff0c;有几个关键的参数需要配置&#xff0c;以确保代理服务器能够正确地与后端服务器进行通信。一些重要参数的介绍&#xff1a; proxy_ssl_server_name&#xff1a;这个参数用于指定是否在TLS握手时通过SNI&#xff08;Server Name Indicati…...

第 17 章 - Go语言 上下文( Context )

在Go语言中&#xff0c;context包为跨API和进程边界传播截止时间、取消信号和其他请求范围值提供了一种方式。它主要应用于网络服务器和长时间运行的后台任务中&#xff0c;用于控制一组goroutine的生命周期。下面我们将详细介绍context的定义、使用场景、取消和超时机制&#…...

Android Framework AMS(16)进程管理

该系列文章总纲链接&#xff1a;专题总纲目录 Android Framework 总纲 本章关键点总结 & 说明&#xff1a; 说明&#xff1a;本章节主要解读AMS 进程方面的知识。关注思维导图中左上侧部分即可。 我们本章节主要是对Android进程管理相关知识有一个基本的了解。先来了解下L…...

STM32设计防丢防摔智能行李箱

目录 目录 前言 一、本设计主要实现哪些很“开门”功能&#xff1f; 二、电路设计原理图 1.电路图采用Altium Designer进行设计&#xff1a; 2.实物展示图片 三、程序源代码设计 四、获取资料内容 前言 随着科技的不断发展&#xff0c;嵌入式系统、物联网技术、智能设备…...

【异常解决】Linux shell报错:-bash: [: ==: 期待一元表达式 解决方法

博主介绍&#xff1a;✌全网粉丝21W&#xff0c;CSDN博客专家、Java领域优质创作者&#xff0c;掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域✌ 技术范围&#xff1a;SpringBoot、SpringCloud、Vue、SSM、HTML、Nodejs、Python、MySQL、PostgreSQL、大数据、物…...

ML 系列: 第 23 节 — 离散概率分布 (多项式分布)

目录 一、说明 二、多项式分布公式 2.1 多项式分布的解释 2.2 示例 2.3 特殊情况&#xff1a;二项分布 2.4 期望值 &#xff08;Mean&#xff09; 2.5 方差 三、总结 3.1 python示例 一、说明 伯努利分布对这样一种情况进行建模&#xff1a;随机变量可以采用两个可能的值&#…...

Webpack 1.13.2 执行 shell 命令解决 打印时没有背景色和文字颜色的问题

这是因为 Webpack 1.13.2 不支持新的插件钩子 API。Webpack 1 的插件系统使用的是 plugin 方法&#xff0c;而不是 Webpack 4 中的 hooks。 在 Webpack 1 中&#xff0c;你可以使用以下代码来确保 sed 命令在打包完成后执行&#xff1a; const { exec } require(child_proce…...

C++构造函数详解

构造函数详解&#xff1a;C 中对象初始化与构造函数的使用 在 C 中&#xff0c;构造函数是一种特殊的成员函数&#xff0c;它在创建对象时自动调用&#xff0c;用来初始化对象的状态。构造函数帮助我们确保每个对象在被创建时就处于一个有效的状态&#xff0c;并且在不传递任何…...

POI实现根据PPTX模板渲染PPT

目录 1、前言 2、了解pptx文件结构 3、POI组件 3.1、引入依赖 3.2、常见的类 3.3、实现原理 3.4、关键代码片段 3.4.1、获取ppt实例 3.4.2、获取每页幻灯片 3.4.3、循环遍历幻灯片处理 3.4.3.1、文本 3.4.3.2、饼图 3.4.3.3、柱状图 3.4.3.4、表格 3.4.3.5、本地…...

【论文模型复现】深度学习、地质流体识别、交叉学科融合?什么情况,让我们来看看

文献&#xff1a;蓝茜茜,张逸伦,康志宏.基于深度学习的复杂储层流体性质测井识别——以车排子油田某井区为例[J].科学技术与工程,2020,20(29):11923-11930. 本文目录 一、前言二、文献阅读-基于深度学习的复杂储层流体性质测井识别2.1 摘要2.2 当前研究不足2.3 本文创新2.4 论文…...

树的直径计算:算法详解与实现

树的直径计算:算法详解与实现 1. 引言2. 算法概述3. 伪代码实现4. C语言实现5. 算法分析6. 结论在图论中,树的直径是一个关键概念,它表示树中任意两点间最长路径的长度。对于给定的树T=(V,E),其中V是顶点集,E是边集,树的直径定义为所有顶点对(u,v)之间最短路径的最大值。…...

conda创建 、查看、 激活、删除 python 虚拟环境

1、创建 python 虚拟环境 ,假设该环境命名为 “name”。 conda create -n name python3.11 2、查看 python 虚拟环境。 conda info -e 3、激活使用 python 虚拟环境。 conda activate name 4、删除 python 虚拟环境 conda remove -n name --all ​​ 助力快速掌握数据集…...

vs2022搭建opencv开发环境

1 下载OpenCV库 https://opencv.org/ 下载对应版本然后进行安装 将bin目录添加到系统环境变量opencv\build\x64\vc16\bin 复制该路径 打开高级设置添加环境变量 vs2022新建一个空项目 修改属性添加头文件路径和库路径 修改链接器&#xff0c;将OpenCV中lib库里的o…...

NVIDIA NIM 开发者指南:入门

NVIDIA NIM 开发者指南&#xff1a;入门 NVIDIA 开发者计划 想要了解有关 NIM 的更多信息&#xff1f;加入 NVIDIA 开发者计划&#xff0c;即可免费访问任何基础设施云、数据中心或个人工作站上最多 16 个 GPU 上的自托管 NVIDIA NIM 和微服务。 加入免费的 NVIDIA 开发者计…...

探索Python网络请求新纪元:httpx库的崛起

文章目录 **探索Python网络请求新纪元&#xff1a;httpx库的崛起**第一部分&#xff1a;背景介绍第二部分&#xff1a;httpx库是什么&#xff1f;第三部分&#xff1a;如何安装httpx库&#xff1f;第四部分&#xff1a;简单的库函数使用方法1. 发送GET请求2. 发送POST请求3. 超…...

学了Arcgis的水文分析——捕捉倾泻点,河流提取与河网分级,3D图层转要素失败的解决方法,测量学综合实习网站存着

ArcGIS水文分析实战教程&#xff08;7&#xff09;细说流域提取_汇流域栅格-CSDN博客 ArcGIS水文分析实战教程&#xff08;6&#xff09;河流提取与河网分级_arcgis的dem河流分级-CSDN博客 ArcGIS水文分析实战教程&#xff08;5&#xff09;细说流向与流量-CSDN博客 ArcGIS …...

QQ 小程序已发布,但无法被搜索的解决方案

前言 我的 QQ 小程序在 2024 年 8 月就已经审核通过&#xff0c;上架后却一直无法被搜索到。打开后&#xff0c;再在 QQ 上下拉查看 “最近使用”&#xff0c;发现他出现一下又马上消失。 上线是按正常流程走的&#xff0c;开发、备案、审核&#xff0c;没有任何违规&#xf…...

【C++】拷贝构造 和 赋值运算符重载

目录&#xff1a; 一、拷贝构造 &#xff08;一&#xff09;拷贝函数的特点 二、赋值运算符重载 &#xff08;一&#xff09;运算符重载 &#xff08;二&#xff09;赋值运算符重载 正文 一、拷贝构造 如果一个构造函数的第一个参数是自身类类型的引用&#xff0c;且任何…...

21.UE5游戏存档,读档,函数库

2-23 游戏存档、读档、函数库_哔哩哔哩_bilibili 目录 1.存档蓝图 2.函数库 2.1保存存档 2.2读取存档&#xff1a; 3.加载游戏&#xff0c;保存游戏 3.1游戏实例对象 3.2 加载游戏 3.3保存游戏 这一节的内容较为错综复杂&#xff0c;中间没有运行程序进行阶段性成果的验…...

「Mac玩转仓颉内测版14」PTA刷题篇5 - L1-005 考试座位号

本篇将继续讲解PTA平台上的题目 L1-005 考试座位号&#xff0c;通过考生准考证号与座位号的对应关系&#xff0c;掌握简单的数据查询与映射操作&#xff0c;进一步提升Cangjie编程语言的实际应用能力。 关键词 PTA刷题数据查询映射操作输入输出Cangjie语言 一、L1-005 考试座位…...

Vue3引用高德地图,进行位置标记获取标记信息

首先安装地图插件 cnpm i amap/amap-jsapi-loader --save封装地图子组件 <template><el-dialogtitle"选择地点"width"740px"class"select-map-dialog"v-model"dialogShow":close-on-click-modal"false":modal-or…...

《C++设计模式:重塑游戏角色系统类结构的秘籍》

在游戏开发领域&#xff0c;游戏角色系统的类结构设计至关重要。一个良好的类结构可以使游戏更易于扩展、维护和优化&#xff0c;而 C中的设计模式为我们提供了强大的工具来实现这一目标。 一、理解游戏角色系统的复杂性 游戏角色系统通常具有高度的复杂性。每个角色都有自己…...

深入浅出 Go 语言:现代编程的高效选择

深入浅出 Go 语言:现代编程的高效选择 引言 Go 语言(也称 Golang)是由 Google 开发的一种现代编程语言,面向高效、简单和并发。自 2009 年问世以来,它已迅速成长为许多企业和开发者首选的语言,尤其是在后端开发、云计算和微服务领域。 本文旨在从 Go 语言的设计哲学、…...

RDIFramework.NET CS敏捷开发框架 V6.1发布(.NET6+、Framework双引擎、全网唯一)

RDIFramework.NET C/S敏捷开发框架V6.1版本迎来重大更新与调整&#xff0c;全面重新设计业务逻辑代码&#xff0c;代码量减少一半以上&#xff0c;开发更加高效。全系统引入全新字体图标&#xff0c;整个界面焕然一新。底层引入最易上手的ORM框架SqlSugar&#xff0c;让开发更加…...

vue路由的钩子函数?

在 Vue 中&#xff0c;路由的钩子函数可以用来在导航过程中执行一些操作&#xff0c;比如进行权限验证、页面加载前后的处理等。常用的路由钩子函数包括全局前置守卫、全局解析守卫、全局后置钩子以及路由独享守卫。下面是这些路由守卫函数的简要说明&#xff1a; 全局前置守卫…...

【Java】枚举类映射

在数据库中常用数字来代替字符串类型&#xff0c;编写一个枚举映射类 当数据库的介质类型要存储数字&#xff0c;前端可以任意传参&#xff0c;通过枚举转换后端都会转成数字对应类型 import lombok.Getter;/*** <p>* 存档介质类型* </p>** author Jyang* date 2…...

精华帖分享|浅谈金融时间序列分析与股价随机游走

本文来源于量化小论坛公共讨论区板块精华帖&#xff0c;作者为正扬&#xff0c;发布于2024年6月3日。 以下为精华帖正文&#xff1a; 01 引 时间序列分析是个很唬人的术语&#xff0c;实际上它也不是一个很容易接近的话题。我本科曾经短暂地学过一点点&#xff0c;又看到互联…...

任意文件下载漏洞

1.漏洞简介 任意文件下载漏洞是指攻击者能够通过操控请求参数&#xff0c;下载服务器上未经授权的文件。 攻击者可以利用该漏洞访问敏感文件&#xff0c;如配置文件、日志文件等&#xff0c;甚至可以下载包含恶意代码的文件。 这里再导入一个基础&#xff1a; 你要在网站下…...

网站公司建站/网络优化师

1、创建线程 在 Java 程序中创建线程有几种方法。每个 Java 程序至少包含一个线程&#xff1a;主线程。其它线程都是通过 Thread 构造器或实例化继续类 Thread 的类来创建的。 Java 线程可以通过直接实例化 Thread 对象或实例化继续 Thread 的对象来创建其它线程。在线程基础…...

王刚做的加盟网站名叫什么/百度收录量查询

一、背景 我们工作中常用Jenkins部署Java代码&#xff0c;因其灵活的插件特性&#xff0c;例如jdk&#xff0c;maven&#xff0c;ant等使得java项目编译后上线部署一气呵成&#xff0c;同样对于脚本语言类型如Python上线部署&#xff0c;利用Jenkins强大的插件功能&#xff0c;…...

上海seo网站策划/seo优化方案项目策划书

Devoxx 2015圆满结束。 来自比利时安特卫普的现场&#xff0c;随着最后一头散乱的人的耳朵响起&#xff0c;长长的桌子的声被藏起来&#xff0c;灯光甚至网络&#xff08;eek&#xff01;&#xff09;都被关闭&#xff0c;比利时健壮的人瞪着那些敢于四处闲逛的人&#xff0c;这…...

镇海区建设交通局网站进不去了/阿里巴巴数据分析官网

一、简介基于matlab 二值化停车场车辆计数二、源代码clc;clear allIimread(C:\Users\lenovo\Desktop\新建文件夹\2.png);Grgb2gray(I);% Ghisteq(G);Himhist(G);% bar(H);[a,b]size(G);%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%提取白车GG-100;for i1:1:afor j1:1:bif G(i,j)>2…...

长沙网站制作公司有哪些/seo学堂

【摘要】PHP作为一种超文本预处理器&#xff0c;已经成为了我们常用的网站编程语言&#xff0c;并且结合了C语言&#xff0c;Java等我们常见的编程语言&#xff0c;所以&#xff0c;有很多web开发领域的新人都看中了他的使用广泛性&#xff0c;有很多人都想了解php的内容&#…...

电子商务网站软件建设的核心是什么/关键词检索怎么弄

简易版之最短距离 Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 6526 Accepted Submission(s): 2885 Problem Description寒假的时候&#xff0c;ACBOY要去拜访很多朋友&#xff0c;恰巧他所有朋友的家都处在…...