Linux网络-------3.应⽤层协议HTTP

1.HTTP协议

虽然我们说,应⽤层协议是我们程序猿⾃⼰定的.但实际上,已经有⼤佬们定义了⼀些现成的,⼜⾮常好⽤的应⽤层协议,供我们直接参考使⽤.HTTP(超⽂本传输协议)就是其中之⼀。

在互联⽹世界中,HTTP(HyperText Transfer Protocol,超⽂本传输协议) 是⼀个⾄关重要的协议。

它定义了客⼾端(如浏览器)与服务器之间如何通信,以交换或传输超⽂本(如HTML⽂档)。HTTP协议是客⼾端与服务器之间通信的基础。客⼾端通过HTTP协议向服务器发送请求,服务器收到请求后处理并返回响应。HTTP协议是⼀个⽆连接、⽆状态的协议,即每次请求都需要建⽴新的连接,且服务器不会保存客⼾端的状态信息。

在这里插入图片描述

3.HTTP回应—Response

在这里插入图片描述

4.HTTP request----------------客户端如何打开想要访问的资源

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 左边就是http协议规定的传输的数据类型,右边则是各个主机中存储的数据
  • 这里只用请求做说明-------说白了就是,左边到右边就是反序列化,右边到左边就是序列化!!!!!!!!!!

5.HTTP状态码

在这里插入图片描述

  • 从客户端读取之后,需要设置状态码!!!!!!!

在这里插入图片描述

  • 404就是典型的客户端错误码,指客户访问了服务器端没有存储的网页,会显示这个错误

以淘宝·网页举例,淘宝的服务端没有存储a.html 所以会显示无法访问!!!!!
在这里插入图片描述

6.HTTP常⻅⽅法

在这里插入图片描述

1.GET方法

⽤途:⽤于请求URL指定的资源。
⽰例: GET /index.html HTTP/1.1
特性:指定资源经服务器端解析后返回响应内容。

GET方法详解

2.POST⽅法

⽤途:⽤于传输实体的主体,通常⽤于提交表单数据。
⽰例: POST /submit.cgi HTTP/1.1
特性:可以发送⼤量的数据给服务器,并且数据包含在请求体中。

使用方法

二者对比------以login.html为例

在这里插入图片描述

那如果把post改为get呢?

在这里插入图片描述

  • 使用get的话,服务端就能拿到登录的数据了,联系数据库,就能做客户注册了!!!!!!!!!!!
  • 使用?做分割符!!!!!!!!

在这里插入图片描述

7.代码全览

先看一下格式:

在这里插入图片描述
Http.hpp:

