应用层协议Http
Http协议
1.1 什么是http协议
在进行网络通信时,应用层协议一般都是程序员自己写的,但是有一些大佬其实已经定义出了一些现成的应用层协议,例如:HTTP(超文本传输协议)、FTP(文件传输协议)、SMTP(简单邮件传输协议)、POP3(邮局协议版本3)、IMAP(Internet邮件访问协议)、DNS(域名系统)、SNMP(简单网络管理协议)、DHCP(动态主机配置协议)等等
HTTP 协议是客户端与服务器之间通信的基础。它规定了客户端(通常是浏览器)和服务器之间交换数据(尤其是超文本)的格式和方式。客户端通过 HTTP 协议向服务器发送请求,服务器收到请求后处理并返回响应。HTTP 协议是一个无连接、无状态的协议,即每次请求都需要建立新的连接,且服务器不会保存客户端的状态信息
1.2 认识Url
URL(Uniform Resource Locator,统一资源定位符)是互联网上标准资源的地址(Address)。它是对可以从互联网上得到的资源的位置和访问方法的一种简洁的表示,是互联网上标准资源的地址。互联网上的每个文件都有一个唯一的URL,它包含的信息指出文件的位置以及浏览器应该怎么处理它。
- 协议:指定了应用层应该使用哪种协议来访问资源,如http、https、ftp等。
- 主机名:指定了服务器的域名或IP地址,是资源所在的服务器地址,但是ip地址对于用户来说是不合适的,这里通常是域名例如 www.baidu.com ,当我们通过域名访问服务器时,会自动将域名改为IP地址,所以我们可以认为他们是等价的
- 端口号:HTTP协议在默认情况下强绑定到TCP的80端口上。这意味着,当浏览器或其他HTTP客户端尝试访问一个Web服务器上的资源时,如果没有指定端口号,它会默认使用80端口来发送请求。服务器在监听80端口时,也会期待接收来自这个端口的HTTP请求,所以url中一般不会包含80端口号。
- 路径:指定了服务器上资源的路径。
- 查询字符串:可选部分,位于路径之后,用 ?分隔。它允许向服务器传递额外的信息,如搜索条件、表单数据等。这些信息通常以键值对的形式出现,键值对之间用 & 连接。
- 片段标识符:可选部分,位于URL的末尾,用 # 分隔。它用于指定资源内部的一个片段(如HTML页面中的一个特定部分)。浏览器会自动滚动到该片段的位置
1.3 urlencode 和 urldecode
像 / ? : 等这样的字符, 已经被 url 当做特殊意义理解了. 因此这些字符不能随意出现. 比如, 某个参数中需要带有这些特殊字符, 就必须先对特殊字符进行转义. 转义的规则如下: 将需要转码的字符转为 16 进制,然后从右到左,取 4 位(不足 4 位直接处理),每 2 位 做一位,前面加上%,编码成%XY 格式。
例如我们搜索关键词c++,此时URL中的字符++就被编码为%2b%2b了
1.4 HTTP 协议请求与响应格式
1.4.1 HTTP请求
HTTP请求包括四部分:
- 请求行:格式:<请求方法><空格><URL><空格><HTTP版本><\r\n>
- 请求报头:请求头包含了多个字段,每个字段都是一个键值对,用来描述请求或指定请求的资源、客户端的信息、响应内容应如何接收等。<key>< : 空格><value>
- 空行:用来标记请求报头结束,方便反序列化
- 请求正文:如果客户端没有像服务器传输数据的话,请求正文就是空的
注意:
在url中的 / 并不是我们平时见的根目录,他的名字叫web根目录,可以是一台机器的任意一个目录,所有的网站静态资源都保存在这个目录下,当我们访问的url为/时,一般会跳转到该网站的首页
根据http请求的格式,我们就可以将客户端的请求进行反序列化,找到其要访问的资源,在构建应答发送给它。
代码实现思路:
要注意,当我们用浏览器访问我们的机器的资源时,就是构建了一个http请求,浏览器会将请求进行序列化,再将序列化出来的字符串发送给我们,所以我们收到的其实是如上如所示的一条字符串,而我们的工作就是将这个字符串进行反序列化,然后构建应答发送给浏览器
反序列化思路:先粗分再细分
首先我们将收到的序列化字符串先分为请求行,报头、请求正文,报头的话就是多个字符串,我们先把他放在一个字符串数组中。再解析请求行详细分为方法、url、版本的等信息,报头是key-value的格式,我们可以用哈希表来存放
const static std::string base_sep = "\r\n";
const static std::string space_sep = " ";
const static std::string line_sep = ": ";
const static std::string prefixpath = "wwwroot"; // web根目录
const static std::string homepage = "index.html";
const static std::string suffixsep = ".";
const static std::string httpversion = "HTTP/1.0";
const static std::string arg_sep = "?";class HttpRequest
{
private:// 得到一行std::string GetLine(std::string &reqstr){auto pos = reqstr.find(base_sep);if (pos == std::string::npos)return std::string();std::string line = reqstr.substr(0, pos);reqstr.erase(0, line.size() + base_sep.size());return line.empty() ? base_sep : line; // 如果读取到空白行那截取的就是空串,则直接返回空白行就可以}// 解析请求行void ParseReqline(){std::stringstream ss(_req_line);ss >> _method >> _url >> _version;_path += _url;// 添加一个首页if (_path[_path.size() - 1] == '/'){_path += homepage;}auto pos = _url.rfind(suffixsep);if (pos != std::string::npos){_suffix = _path.substr(pos);}else{_suffix = ".default";}}void ParseReqHeader(){for (auto &header : _req_headers){auto pos = header.find(line_sep);if (pos == std::string::npos)continue;std::string k = header.substr(0, pos);std::string v = header.substr(pos + line_sep.size());if (k.empty() || v.empty())continue;_headers_kv.insert(std::make_pair(k, v));}}public:HttpRequest(): _blank_line(base_sep), _path(prefixpath){}~HttpRequest(){}void DeSerialize(std::string &reqstr){// 粗力度反序列_req_line = GetLine(reqstr); // 请求行std::string header; // 请求报头do{header = GetLine(reqstr);if (header.empty() || header == base_sep)break;_req_headers.push_back(header);} while (true);if (!reqstr.empty()) // 请求正文{_body_text = reqstr;}ParseReqline();ParseReqHeader();}void Print(){std::cout << "----------------------------" << std::endl;std::cout << "###" << _req_line << std::endl;for (auto &header : _req_headers){std::cout << "@@@" << header << std::endl;}std::cout << "***" << _blank_line;std::cout << ">>>" << _body_text << std::endl;std::cout << "Method: " << _method << std::endl;std::cout << "Url: " << _url << std::endl;std::cout << "Version: " << _version << std::endl;for (auto &header_kv : _headers_kv){std::cout << ")))" << header_kv.first << "->" << header_kv.second << std::endl;}}std::string Url(){LOG(DEBUG, "Client Want url %s\n", _url.c_str());return _url;}std::string Path(){LOG(DEBUG, "Client Want path %s\n", _path.c_str());return _path;}std::string Suffix(){return _suffix;}std::string Method(){LOG(DEBUG, "Client request method is %s\n", _method.c_str());return _method;}std::string GetResuestBody(){LOG(DEBUG, "Client request method is %s, args: %s, request path: %s\n",_method.c_str(), _body_text.c_str(), _path.c_str());return _body_text;}private:// 基本HttpRequwst格式std::string _req_line;std::vector<std::string> _req_headers;std::string _blank_line;std::string _body_text;// 更详细的数据std::string _method;std::string _url;std::string _version;std::string _path;std::string _suffix; // 资源后缀std::unordered_map<std::string, std::string> _headers_kv;
};
1.4.2 Http应答
Http应答与请求格式类似也分为4部分:
- 状态行:格式:<HTTP版本><空格><状态码><空格><状态码描述><\r\n>
- 相应报头:响应头部与请求头部类似,为响应报文添加了一些附加信息。<key>< : 空格><value>
- 空行
- 响应正文:响应正文是服务器返回给客户端的数据部分,包含了服务器对客户端请求的响应内容。响应正文的格式和类型由响应头部中的Content-Type字段指定。
对于相应来说我们只需要序列化,反序列化的工作是由客户端来实现的,将序列化字符串发送给客户端即可
class HttpResponse
{
public:HttpResponse() : _verison(httpversion), _blank_line(base_sep){}void AddCode(int code, const std::string &desc){_status_code = code;_desc = desc;}void AddHeader(const std::string &k, const std::string &v){_headers_kv[k] = v;}void AddBodyText(const std::string &body_text){_resp_body_text = body_text;}std::string Serialize(){// 构建状态行_status_line = _verison + space_sep + std::to_string(_status_code) + space_sep + _desc + base_sep;// 2. 构建应答报头for (auto &header : _headers_kv){std::string header_line = header.first + line_sep + header.second + base_sep;_resp_headers.push_back(header_line);}// 空行在构造函数实现了// 正式序列化std::string responsestr = _status_line;for (auto &line : _resp_headers){responsestr += line;}responsestr += _blank_line;responsestr += _resp_body_text;return responsestr;}private:// httpresponse base 属性std::string _verison;int _status_code;std::string _desc;std::unordered_map<std::string, std::string> _headers_kv;// 基本的httpResponse的格式std::string _status_line;std::vector<std::string> _resp_headers;std::string _blank_line;std::string _resp_body_text;
};
1.5 HTTP 的方法
方法 | 说明 | 支持的HTTP协议版本 |
GET | 获取资源 | 1.0 1.1 |
POST | 传输实体 | 1.0 1.1 |
HEAD | 获得报文首部 | 1.0 1.1 |
DELETE | 删除文件 | 1.0 1.1 |
OPTIONS | 询问支持方法 | 1.0 |
TRACE | 追踪路径 | 1.0 |
PUT | 传输文件 | 1.0 1.1 |
CONNECT | 要求用隧道协议连接代理 | 1.0 |
LINK | 建立和资源之间的联系 | 1.0 |
UNLINK | 断开连接关系 | 1.0 |
在这些方法中使用最多的是GET方法和POST方法,其中GET方法使用的是最多的。
GET
用于请求 URL 指定的资源,POST用于传输实体的主体,通常用于提交表单数据。
两者传输数据的方式不同,GET方法通过url传参,而POST方法则是将参数放到了正文中
1.6 HTTP 的状态码
状态码按照首位数字的不同,可以分为五类:
类别 | 原因短语 | |
1xx | Informational(信息性状态码) | 接收的请求正在处理 |
2xx | Success(成功状态码) | 请求正常处理完毕 |
3xx | Redirection(重定向状态码) | 需要进行附加操作以完成请求 |
4xx | Client Error(客户端错误状态码) | 服务器无法处理请求 |
5xx | Server Error(服务器错误状态码) | 服务器处理请求出错 |
最常见的状态码, 比如 200(OK), 404(Not Found), 403(Forbidden), 302(Redirect, 重定向), 504(Bad Gateway)
重定向状态码
- 301永久重定向
当服务器返回 HTTP 301 状态码时,表示请求的资源已经被永久移动到新的位置。 在这种情况下,服务器会在响应中添加一个 Location 头部,用于指定资源的新位 置。这个 Location 头部包含了新的 URL 地址,浏览器会自动重定向到该地址。
例如,在 HTTP 响应中,可能会看到类似于以下的头部信息:
C++
HTTP/1.1 301 Moved Permanently\r\n
Location: https://www.new-url.com\r\n
一般用于网站换域名后,自动跳转到新域名, 搜索引擎更新网站链接时使用
- 302临时重定向
当服务器返回 HTTP 302 状态码时,表示请求的资源临时被移动到新的位置。 • 同样地,服务器也会在响应中添加一个 Location 头部来指定资源的新位置。浏览 器会暂时使用新的 URL 进行后续的请求,但不会缓存这个重定向。
例如,在 HTTP 响应中,可能会看到类似于以下的头部信息:
C++
HTTP/1.1 302 Found\r\n
Location: https://www.new-url.com\r\n
一般用于当用户登录成功后跳转到另一个页面
无论是 HTTP 301 还是 HTTP 302 重定向,都需要依赖 Location 选项来指定资 源的新位置。这个 Location 选项是一个标准的 HTTP 响应头部,用于告诉浏览器应该 将请求重定向到哪个新的 URL 地址。
1.7 HTTP 常见 Header
- Content-Type: 数据类型(text/html 等)
- Content-Length: Body 的长度
- Host: 客户端告知服务器, 所请求的资源是在哪个主机的哪个端口上;
- User-Agent: 声明用户的操作系统和浏览器版本信息;
- referer: 当前页面是从哪个页面跳转过来的;
- Location: 搭配 3xx 状态码使用, 告诉客户端接下来要去哪里访问;
- Cookie: 用于在客户端存储少量信息. 通常用于实现会话(session)的功能;
关于 connection 报头
HTTP 中的 Connection 字段是 HTTP 报文头的一部分,它主要用于控制和管理客户 端与服务器之间的连接状态
核心作用
• 管理持久连接:Connection 字段还用于管理持久连接(也称为长连接)。持久连接允许客户端和服务器在请求/响应完成后不立即关闭 TCP 连接,以便在同一个连接 上发送多个请求和接收多个响应。 持久连接(长连接)
• HTTP/1.1:在 HTTP/1.1 协议中,默认使用持久连接。当客户端和服务器都不明 确指定关闭连接时,连接将保持打开状态,以便后续的请求和响应可以复用同一个连 接。
• HTTP/1.0:在 HTTP/1.0 协议中,默认连接是非持久的。如果希望在 HTTP/1.0 上实现持久连接,需要在请求头中显式设置 Connection: keep-alive。 语法格式
• Connection: keep-alive:表示希望保持连接以复用 TCP 连接。
• Connection: close:表示请求/响应完成后,应该关闭 TCP 连接。
class HttpServer
{
private:std::string GetFile(std::string path){std::ifstream in(path, std::ios::binary);if (!in.is_open())return std::string();in.seekg(0, in.end);int filesize = in.tellg(); // 计算rw偏移量in.seekg(0, in.beg);std::string content;content.resize(filesize);in.read((char *)content.c_str(), filesize);in.close();return content;}public:HttpServer(){_mine_type.insert(std::make_pair(".html", "text/html"));_mine_type.insert(std::make_pair(".jpg", "image/jpeg"));_mine_type.insert(std::make_pair(".png", "image/png"));_mine_type.insert(std::make_pair(".default", "text/html"));_code_to_desc.insert(std::make_pair(100, "Continue"));_code_to_desc.insert(std::make_pair(200, "OK"));_code_to_desc.insert(std::make_pair(201, "Created"));_code_to_desc.insert(std::make_pair(301, "Moved Permanently"));_code_to_desc.insert(std::make_pair(302, "Found"));_code_to_desc.insert(std::make_pair(404, "Not Found"));}std::string HandlerHttpRequest(std::string &reqstr){std::cout << "---------------------------------------" << std::endl;std::cout << reqstr;std::cout << "---------------------------------------" << std::endl;HttpRequest req;req.DeSerialize(reqstr);HttpResponse resp;if (req.Path() == "wwwroot/redir"){// 处理重定向std::string redir_path = "https://www.qq.com";// resp.AddCode(302, _code_to_desc[302]);resp.AddCode(301, _code_to_desc[301]);resp.AddHeader("Location", redir_path);}else{std::string content = GetFile(req.Path()); // 读取目标文件内容if (content.empty()) // 文件不存在{content = GetFile("wwwroot/404.html");resp.AddCode(404, _code_to_desc[404]);resp.AddHeader("Content-Length", std::to_string(content.size()));resp.AddHeader("Content-Type", _mine_type[".html"]);resp.AddBodyText(content);}else{resp.AddCode(200, _code_to_desc[200]);resp.AddHeader("Content-Length", std::to_string(content.size()));resp.AddHeader("Content-Type", _mine_type[req.Suffix()]);resp.AddBodyText(content);}}std::string respstr = resp.Serialize();return respstr;}
private:std::unordered_map<std::string, std::string> _mine_type;std::unordered_map<int, std::string> _code_to_desc;
};
1.8 通过url调取服务
客户端不仅可以访问我们机器上的静态资源,也可以调取一些服务,例如当我们在百度搜索hello
https://cn.bing.com/search?q=hello&qs=n&form=QBRE&sp=-1&lq=0&pq=hello&sc=10-5&sk=&cvid=3B4149A5C11047A4BD8B5A2487B2D895&ghsh=0&ghacc=0&ghpl=
我们知道cn.bing.com时百度的域名,后面紧跟的/search,是我们要访问的路径,?后面是传递的参数,域名后面的/path既可以是一种资源也可以是一种服务,这里的/search明显是利用后面的参数区调用search服务了
我们实现的时候,可以使用哈希表,利用域名后面的路径来索引我们要执行的服务
using func_t=std::function<HttpResponse(HttpRequest&)>;
class HttpServer
{
private:std::string GetFile(std::string path){std::ifstream in(path, std::ios::binary);if (!in.is_open())return std::string();in.seekg(0, in.end);int filesize = in.tellg(); // 计算rw偏移量in.seekg(0, in.beg);std::string content;content.resize(filesize);in.read((char *)content.c_str(), filesize);in.close();return content;}public:HttpServer(){_mine_type.insert(std::make_pair(".html", "text/html"));_mine_type.insert(std::make_pair(".jpg", "image/jpeg"));_mine_type.insert(std::make_pair(".png", "image/png"));_mine_type.insert(std::make_pair(".default", "text/html"));_code_to_desc.insert(std::make_pair(100, "Continue"));_code_to_desc.insert(std::make_pair(200, "OK"));_code_to_desc.insert(std::make_pair(201, "Created"));_code_to_desc.insert(std::make_pair(301, "Moved Permanently"));_code_to_desc.insert(std::make_pair(302, "Found"));_code_to_desc.insert(std::make_pair(404, "Not Found"));}std::string HandlerHttpRequest(std::string &reqstr){
#ifdef TESTstd::cout << "---------------------------------------" << std::endl;std::cout << reqstr;std::string responsestr = "HTTP/1.1 200 OK\r\n";responsestr += "Content-Type: text/html\r\n";responsestr += "\r\n";responsestr += "<html><h1>hello Linux, hello bite!</h1></html>";return responsestr;
#elsestd::cout << "---------------------------------------" << std::endl;std::cout << reqstr;std::cout << "---------------------------------------" << std::endl;HttpRequest req;req.DeSerialize(reqstr);HttpResponse resp;if (req.Path() == "wwwroot/redir"){// 处理重定向std::string redir_path = "https://www.qq.com";// resp.AddCode(302, _code_to_desc[302]);resp.AddCode(301, _code_to_desc[301]);resp.AddHeader("Location", redir_path);}else if(!req.GetResuestBody().empty()){if(IsServiceExists(req.Path())){_service_list[req.Path()](req);}}else{std::string content = GetFile(req.Path()); // 读取目标文件内容if (content.empty()) // 文件不存在{content = GetFile("wwwroot/404.html");resp.AddCode(404, _code_to_desc[404]);resp.AddHeader("Content-Length", std::to_string(content.size()));resp.AddHeader("Content-Type", _mine_type[".html"]);resp.AddBodyText(content);}else{resp.AddCode(200, _code_to_desc[200]);resp.AddHeader("Content-Length", std::to_string(content.size()));resp.AddHeader("Content-Type", _mine_type[req.Suffix()]);resp.AddBodyText(content);}}std::string respstr = resp.Serialize();return respstr;
#endif}void InsertService(const std::string& servicename,func_t func){std::string s = prefixpath + servicename;_service_list[s]=func;}bool IsServiceExists(const std::string &servicename){auto iter = _service_list.find(servicename);if(iter == _service_list.end()) return false;else return true;}
private:std::unordered_map<std::string, std::string> _mine_type;std::unordered_map<int, std::string> _code_to_desc;std::unordered_map<std::string, func_t> _service_list;
};
#include"TcpServer.hpp"
#include"http.hpp"HttpResponse Login(HttpRequest &req)
{HttpResponse resp;std::cout << "外部已经拿到了参数了: "<< std::endl;req.GetResuestBody();std::cout << "####################### "<< std::endl;resp.AddCode(200, "OK");resp.AddBodyText("<html><h1>result done!</h1></html>");// username=helloworld&userpasswd=123456// 1. pipe// 2. dup2// 3. fork();// 4. exec* -> python, PHP, 甚至是Java!return resp;
}
int main(int argc,char* argv[])
{if(argc<2){std::cerr<<"Usage:"<<argv[0]<<"Server port"<<std::endl;exit(0);}uint16_t port=std::stoi(argv[1]);HttpServer http;http.InsertService("/login",Login);std::unique_ptr<TcpServer> tsvr=std::make_unique<TcpServer>(std::bind(&HttpServer::HandlerHttpRequest,&http,std::placeholders::_1),port);tsvr->Run();return 0;
}
相关文章:

应用层协议Http
Http协议 1.1 什么是http协议 在进行网络通信时,应用层协议一般都是程序员自己写的,但是有一些大佬其实已经定义出了一些现成的应用层协议,例如:HTTP(超文本传输协议)、FTP(文件传输协议&#…...
display flex 的div 被子元素撑开不显示滚动条的一个解决demo
display flex 的div 被子元素撑开,不显示y轴滚动条的 一个解决demo。 注: 不一定适用所有人的的相同问题 less # less .contact {display: flex;flex-grow: 1;overflow: hidden auto;flex-direction: column;.contact-items {flex: 1 1 0;display: flex…...

判断键盘输入是数字、大写字母还是小写字母——C#学习笔记
以下代码将判断键盘输入字符是数字 还是字母: using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks;namespace ConsoleApp4 {using System;using System.Threading;public class BoolKeyTest{sta…...
进程控制块PCB的组织方式有哪些?
进程控制块(PCB,Process Control Block)是操作系统用来管理和跟踪进程的一个数据结构,它保存了与进程相关的各种信息。PCB 是操作系统调度进程的核心数据结构,通常通过某种组织方式进行管理。常见的 PCB 组织方式主要有…...
getent passwd 获取linux并显示用户账户信息
getent passwd 命令在Unix和类Unix系统(如Linux)中用于从系统的密码数据库(通常是/etc/passwd文件,但在某些配置中可能是通过网络服务如NIS或LDAP)中获取并显示用户账户信息。这个命令的输出列出了系统上所有用户的详细…...
达梦数据库+JPA+Springboot 报错 :无效的列名
文章目录 0、 开发环境1、需求说明2、适配过程3、问题描述和解决3.1报错问题3.2 问题解决 0、 开发环境 开发环境: idea 2022 SpringBoot版本 :2.7.1 JDK 111、需求说明 适配国产化数据库达梦8 2、适配过程 参考 Springboot jpa 达梦 3、问题描述…...

#单片机基础 笔记一
--单片机概述STM32开发环境搭建创建工程 --STM32-GPIO(重点)编程 --STM32-USART串口应用 --STM32-SPI(液晶屏)中断系统 --STM32-时钟系统(重点中的重点) --STM32-PWMADC --STM32-DMA DHT11 1. 计算机组成原理 1.1 计算机的组成 1…...

echarts多个环形图
echarts图表集 var dataValue [{name:今日待分配方量,value:49}, {name:今日已分配方量,value:602}, {name:今日完成方量,value:1037}]var piedata1 [{name: 1#拌和机,value: 20},{name: 2#拌和机,value: 22},{name: 3#拌和机 ,value: 17},{name: 4#拌和机,value: 18},{name…...
vue 的面试题
一、Html篇 1、常用的块级元素及行内元素有哪些? 块级元素:div、p、h1~h6、ol、ul、li、table、form 行内标签:a、span、img、input、lable、button 行内块元素:img、input、button 2、行内元素和块级元素的区别? 块级…...
MongoDB-部署PSA(一主一从一仲裁)架构复制集群
目录 环境信息环境准备mongoDB配置&部署复制集群搭建 环境信息 IP端口节点10.0.0.127017主10.0.0.227017从10.0.0.327017仲裁 环境准备 1.关闭THP Transparent Huge Pages 简称 THP。透明大页面(THP)是一种Linux内存管理系统,通过使用…...

CSS中 特殊类型的选择器 伪元素如何使用
一、什么是伪元素 在 CSS 中,伪元素是一种特殊类型的选择器,它允许你为元素的特定部分添加样式,而这些部分在 HTML 文档中并不实际存在。伪元素通常用于创建装饰性效果,如添加边框、背景、阴影等,而不需要额外的 HTML…...

科技信贷业务怎么寻找客户?
在科技信贷业务领域,寻找客户的痛点主要集中在以下几个方面: 1.风险评估难题:科技型企业尤其是初创企业,往往缺乏足够的历史数据和抵押物,这使得金融机构在评估其信用风险时面临较大挑战。由于科技企业的研发周期长、…...

VM中创建CentOS 7及VM中如何修改DHCP的IP网段
一、创建虚拟机 1新建Centos虚拟机 2类型选择 3版本兼容性选择 4镜像选择 5安装系统选择 6虚拟机的创建路径(选择C盘以外且不包含中文名称的路径) 7硬件配置选择 网络类型的选择(通常情况下选择NAT模式) 8剩下的全部按推荐走&…...

mybatis#{}与${}的区别,mybatis关联查询,mybatis动态sql
1.#{}与${}的区别 #{}是占位符,是采用预编译的方式向sql中传值,可以防止sql注入如果我们往sql中传值,可以使用这个,例如这个delete语句 ${}将内容直接拼接到sql语句中,一般不用于传值,可以当作列名&#x…...
K8S(Kubernates) 知识目录
1. 一文了解K8S(Kubernates) 2. K8S CNI CRI CSI 3. K8S Helm 4. K8S Harbor 5. K8S Operator 6. K8S 服务网格 7. K8S 存储 8. K8S 容器调度 9. K8S 探针 10. K8S Watch 11. K8S 版本发版 12. K8S 资源管理CPU&内存 13. K8S Flannel 1…...

基于Java+SpringBoot+Vue+MySQL的失物招领管理系统
作者:计算机学姐 开发技术:SpringBoot、SSM、Vue、MySQL、JSP、ElementUI等,“文末源码”。 专栏推荐:前后端分离项目源码、SpringBoot项目源码、SSM项目源码 系统展示 基于SpringBootVue的失物招领管理系统【附源码文档】、前后…...

Mybatis链路分析:JDK动态代理和责任链模式的应用
背景 此前写过关于代理模式的文章,参考:代理模式 动态代理功能:生成一个Proxy代理类,Proxy代理类实现了业务接口,而通过调用Proxy代理类实现的业务接口,实际上会触发代理类的invoke增强处理方法。 责任链功…...
【Spring Boot 3】【Web】解析获取HTTP请求参数
【Spring Boot 3】【Web】解析获取HTTP请求参数 背景介绍开发环境开发步骤及源码工程目录结构背景 软件开发是一门实践性科学,对大多数人来说,学习一种新技术不是一开始就去深究其原理,而是先从做出一个可工作的DEMO入手。但在我个人学习和工作经历中,每次学习新技术总是要…...

conda换源是什么?
换源对于我们在国内的python使用者来说是非常有必要的,之前讲了pip如何换源。 pip更换为国内镜像源的步骤,为什么要更换镜像源 那现在讲一下conda如何换源。 conda换源(清华源) 有时候,conda虽然和pip共用一个本地…...
英文缩写大全(IT 领域和电子行业制造领域)
英文缩写大全(IT 领域和电子行业制造领域) 前言一、计算机通用二、WINDOWS三、LINUX四、编程语言1. 前端 / 设计2. JAVA / Android3. PHP4. Python 四、电子行业制造领域五、常识 前言 本文收集了各类英文缩写大全,方便查阅,主要…...

深度学习在微纳光子学中的应用
深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向: 逆向设计 通过神经网络快速预测微纳结构的光学响应,替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…...

微信小程序之bind和catch
这两个呢,都是绑定事件用的,具体使用有些小区别。 官方文档: 事件冒泡处理不同 bind:绑定的事件会向上冒泡,即触发当前组件的事件后,还会继续触发父组件的相同事件。例如,有一个子视图绑定了b…...

微软PowerBI考试 PL300-选择 Power BI 模型框架【附练习数据】
微软PowerBI考试 PL300-选择 Power BI 模型框架 20 多年来,Microsoft 持续对企业商业智能 (BI) 进行大量投资。 Azure Analysis Services (AAS) 和 SQL Server Analysis Services (SSAS) 基于无数企业使用的成熟的 BI 数据建模技术。 同样的技术也是 Power BI 数据…...
线程与协程
1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指:像函数调用/返回一样轻量地完成任务切换。 举例说明: 当你在程序中写一个函数调用: funcA() 然后 funcA 执行完后返回&…...
django filter 统计数量 按属性去重
在Django中,如果你想要根据某个属性对查询集进行去重并统计数量,你可以使用values()方法配合annotate()方法来实现。这里有两种常见的方法来完成这个需求: 方法1:使用annotate()和Count 假设你有一个模型Item,并且你想…...
Python 包管理器 uv 介绍
Python 包管理器 uv 全面介绍 uv 是由 Astral(热门工具 Ruff 的开发者)推出的下一代高性能 Python 包管理器和构建工具,用 Rust 编写。它旨在解决传统工具(如 pip、virtualenv、pip-tools)的性能瓶颈,同时…...

Qemu arm操作系统开发环境
使用qemu虚拟arm硬件比较合适。 步骤如下: 安装qemu apt install qemu-system安装aarch64-none-elf-gcc 需要手动下载,下载地址:https://developer.arm.com/-/media/Files/downloads/gnu/13.2.rel1/binrel/arm-gnu-toolchain-13.2.rel1-x…...
Spring Security 认证流程——补充
一、认证流程概述 Spring Security 的认证流程基于 过滤器链(Filter Chain),核心组件包括 UsernamePasswordAuthenticationFilter、AuthenticationManager、UserDetailsService 等。整个流程可分为以下步骤: 用户提交登录请求拦…...

《Docker》架构
文章目录 架构模式单机架构应用数据分离架构应用服务器集群架构读写分离/主从分离架构冷热分离架构垂直分库架构微服务架构容器编排架构什么是容器,docker,镜像,k8s 架构模式 单机架构 单机架构其实就是应用服务器和单机服务器都部署在同一…...
微服务通信安全:深入解析mTLS的原理与实践
🔥「炎码工坊」技术弹药已装填! 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、引言:微服务时代的通信安全挑战 随着云原生和微服务架构的普及,服务间的通信安全成为系统设计的核心议题。传统的单体架构中&…...