// 防止头文件被重复包含
#pragma once// 包含必要的头文件
#include "Socket.hpp"       // Socket相关功能
#include "TcpServer.hpp"    // TCP服务器实现
#include "Util.hpp"         // 工具函数
#include "Log.hpp"          // 日志模块
#include <iostream>         // 标准输入输出
#include <string>           // 字符串处理
#include <memory>           // 智能指针
#include <sstream>          // 字符串流
#include <functional>       // 函数对象
#include <vector>           // 动态数组
#include <unordered_map>    // 哈希表// 使用命名空间
using namespace SocketModule;  // Socket模块命名空间
using namespace LogModule;     // 日志模块命名空间// 定义常量字符串
const std::string gspace = " ";        // 空格
const std::string glinespace = "\r\n"; // HTTP换行符
const std::string glinesep = ": ";     // 头部字段分隔符// 定义Web根目录和默认页面
const std::string webroot = "./wwwroot";   // 网站根目录
const std::string homepage = "index.html"; // 默认首页
const std::string page_404 = "/404.html";  // 404页面路径// HTTP请求类
class HttpRequest
{
public:// 构造函数,初始化交互标志为falseHttpRequest() : _is_interact(false){}// 序列化方法(暂未实现)std::string Serialize(){return std::string();}// 解析请求行(如 GET / HTTP/1.1)void ParseReqLine(std::string &reqline){// 使用字符串流分割请求行std::stringstream ss(reqline);ss >> _method >> _uri >> _version; // 分别提取方法、URI和版本--------以空格为分隔符--------这里method是GET,uri是/--但会被自动翻译为/下的第一个.html文件,verson则是HTTP/1.1}// 反序列化HTTP请求bool Deserialize(std::string &reqstr){// 1. 提取请求行std::string reqline;bool res = Util::ReadOneLine(reqstr, &reqline, glinespace);//把第一行读入reqline,glinespace是\r\n-----作为一句的尾部LOG(LogLevel::DEBUG) << reqline;  // 记录请求行日志// 2. 解析请求行ParseReqLine(reqline);// 处理URIif (_uri == "/")_uri = webroot + _uri + homepage; // 默认首页路径else_uri = webroot + _uri; // 其他资源路径// 记录解析结果日志LOG(LogLevel::DEBUG) << "_method: " << _method;LOG(LogLevel::DEBUG) << "_uri: " << _uri;LOG(LogLevel::DEBUG) << "_version: " << _version;// 检查URI中是否包含参数const std::string temp = "?";auto pos = _uri.find(temp);if (pos == std::string::npos){return true;  // 无参数直接返回//----------访问.html,png等静态内容时就会直接返回!!!!!!!!!}// 分离参数和URI_args = _uri.substr(pos + temp.size()); // 提取参数部分_uri = _uri.substr(0, pos);            // 提取纯URI部分_is_interact = true;                   // 标记为交互请求return true;}// 获取URIstd::string Uri(){return _uri;}// 检查是否为交互请求bool isInteract(){ return _is_interact;}// 获取参数std::string Args(){return _args;}// 析构函数~HttpRequest(){}private:std::string _method;    // HTTP方法(GET/POST等)std::string _uri;       // 请求资源路径std::string _version;   // HTTP版本std::unordered_map<std::string, std::string> _headers; // 请求头std::string _blankline; // 空行std::string _text;      // 请求体std::string _args;      // 请求参数bool _is_interact;      // 是否为交互请求标志
};// HTTP响应类
class HttpResponse
{
public:// 构造函数,初始化空行和HTTP版本HttpResponse() : _blankline(glinespace), _version("HTTP/1.0"){}// 序列化HTTP响应std::string Serialize(){// 构建状态行std::string status_line = _version + gspace + std::to_string(_code) + gspace + _desc + glinespace;// 构建响应头std::string resp_header;for (auto &header : _headers){std::string line = header.first + glinesep + header.second + glinespace;resp_header += line;}// 组合状态行、响应头、空行和响应体return status_line + resp_header + _blankline + _text;}// 设置目标文件void SetTargetFile(const std::string &target){_targetfile = target;}// 设置状态码和描述void SetCode(int code){_code = code;switch (_code){case 200:_desc = "OK";break;case 404:_desc = "Not Found";break;case 301:_desc = "Moved Permanently";break;case 302:_desc = "See Other";break;default:break;}}// 添加响应头void SetHeader(const std::string &key, const std::string &value){auto iter = _headers.find(key);if (iter != _headers.end())return;_headers.insert(std::make_pair(key, value));}// 根据文件后缀确定Content-Type!!!!!!!!//如果要访问的网页中还有其他资源如图片,音频。。。。。。就需要设置content-type-------可查找mine表!!!!std::string Uri2Suffix(const std::string &targetfile){// 查找最后一个点号auto pos = targetfile.rfind(".");if (pos == std::string::npos){return "text/html";  // 默认返回HTML类型}std::string suffix = targetfile.substr(pos);if (suffix == ".html" || suffix == ".htm")return "text/html";else if (suffix == ".jpg")return "image/jpeg";else if (suffix == "png")return "image/png";elsereturn "";}// 构建HTTP响应bool MakeResponse(){// 忽略favicon.ico请求if (_targetfile == "./wwwroot/favicon.ico"){LOG(LogLevel::DEBUG) << "用户请求: " << _targetfile << "忽略它";return false;}// 处理重定向测试if (_targetfile == "./wwwroot/redir_test"){SetCode(301);SetHeader("Location", "https://www.qq.com/");return true;}// 读取文件内容int filesize = 0;bool res = Util::ReadFileContent(_targetfile, &_text);if (!res)  // 文件不存在{_text = "";LOG(LogLevel::WARNING) << "client want get : " << _targetfile << " but not found";SetCode(404);  // 设置404状态码------客户端访问了不存在的网页!!!!!!!_targetfile = webroot + page_404;  // filetarget指向404页面!!!!!!!!filesize = Util::FileSize(_targetfile);Util::ReadFileContent(_targetfile, &_text);  // 读取404页面内容std::string suffix = Uri2Suffix(_targetfile);SetHeader("Content-Type", suffix);  // 设置Content-Type---------------注意这个一定要有,不然没办法链接到网页SetHeader("Content-Length", std::to_string(filesize));  // 设置内容长度}else  // 文件存在{LOG(LogLevel::DEBUG) << "读取文件: " << _targetfile;SetCode(200);  // 设置200状态码filesize = Util::FileSize(_targetfile);std::string suffix = Uri2Suffix(_targetfile);SetHeader("Conent-Type", suffix);//  设置Content-Type---------------注意这个内容类型一定要有,不然没办法链接到网页SetHeader("Content-Length", std::to_string(filesize));SetHeader("Set-Cookie", "username=zhangsan;");  // 设置Cookie}return true;}// 设置响应体文本void SetText(const std::string &t){_text = t;}// 反序列化方法(暂未实现)bool Deserialize(std::string &reqstr){return true;}// 析构函数~HttpResponse() {}// 公有成员变量
public:std::string _version;  // HTTP版本int _code;             // 状态码std::string _desc;     // 状态描述std::unordered_map<std::string, std::string> _headers; // 响应头std::vector<std::string> cookie;  // Cookie集合std::string _blankline;  // 空行std::string _text;       // 响应体std::string _targetfile; // 目标文件路径
};// 定义HTTP处理函数类型
using http_func_t = std::function<void(HttpRequest &req, HttpResponse &resp)>;// HTTP服务器类
class Http
{
public:// 构造函数,初始化TCP服务器Http(uint16_t port) : tsvrp(std::make_unique<TcpServer>(port))//《2》进一步使用port初始化TcpServer类并返回其指针---------->tcpseerver.hpp{}// 处理HTTP请求void HandlerHttpRquest(std::shared_ptr<Socket> &sock, InetAddr &client)//-----------<13>这个才是业务函数!{// 接收HTTP请求std::string httpreqstr;int n = sock->Recv(&httpreqstr);  // 接收请求数据--------<14>把客户端发来的内容(需要反序列化)存入httpreqstrif (n > 0)  // 接收成功{std::cout << "##########################" << std::endl;std::cout << httpreqstr;  // 打印原始请求std::cout << "##########################" << std::endl;// 解析请求HttpRequest req;HttpResponse resp;req.Deserialize(httpreqstr);// 处理交互请求if (req.isInteract()){// 检查路由是否存在------if (_route.find(req.Uri()) == _route.end())//查看uri是否存在于<6>中建立的_route{// 可添加重定向逻辑}else//如果存在------本文中,如果你在网页中点击login界面时会走这一条路!!!!!!!{// 调用注册的处理函数_route[req.Uri()](req, resp);//-----------------------------<15>调用<6>传递的键值对的函数--即业务函数----见main.cc的Login(HttpRequest &req, HttpResponse &resp)函数std::string response_str = resp.Serialize();//序列化,准备返回给服务器sock->Send(response_str);  // 发送响应-----------------------<16>最后一步,返回给服务器!!!!!!}}else  // 处理静态资源请求-----例如.html/.png文件{resp.SetTargetFile(req.Uri());if (resp.MakeResponse())  // 构建响应成功{std::string response_str = resp.Serialize();sock->Send(response_str);  // 发送响应}}}// 调试模式下的处理
#ifdef DEBUGstd::string httpreqstr;sock->Recv(&httpreqstr);std::cout << httpreqstr;// 构建简单响应HttpResponse resp;resp._version = "HTTP/1.1";resp._code = 200;resp._desc = "OK";std::string filename = webroot + homepage;bool res = Util::ReadFileContent(filename, &(resp._text));(void)res;std::string response_str = resp.Serialize();sock->Send(response_str);
#endif}// 启动HTTP服务器void Start()//---------------------<8>调用TcpServer的start,并传递参数是提供服务时用的fd建立的sock类,和客户传递过来的主机地址--------->tcpserver.hpp{tsvrp->Start([this](std::shared_ptr<Socket> &sock, InetAddr &client){ this->HandlerHttpRquest(sock, client); });//业务函数在这呢!!!!!!}// 注册服务路由void RegisterService(const std::string name, http_func_t h)//<7>注册服务路由{std::string key = webroot + name; // 构建完整路径auto iter = _route.find(key);if (iter == _route.end())  // 防止重复注册{_route.insert(std::make_pair(key, h));}}// 析构函数~Http(){}private:std::unique_ptr<TcpServer> tsvrp;  // TCP服务器实例std::unordered_map<std::string, http_func_t> _route;  // 路由表
};

Main.cc:

#include"Http.hpp"void Login(HttpRequest &req, HttpResponse &resp)
{// req.Args();LOG(LogLevel::DEBUG) << req.Args() << ", 我们成功进入到了处理数据的逻辑";std::string text = "hello: " + req.Args(); // username=zhangsan&passwd=123456// 登录认证resp.SetCode(200);resp.SetHeader("Content-Type","text/plain");resp.SetHeader("Content-Length", std::to_string(text.size()));resp.SetText(text);
}
// void Register(HttpRequest &req, HttpResponse &resp)
// {
//     LOG(LogLevel::DEBUG) << req.Args() << ", 我们成功进入到了处理数据的逻辑";
//     std::string text = "hello: " + req.Args();//     resp.SetCode(200);
//     resp.SetHeader("Content-Type","text/plain");
//     resp.SetHeader("Content-Length", std::to_string(text.size()));
//     resp.SetText(text);
// }
// void VipCheck(HttpRequest &req, HttpResponse &resp)
// {
//     LOG(LogLevel::DEBUG) << req.Args() << ", 我们成功进入到了处理数据的逻辑";
//     std::string text = "hello: " + req.Args();
//     resp.SetCode(200);
//     resp.SetHeader("Content-Type","text/plain");
//     resp.SetHeader("Content-Length", std::to_string(text.size()));
//     resp.SetText(text);
// }// void Search(HttpRequest &req, HttpResponse &resp)
// {// }// http port
int main(int argc, char *argv[])
{if(argc != 2){std::cout << "Usage: " << argv[0] << " port" << std::endl;exit(USAGE_ERR);}uint16_t port = std::stoi(argv[1]);std::unique_ptr<Http> httpsvr = std::make_unique<Http>(port);//《1》初始化一个HTTP类,并返回其指针---------》http.hpphttpsvr->RegisterService("/login", Login); // <6> 注册服务路由-------->http.hpp,再回到main.cc// httpsvr->RegisterService("/register", Register);// httpsvr->RegisterService("/vip_check", VipCheck);// httpsvr->RegisterService("/s", Search);// httpsvr->RegisterService("/", Login);httpsvr->Start();//<7>开始接收服务--------->http.hppreturn 0;
}

Util.hpp:

// 防止头文件重复包含的编译指令
#pragma once// 包含标准输入输出流库
#include <iostream>
// 包含文件流操作库
#include <fstream>
// 包含字符串处理库
#include <string>// 工具类声明
class Util
{
public:// 静态方法:读取文件内容到字符串中,传递一个文件路径,和一个string,把文件的内容读取到string中// 参数:filename - 文件名,out - 输出字符串指针// 返回值:成功返回true,失败返回falsestatic bool ReadFileContent(const std::string &filename /*std::vector<char>*/, std::string *out){// 获取文件大小int filesize = FileSize(filename); // FileSize函数用于获取指定文件的大小(以字节为单位)。如果没打开就返回-1!!!!// 检查文件大小是否有效if (filesize > 0){// 创建输入文件流对象std::ifstream in(filename); // ifstream#include <fstream>// 方式1:先声明后打开// std::ifstream in;          // 创建未关联文件的流对象// in.open("example.txt");    // 打开文件// 方式2:声明时直接打开(推荐)// std::ifstream in("example.txt");  // 创建并立即打开文件,不用显示调用open函数打开文件// 检查文件是否成功打开if (!in.is_open())return false;// 调整输出字符串大小以容纳文件内容out->resize(filesize);// 将文件内容读取到字符串中// 注意:这里使用了c_str()获取字符串底层指针,并进行强制类型转换in.read((char *)(out->c_str()), filesize);// istream& read(char* s, streamsize n);// 在 C++ 中,out->c_str() 返回的是 const char* 类型指针,而 std::ifstream::read() 需要的是 char* 类型指针,因此需要进行强制类型转换。// 关闭文件流in.close(); // 记得要关闭!!!}else{// 文件大小为0或获取失败时返回falsereturn false;}// 读取成功返回truereturn true;}// 静态方法:从大字符串中读取一行// 参数:bigstr - 输入大字符串,out - 输出行字符串指针,sep - 行分隔符// 返回值:成功返回true,失败返回falsestatic bool ReadOneLine(std::string &bigstr, std::string *out, const std::string &sep /*\r\n*/){// 查找分隔符位置auto pos = bigstr.find(sep);// 如果没有找到分隔符则返回falseif (pos == std::string::npos) // std::string::npos 是 C++ 标准库中 std::string 类的一个静态常量成员,表示"未找到"或"无效位置"的特殊值。它是字符串操作中非常重要的一个标记值。return false;// 提取分隔符前的内容作为一行*out = bigstr.substr(0, pos); // 起始永远是0// 从原字符串中删除已读取的行和分隔符bigstr.erase(0, pos + sep.size()); // 起始必须是0// 读取成功返回truereturn true;}// 静态方法:获取文件大小// 参数:filename - 文件名// 返回值:成功返回文件大小(字节),失败返回-1static int FileSize(const std::string &filename){// 以二进制模式打开文件std::ifstream in(filename, std::ios::binary); // std::ios::binary 是 C++ 中文件打开模式的一个标志,它的作用是告诉文件流以二进制模式而非文本模式打开文件// 文本模式(默认):// 在某些系统(如 Windows)上,会进行换行符转换:// 读取时,\r\n(Windows 换行)会被转换为 \n(C++ 标准换行)。// 写入时,\n 会被转换为 \r\n。// 可能在某些平台上处理特殊字符(如 EOF)时会有额外行为。// 二进制模式:// 完全按原样读写数据!!!!!,不做任何转换。// 适合处理非文本文件(如图片!!!!!!!、音频、视频、压缩包等)。!!!!!!!!!!!!!!!!!!!!!!!// 也适合需要精确控制文件内容的场景(如跨平台数据交换)。// 检查文件是否成功打开if (!in.is_open())return -1;// 将文件指针移动到文件末尾--------seekg移动函数!!!!in.seekg(0, in.end);//in.end:基准位置(seek direction),这里是文件末尾(std::ios::end)。// 获取当前指针位置(即文件大小)int filesize = in.tellg();// 将文件指针移回文件开头in.seekg(0, in.beg);// 关闭文件流in.close();// 返回文件大小return filesize;}
}; // 类定义结束

效果演示

在这里插入图片描述
在这里插入图片描述

  • 在浏览器中输入115.120.238.130:8081
  • 别忘了要先去云服务器官网开启安全组,这样浏览器才能访问服务端

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

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

相关文章

05 GWAS表型数据处理原理

表型数据处理 • 质量性状 – 二分类&#xff1a;可用0 / 1, 1 / 2 数值表示 – 多分类&#xff1a;哑变量赋值&#xff0c;0/1 • 数量性状 – 尽量符合正太分布 – 剔除异常表型值样本 – 多年多点重复观测 – 对于阈值性状&#xff0c;分级数量化或哑变量赋值 R中 shapiro.t…

【Cpolar实现内网穿透】

Cpolar实现内网穿透业务需求第一步&#xff1a;准备工作1、关闭安全软件2、下载所需软件第二步&#xff1a;Nginx的配置第三步&#xff1a;使用cpolar实现内网穿透1、进入 https://dashboard.cpolar.com/get-started 注册&#xff0c;登录&#xff0c;完成身份证的实名认证2、下…

基于 JavaWeb+MySQL 的学院党费缴费系统

基于 JavaWeb 的学院党费缴费系统第 1 章绪论1.1 项目背景当今互联网发展及其迅速&#xff0c;互联网的便利性已经遍及到各行各业&#xff0c;惠及到每一个人&#xff0c;传统的缴费方式都需要每个人前往缴费点陆续排队缴费&#xff0c;不仅浪费大量了个人时间&#xff0c;而且…

LCGL基本使用

LVGC简介 light video Graphics Library (1)纯c与语言编程,将面向对象的思想植入c语言。 (2)轻量化图形库资源,人机交互效果好,在(ios Android QT)移植性较好,但是这些平台对硬件要求较高 lcgc工程搭建 工程源码的获取 获取工程结构 https://github.com/lvgl/lv_po…

嵌入式第十六课!!!结构体与共用体

一、结构体结构体是一种数据类型&#xff0c;它的形式是这样的&#xff1a;struct 结构体名{ 结构体成员语句1&#xff1b;结构体成员语句2&#xff1b;结构体成员语句3&#xff1b;}&#xff1b;举个例子&#xff1a;struct Student {int id;char name[20];float score…

java web 实现简单下载功能

java web 实现简单下载功能 项目结构├── src\ │ ├── a.txt │ └── com\ │ └── demo\ │ └── web\ │ ├── Cookie\ │ ├── download\ │ ├── homework\ │ ├── serv…

虚幻基础:模型穿模

能帮到你的话&#xff0c;就给个赞吧 &#x1f618; 文章目录模型穿模模型之间的阻挡是否正确设置模型是角色的组件&#xff1a;角色的组件不会与场景中其他的物体发生阻挡但可以发生重叠模型穿模 模型之间的阻挡是否正确设置 模型是角色的组件&#xff1a;角色的组件不会与场…

【Linux】linux基础开发工具(二) 编译器gcc/g++、动静态库感性认识、自动化构建-make/Makefile

文章目录一、gcc/g介绍二、gcc编译选项预处理编译汇编链接三个细节三、动静态库感性认识动静态库的优缺点四、自动化构建-make/Makefile背景知识初步上手Makefilemakefile的推导过程makefile语法一、gcc/g介绍 我们之前介绍了编辑器vim&#xff0c;可以让我们在linux上linux系统…

CentOS 7 上使用 Docker 安装 Jenkins 完整教程

目录 前言 准备工作 系统要求 检查系统信息 更新系统 安装Docker 第一步:卸载旧版本Docker(如果存在) 第二步:安装必要的软件包 第三步:添加Docker官方仓库 第四步:安装Docker CE 第五步:启动Docker服务 第六步:验证Docker安装 第七步:配置Docker用户权限…

30.【.NET8 实战--孢子记账--从单体到微服务--转向微服务】--单体转微服务--公共代码--用户上下文会话

在前面的文章中&#xff0c;我们会看到使用ContextSession来获取当前用户的UserId和UserName。这篇文章我们就一起来看看如何实现ContextSession。 一、ContextSession的实现 我们在公共类库SP.Common中创建一个名为ContextSession的类&#xff0c;用于获取当前请求的用户信息。…

BaseDao

#### 10.1 DAO概念> DAO&#xff1a;Data Access Object&#xff0c;数据访问对象。 > > Java是面向对象语言&#xff0c;数据在Java中通常以对象的形式存在。一张表对应一个实体类&#xff0c;一张表的操作对应一个DAO对象&#xff01;>> 在Java操作数据库时&a…

USRP捕获手机/路由器数据传输信号波形(中)

目录&#xff1a; USRP捕获手机/路由器数据传输信号波形&#xff08;上&#xff09; USRP捕获手机/路由器数据传输信号波形&#xff08;中&#xff09; USRP捕获手机/路由器数据传输信号波形&#xff08;下&#xff09; 三、双工通信信号捕获 3.1 信号接收系统 5805e6Hz&a…

使用 Kiro AI IDE 3小时实现全栈应用Admin系统

Hello&#xff0c; 大家好&#xff0c;我是程序员海军, 全栈开发 |AI爱好者 &#xff5c; 独立开发。 之前我是采用Node生态开发的大模型以及MCP Server,大模型开发的生态主要是Python语言&#xff0c;为了更好的学习大模型开发&#xff0c;于是开了新坑。开始学习Python, 以及…

浏览器pdf、image显示

浏览器地址栏 pdf data:application/pdf;base64, data:application/pdf;base64,JVBERi0xLjcKJeLjz9MKMjMgMCBvYmoKPDwv image data:image/jpeg;base64, data:image/jpeg;base64,/9j/4Q3fRXhpZgAATU0AKgAAAAgABwE

《Linux运维总结:银河麒麟V10 SP3启动docker容器报错permission denied》

总结&#xff1a;整理不易&#xff0c;如果对你有帮助&#xff0c;可否点赞关注一下&#xff1f; 更多详细内容请参考&#xff1a;Linux运维实战总结 一、环境信息 二、背景 1、使用docker启动一个nginx容器&#xff0c;报错信息如下&#xff1a; docker: Error response from…

PDF源码解析

PDF源码解析打开PDF解析PDF​0. 文件头关键信息解析技术原理图解文件头的重要性实际文件结构示例开发者注意事项历史背景1. 根目录整体结构关键字段解析核心概念解释实际应用场景完整对象关系图技术总结2. 页面树对象结构关键字段解析页面树工作原理技术要点总结实际应用3. 图像…

java开闭原则 open-closed principle

基本知识 1.核心思想&#xff1a;面向抽象编程 2.基本内涵&#xff1a;对修改关闭&#xff0c;对扩展开放 3.要求&#xff1a;尽可能不修改源码而是增加新功能 例子 以spring5核心原理与30个类手写实战中的为例 package com.gupaoedu.vip.design.principle.openclose;/*** Crea…

拥抱智慧物流时代:数字孪生技术的应用与前景

概述 在数字经济全面推进的当下&#xff0c;物流行业正经历着前所未有的智能化升级。作为新一代信息技术的重要代表&#xff0c;数字孪生技术正悄然改变着物流的运作方式和决策模式。所谓数字孪生&#xff0c;是指在虚拟空间中创建与现实物流系统高度一致的数字模型&#xff0…

libnest2d-头文件分析-libnest2d.hpp-几何类型-策略类型-参数配置

libnest2d 库的主头文件&#xff0c;定义了一个用于 二维不规则形状自动排样&#xff08;Nesting&#xff09; 的C接口。以下是详细解析&#xff1a;1. 头文件结构 (1) 防止重复包含 #ifndef LIBNEST2D_HPP #define LIBNEST2D_HPP // ... #endif // LIBNEST2D_HPP确保头文件只被…

【Docker】部署Docker可视化管理面板Dpanel

一、DPanel 介绍 1.1 DPanel 简介 DPanel 是一款专为 Docker 设计的可视化管理面板&#xff0c;旨在降低容器、镜像及相关资源的管理门槛。通过简洁直观的图形化界面&#xff0c;即使是对 Docker 不熟悉的用户&#xff0c;也能轻松上手&#xff0c;实现容器化应用的高效部署与管…