【Linux网络编程八】实现最简单Http服务器(基于Tcp套接字)

基于TCP套接字实现一个最简单的Http服务器

  • Ⅰ.Http请求和响应格式
    • 1.请求格式
    • 2.响应格式
    • 3.http中请求格式中细节字段
    • 4.http中响应格式中细节字段
  • Ⅱ.域名ip与URL
  • Ⅲ.web根目录
  • Ⅳ.Http服务器是如何工作的?
    • 一.获取请求
    • 二.分析请求
      • 2.1反序列化
      • 2.2解析url
    • 三.构建响应
      • 3.1构建响应正文
      • 3.2构建响应状态
        • ①404错误
        • ②302重定向
      • 3.3构建响应报头
        • ①Content-Length正文长度
        • ②Connection:keep-alive长短链接
        • ③Content Type报头属性
        • ④Set-Cookie会话保存
    • 四.返回响应
  • Ⅵ.实现最简单Http服务器

Ⅰ.Http请求和响应格式

1.请求格式

http请求由4部分构成:请求行,请求报头,空行,请求正文。每个部分都以‘\r\n’结束。

1.请求行:由Method URL HTTP version \r\n构成。也就是由请求方法,请求的资源,http的版本组成。
2.请求报头:里面存放的都是一些请求属性值,并且以Key:Values的形式存储。并且每种属性之间也是用’\r\n’分割。比如里面有一个Content-Length字段,表明的请求正文的长度是多少。
3.空行:单纯的空行,直接存储’\r\n’
4.请求正文:就是客户端要上传的内容,可以没有。
在这里插入图片描述

【注意】

这其实是我们在电脑上看到的格式,但在内存中,虽然每个部分都以’\r\n’结束,但’\r\n’也是字符。所以一个http请求就是一个字符串,只不过这个长字符串中间有很多个’\r\n’.
不过打印出来是多行的。

在这里插入图片描述

【空行的作用是什么?】

如何准确将报头和有效载荷分离?通过添加一个空行。空行之前就是整体报头,之后就是有效内容

【服务器如何读取一个完整的报文?】
1.在tcp缓冲区里,对整个宇符串(可能包含好几个请求)分析,那么你能确定服务器端读取报文时,就可以准确的能够读取到完整的一个报文吗?

不能!只能保证可以读取到完整的报头,因为如果读取到空行,那么空行之前的就是报头而之后的正文部分无法确定多长

2.那http是如何确保能够读取到一个完整的报文的?

①.首先完整的读取到一个报头
②.根据报头里的,报文属性Content-Length,得到正文部分的长度,然后从空行之后按照长度读取。

只有能保证服务器端读完整了,才可以进行网络序列化和反序列化。

示例:
在这里插入图片描述

2.响应格式

http响应也有4部分构成:状态行,响应报头,空行,响应正文,每个部分都以‘\r\n’结束。

1.状态行:由HTTP Version 状态码 状态码描述 ‘\r\n’组成,是描述本次请求的状态。
2.响应报文:与请求报头一样,里面是一些响应报文的一些属性,以Key: Value的形式存储。
3.空行:单纯’\r\n’。
4.响应正文:就是要请求的资源,比如图片,网站等。
在这里插入图片描述
【注意】
客户端在读取时,也无法确保能够准确的读取到完整的一个报文,所以需要添加一个空行。响应属性里也具有响应正文的长度。这样就可以读取到一个完整的报文了。

示例:
在这里插入图片描述
总结:
在这里插入图片描述

3.http中请求格式中细节字段

  • 1.1 HTTP请求中的请求方法

在这里插入图片描述

浏览器提交数据给服务器都是通过表单提交的!
在这里插入图片描述

  • 1.2 GET与POST的区别
    如果你今天想去啊?获取网页你就只用get方法就可以好不管是图片视频音频啊,我们就用get方法呃,但是如果你想把你的参数提交给服务端那么此时你可以选择post方法也可以可以选择get.
    实际上,我们通常是通过一个叫做表单的东西来提交参数的,然后我们去提参的时候,如果我们对应的表单提参方法采用的是get方法,那么其中它对应的参数呢,就会拼接到我们url的后面。然后用?作为分隔符,然后呢左侧是你要访问的资源右侧是呃右侧是你要提交的参数。参数参数之间用&分割。

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

POST方法在提交参数时,不是通过url来提交的,而是通过正文部分将参数提交给服务器的。
在这里插入图片描述
所以POST方法也支持参数提交,采用请求的正文提交参数!
【总结】
在这里插入图片描述

4.http中响应格式中细节字段

Http响应的状态码:

在这里插入图片描述
最常见的状态码,比如200(OK),404(Not Found),403(Forbidden),302(Redirect,重定向),504(Bad Gateway)。
404错误本质就是因为客户端发起的请求不合理。

HTTP常见Header:

1.Content-Type:数据类型(text/html等)Content-Length: Body的长度
2.Host:客户端告知服务器,所请求的资源是在哪个主机的哪个端口上;
3.User-Agent:声明用户的操作系统和浏览器版本信息:
4.referer:当前页面是从哪个页面跳转过来的:
5.location:搭配3xx状态码使用,告诉客户端接下采要去哪里访回;
6.Cookie:用于在客户端存储少量信息.通常用于实现会话(session)的功能;**

Ⅱ.域名ip与URL

Ⅲ.web根目录

第一组知识:

就是我们将来收到一个请求的时候,我们给别人进行响应,响应的时候,我们自己要添加我们自己默认的一些报头还有它的一些状态码了协议版本了,空行了这些字段.我们的网页,图片,也就是资源呢,其实是会被拼接到响应的正文部分!

第二组知识:

那么别人请求的时候呢?他通常会通过在请求里面包含他要请求什么网页,他会包含我要请求什么样的资源,把路径呢通过url给我们呈现出来,那么所以呢,我们的服务器就可以获取到路径,就可以动态的根据路径去挑选,他想要的资源,而我们的资源其实都是存放在一个个的文件里。

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

【web根目录本质】

我在我的服务器进程路径下存在一个wwwroot的文件夹好,这是什么意思呢?将来我想把我的所写的所有的网页所有的图片所有的视频包括我的网站的首页全放在这个目录里面好.以目录以树状结构的形式,从这个目录开始让别人去访问我们把这个目录下的文件,这叫做web根目录.本质就是存放资源的一个统一路径。

http协议呢,它其实,一定要有自己的web根目录,这个web根目录可以是linux的根目录也可以由你自己去指定。

【浏览器(Client)指定路径下将请求的文件内容】
在这里插入图片描述
【存在问题】
客户端不管请求什么目录文件,都访问的是wwwroot目录下html文件的内容所以我们想让客户端请求什么目录文件,服务器就返回该目录下的文件内容,该如何做呢?

必须解析请求行中的url

Ⅳ.Http服务器是如何工作的?

重点:我们要理解一个http服务器,它是怎么工作的,http服务器它无非就是在对http请求的内容在进行,我们对应的解析。

我们的重点是什么呢?重点是你要去理解http的请求,它无非就是通过套接字,我们(http服务器)读到了它的http的请求,读到了它的请求然后按照我们http的上面的这个格式(反序列化),把它给它解析出来提取url然后呢,我们给它构建响应把响应再给它返回去此时,这个就叫做http协议。

我们的浏览器访问服务器的本质实际上是把我们的服务器上的指定路径下的网页资源获取到浏览器当中然后由浏览器进行解释。最后就看到了网页效果。

【总结】
其实所有的协议本质都是对‘字符串′做分析处理

一.获取请求

从网络里获取到的请求是字符串形式的,但字符串形式服务器并不认识,需要反序列化成结构体(客户端和服务器端都认识的结构体)
在这里插入图片描述

二.分析请求

2.1反序列化

将读取到的字符串分割构成服务器认识的请求格式,也就是最上面讲到到http请求格式,由请求行,请求报头,空行,请求报文组成,我们需要构建一个request类。

这个工作。其实就是把他的请求做字符串做分析,按行做分析(反序列化)。把一整个字符串分割成若干个字符串。
分析完之后之后呢?然后我们再把正文部分直接也拿出来,把它的每一行也都分别push到vector里就可以了(构建初始化req对象)最后直接将正文部分放进text中。

class HttpRequest
{
public:void Deserialize(std::string req)//反序列化将字符串分割构建结构体对象{while(true){std::size_t pos=req.find(seq);if(pos==std::string::npos)break;std::string temp =req.substr(0,pos);if(temp.empty())break;req_header.push_back(temp);req.erase(0,pos+seq.size());}text=req;
public:std::vector<std::string> req_header;std::string text;//服务器分析的结果std::string method;//请求方法std::string url;//请求资源std::string http_version;//http版本std::string resource_path;//资源路径std::string suffix;//文件后缀
};

【注意】

存在问题:默认从套接字里读取到的就是一个完整的报文没有做处理

2.2解析url

解析url的目的就是为了让服务器返回客户端所指定的资源(客户端通过url形式指定资源路径)
就是将url中的资源路径resource_path给获取到。

而进一步解析:解析请求行(在vector容器中的第一个元素)

void Split(){std::stringstream ss(req_header[0]);ss>>method>>url>>http_version;resource_path=wwwtew;if(url=="/"||url=="/index.html"){resource_path+="/";resource_path+=homepage;}else{resource_path+=url;}
}

如果服务器收到的这个http请求他的url只是一个斜杠,这其实表明的就是他要请求我们的web根目录那么此时,我们只会把整个网站的叫做首页给你返回那么,这个首页呢,我们就叫做index.html

在这里插入图片描述

【跳转网页】
在这里插入图片描述

三.构建响应

服务器获取到请求后,分析请求,将请求的资源,以HTTP响应的方式返回给客户端。

3.1构建响应正文

先将浏览器请求的资源构建出来,因为服务器已经解析出url中资源路径,所以服务器直接可以通过资源路径定位到对应的文件中,并从文件中读取出来,存放在正文中。

服务器动态读取资源文件,构建响应正文

 static std::string ReadHtmlContent(const std::string &htmlpath){//这里存在一个坑--二进制读取存在问题//std::ifstream in(htmlpath);std::ifstream in(htmlpath,std::ios::binary);if(!in.is_open()) return " ";//如果打开文件失败说明客户端的请求不合理返回空//获取文件的大小in.seekg(0, std::ios_base::end);auto len = in.tellg();in.seekg(0, std::ios_base::beg);//提前开辟空间存放二进制std::string content;content.resize(len); in.read((char*)content.c_str(), content.size());/*std::string content;std::string line;while(std::getline(in,line)){content+=line;}*/in.close();return content;}

3.2构建响应状态

①404错误

当服务器读取资源文件时,可能会读取失败,因为客户端不合理的请求,请求不存在的资源。这时服务器就会访问到空资源。

当服务器访问到空资源时,我们就让浏览器再重新请求404网页资源,也就是服务器将404网页资源返回给浏览器。
在这里插入图片描述

   std::string text;bool ok =true;text=ReadHtmlContent(req.resource_path);//可能会读取失败,因为客户端不合理的请求,请求不存在的资源if(text.empty())//访问不存在的资源,就让浏览器申请404网页资源{ok=false;std::string err_html=wwwtew;err_html+="/";err_html+="err.html";//404网页资源text=ReadHtmlContent(err_html);}std::string response_line;//并将状态码设置成404if(ok)response_line="HTTP/1.0 200 OK\r\n";elseresponse_line="HTTP/1.0 404 Not Found\r\n";

err.html:


<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>404 Not Found</title>
<style>
body {
text-align: center;
padding: 150px;
}
h1 {
font-size: 50px;
}
body {
font-size: 20px;
}
a {
color: #008080;
text-decoration: none;
}
a:hover {
color: #005F5F;
text-decoration: underline;
}
</style>
</head>
<body>
<div>
<h1>404</h1>
<p>页面未找到<br></p>
<p>
您请求的页面可能已经被删除、更名或者您输入的网址有误。<br>
请尝试使用以下链接或者自行搜索:<br><br>
<a href="https://www.baidu.com">百度一下></a>
</p>
</div>
</body>
</html>
②302重定向

3XX重定向原理(依赖Location字段)
1.重定向的意思:让服务器指导我们的浏览器访问新的地址,这个新的地址就存在location属性字段中。
2.3XX对应的意思
第一我要访问的资源,服务器无法给我提供对应服务。
第二,我们对应的这个要去的新地址呢,有location指定。
所以你的这个浏览器它会二次发起请求,二次发起请求的时候,那么,这个时候,它请求的就不再是这个了服务器了,而是根据它给location的地址重新构建http请求,去访问新的地址,

在这里插入图片描述

其实就是http response通过3XX这样的状态码,然后配合location让浏览器发起二次请求就可以了

3.3构建响应报头

①Content-Length正文长度
        std::string response_header="Content-length: ";//构建响应报头 response_header+=std::to_string(text.size());response_header+="\r\n";
②Connection:keep-alive长短链接
  • 短链接

一个巨大的页面是会包含非常多的元素的!每一个元素就是一个资源!

一个完整的网页了,每一张图片就是一个文件资源,所以要让浏览器得到这个图片就要发起一次http请求,所以一个网页上面有100张图片,浏览器会先.
获取这个网页,获取这个网页的同时呢?我们这个网页内部可能还需要包含很多很多的图片,那么,浏览器可能需要再二次三次十次八次继续发起请求好,也就是说对我们今天我们的服务器来讲呢,要获取一个100张图片网页我们要发起101次http的请求。就可能一瞬间就要创建几十,甚至上百个线程来把每一个资源都推送给我们的浏览器然后让让它呢帮我们把我们对应的资源呢返回返回到我们的浏览器让它去渲染。

这种基于请求一次,响应一次然后关闭链接这种我们称之为叫做短链接响应(http/1.0)一次请求只拿回来一个资源,然后关闭链接
在这里插入图片描述

  • 长连接

建立一个TCP连接,发送和返回多个http的request和response。这个链接在本轮请求没有完全结束之前链接不关闭,本轮请求比如,你把一个网页该要的100个资源全拿到了本轮请求全部完毕,那么此时,该链接再断开这种我们称之为叫做长链接(http/1.1),一次请求拿回来多个资源。

Connection: keep-alive
所以呢,当我们通信双方在进行最开始几次那么进行http请求时,服务器和客户端双方是需要,协商http版本的,协商http版本来证明,我们双方是否支持叫做长链接技术然后呢?但是在协商,我们都支持支持之后,我可以不用呀。那么所以怎么样能证明,我们2个都要选择使用长链接呢?
那么此时就有connection选项叫做keep alive选项它表明的是我们是否在双方通信时,选择使用长链接,当客户端和服务器connection选项都是keep alive时双方即会采用长链接的方案来进行通信

③Content Type报头属性

根据短链接,我们知道,如果一个网页中包含图片时,浏览器在访问这个网页时,首先会发起链接访问网页资源,然后识别到还有图片,则会自动发起二次链接获取图片资源。然后将图片资源也显示出来。
在这里插入图片描述
但浏览器请求的资源非常多啊,并且我的服务器上呢,这个图片呢,有png的也有jpg的,那么就是服务器怎么知道浏览器要请求的是什么类型的文件呢?因为只有它的请求的文件的类型确定了,服务器才好填我们的Content type,这样将响应返回时,浏览器才能按照对应的格式显示出来。
该怎么做呢?图片的类型完全根据文件的后缀!
所以服务器只要找到对应的图片的文件路径,并将文件的后缀提取出来,就可以找到浏览器想要什么类型的图片了,就可以将图片类型填写到Content-Type属性中。

两件工作
第一件工作,需要我(服务器)能够在读取你的请求时,就已经知道你的http请求的资源的后缀是什么。通过url路径中资源路径就可以找到。
在这里插入图片描述

第二个工作,要维护了一个简单的映射表。存储的是文件后缀对应的Content-Type类型。(利用unordermap做一个content的映射表)

在这里插入图片描述

        response_header+="Content-Type: ";response_header+=SuffixToDesc(req.suffix);response_header+="\r\n";

[问题]
在这里插入图片描述

④Set-Cookie会话保存

http协议呢?本身它是没有状态的,请求什么资源就是什么资源。

应用场景:访问B站
浏览器你发的任意一个链接请求都有要你登录验证,为什么?
因为间每一个资源都要到对你进行身份认证,因为我们必须得是登录验证状态才能问某些资源,那么我们登入B站后,再访问n多个视频(n个资源)时为什么都不需要你再登入验证呢?

也就B站服务器怎么知道我一直是处于登录状态的呢。这就是我们的会话保持的功能的一个概念。

Set-Cookie原理
第一次登入B站时,访问B站登入页面服务器,服务器如果发现你的请求报文里面没有携带cookie信息,此时,服务器就直接跳转到登录页面让你去登录验证.但是呢,只要你曾经注注册并登录过,然后你的浏览器请求一次之后呢认证通过之后,就会把密码和账号信息就写到了这个浏览器的cookie文件往后每次请求都会携带cookie,所以我们此时b站就会自动进行认证了
一自动携带,二自动认证所以从此往后你就不需要再去输入什么账号和密码。

验证:

        response_header+="Set-Cookie: name=tao&&passwd=123456789";response_header+="\r\n";

第一次访问服务器将对应的cookie返回,则浏览器会将cookie信息保存到cookie文件中,等下次请求时,就默认会将cookie信息带上。
在这里插入图片描述

【cookie的保存方法】
我们对应的浏览器要保存这个cookie文件,它通常有两种保存方法第一种是文件级,第2种是内存级。

意思就是说浏览器,它收到了这个cookie,因为浏览器本身是一个进程,那这个进程内部是可以new malloc空间的,所以呢它收到它登入成功之后,它就可以把cookie信息就残留在浏览器进程的这个内存当中,它不往磁盘上写。
所以这个时候呢,你不关浏览器,你可以一直访问你一旦把浏览器关掉了。那么,你再把浏览器重新打开再访问b站你又要重新登录了好这是内存级.

如果你的浏览器它设置的cookie文件的保存呢,是直接把它保存到文件当中,这个文件呢,本身就是浏览器在浏览器的特定的安装路径下就会存在这样的文件文件级的cookie那么文件级的cookie呢,大家都知道文件呢,是在磁盘当中的所以你把浏览器即便关掉了好即便你关掉了,你照样可以不用登录了好这就是文件级。

四.返回响应

       std::string blank_line="\r\n";//构建空白行std::string response=response_line;//添加状态行response+=response_header;//添加响应报头response+=blank_line;//添加空行response+=text;//添加响应正文write(sockfd,response.c_str(),response.size());//发送给客户端

Ⅵ.实现最简单Http服务器

HttpServer.hpp

#include <iostream>
#include <pthread.h>
#include "Socket.hpp"
#include "Log.hpp"
#include <sstream>
#include <string>
#include <fstream>
#include <vector>
#include <unordered_map>
Log log;
const uint16_t defaultport=8888;
const std::string wwwtew="./wwwtew";
const std::string homepage="index.html";
const std::string seq="\r\n";
class HttpServer;
class pthreadData
{
public:pthreadData(int sockfd,HttpServer*s):_sockfd(sockfd),svr(s){}
public:int _sockfd;HttpServer* svr;
};
class HttpRequest
{
public:void Deserialize(std::string req)//反序列化将字符串分割构建结构体对象{while(true){std::size_t pos=req.find(seq);if(pos==std::string::npos)break;std::string temp =req.substr(0,pos);if(temp.empty())break;req_header.push_back(temp);req.erase(0,pos+seq.size());}text=req;}void Split(){std::stringstream ss(req_header[0]);ss>>method>>url>>http_version;resource_path=wwwtew;if(url=="/"||url=="/index.html"){resource_path+="/";resource_path+=homepage;}else{resource_path+=url;}//分割出资源路径后,就可以获取对应的文件后缀auto pos=resource_path.rfind(".");if(pos==std::string::npos)suffix=".html";//默认是html类型else suffix=resource_path.substr(pos);}void DebugPrint(){for(auto &line : req_header){std::cout << "--------------------------------" << std::endl;std::cout << line << "\n\n";}std::cout << "method: " << method << std::endl;std::cout << "url: " << url << std::endl;std::cout << "http_version: " << http_version << std::endl;std::cout << "resource_path: " << resource_path << std::endl;std::cout << text << std::endl;} 
public:std::vector<std::string> req_header;std::string text;//服务器分析的结果std::string method;std::string url;std::string http_version;std::string resource_path;std::string suffix;//文件后缀
};
class HttpServer
{
public:HttpServer(uint16_t port=defaultport):_port(port){}bool Start(){_listensock.Socket();_listensock.Bind(_port);_listensock.Listen();for(;;){std::string clientip;uint16_t clientport;int sockfd=_listensock.Accept(&clientip,&clientport);log(Info,"connect success sockfd:%d",sockfd);pthreadData* td=new pthreadData(sockfd,this);pthread_t tid;pthread_create(&tid,nullptr,ThreadFunc,td);}  }static std::string ReadHtmlContent(const std::string &htmlpath){//这里存在一个坑--二进制读取存在问题//std::ifstream in(htmlpath);std::ifstream in(htmlpath,std::ios::binary);if(!in.is_open()) return " ";//如果打开文件失败说明客户端的请求不合理返回空//获取文件的大小in.seekg(0, std::ios_base::end);auto len = in.tellg();in.seekg(0, std::ios_base::beg);//提前开辟空间存放二进制std::string content;content.resize(len); in.read((char*)content.c_str(), content.size());/*std::string content;std::string line;while(std::getline(in,line)){content+=line;}*/in.close();return content;}//获取文件后缀对应的类型 std::string SuffixToDesc(const std::string &suffix){auto iter=suffix_type.find(suffix);if(iter==suffix_type.end())return suffix_type[".html"];else return suffix_type[suffix];}void HanderHttp(int sockfd){char buffer[10240];//1.服务器获取客户端发起的请求(字符串形式服务器并不认识,需要反序列化成结构体)ssize_t n=read(sockfd,buffer,sizeof(buffer)-1);if(n>0){buffer[n]=0;std::cout<<buffer;HttpRequest req;req.Deserialize(buffer);req.Split();//req.DebugPrint();//1.1服务器对请求进行解析,分析url//2.服务器返回响应给客户端//2.1构建响应//std::string text=ReadHtmlContent(req.resource_path);//构建正文 动态的从文件里读取对应的资源内容,并且根据客户端url来搜索资源//std::string response_line="HTTP/1.0 200 OK\r\n";//构建响应状态行std::string text;bool ok =true;text=ReadHtmlContent(req.resource_path);//可能会读取失败,因为客户端不合理的请求,请求不存在的资源if(text.empty())//访问不存在的资源,就让浏览器申请404网页资源{ok=false;std::string err_html=wwwtew;err_html+="/";err_html+="err.html";text=ReadHtmlContent(err_html);}std::string response_line;//并将状态码设置成404if(ok)response_line="HTTP/1.0 200 OK\r\n";elseresponse_line="HTTP/1.0 404 Not Found\r\n";//response_line="HTTP/1.0 302 Found\r\n";//302配合location实现重定向std::string response_header="Content-length: ";//构建响应报头 response_header+=std::to_string(text.size());response_header+="\r\n";response_header+="Location: https://www.baidu.com\r\n";response_header+="Content-Type: ";response_header+=SuffixToDesc(req.suffix);response_header+="\r\n";response_header+="Set-Cookie: name=tao&&passwd=123456789";response_header+="\r\n";std::string blank_line="\r\n";//构建空白行std::string response=response_line;response+=response_header;response+=blank_line;response+=text;write(sockfd,response.c_str(),response.size());}close(sockfd);}static void *ThreadFunc(void *avgs){pthreadData*td=static_cast<pthreadData*>(avgs);pthread_detach(pthread_self());td->svr->HanderHttp(td->_sockfd);  delete td;return nullptr;}~HttpServer(){}
private:Sock _listensock;//本质是sockfduint16_t _port;std::unordered_map<std::string,std::string> suffix_type;
};

HttpServer.cc

#include <iostream>
#include "HttpServer.hpp"
#include <memory>
using namespace std;int main()
{HttpServer*svr=new HttpServer();svr->Start();}

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

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

相关文章

java重点学习-mysql

2.1 如何定位慢查询? 1:介绍一下当时产生问题的场景(我们当时的一个接口测试的时候非常的慢&#xff0c;压测的结果大概5秒钟) 2.我们系统中当时采用了运维工具(Skywalking)&#xff0c;可以监测出哪个接口&#xff0c;最终因为是sql的问题 3.在mysql中开启了慢日志查询&#…

【LeetCode】14.最长公共前缀

题目要求 解题思路 这道题我们可以通过一列一列的比较是否相等来解决 代码实现 class Solution { public:string longestCommonPrefix(vector<string>& strs) {string ret;//以第一个字符串为标准for(int i0;i<strs[0].size();i){//保存第一个字符串的第i个位…

前端---对MVC MVP MVVM的理解

就需要从前端这些年的从无到有、从有到优的变迁过程讲一下。 1. Web1.0时代 在web1.0时代并没有前端的概念&#xff0c;开发一个web应用多数采用ASP.NET/Java/PHP编写&#xff0c;项目通常用多个aspx/jsp/php文件构成&#xff0c;每个文件中同时包含了HTML、CSS、JavaScript、…

微信小程序手写签名

微信小程序手写签名组件 该组件基于signature_pad封装&#xff0c;signature_pad本身是web端的插件&#xff0c;此处将插件代码修改为小程序端可用。 signature_pad.js /*!* Signature Pad v5.0.3 | https://github.com/szimek/signature_pad* (c) 2024 Szymon Nowak | Releas…

[数据集][目标检测]轮胎检测数据集VOC+YOLO格式4629张1类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;4629 标注数量(xml文件个数)&#xff1a;4629 标注数量(txt文件个数)&#xff1a;4629 标注…

Spring扩展点系列-InstantiationAwareBeanPostProcessor

文章目录 简介测试一1、配置文件Bean注册2、单元测试方法3、测试类4、输出结果结论 测试二1、测试类2、输出结果结论 源码解析postProcessPropertiesCommonAnnotationBeanPostProcessorAnnotationInjectedBeanPostProcessor 总结 简介 spring容器中Bean的生命周期内所有可扩展…

【重构获得模式 Refactoring to Patterns】

重构获得模式 Refactoring to Patterns 面向对象设计模式是“好的面向对象设计”&#xff0c;所谓“好的面向对象设计”指的是那些可以满足“应对变化&#xff0c;提高复用”的设计。 现代软件设计的特征是“需求的频繁变化”。设计模式的要点是“寻找变化点&#xff0c;然后…

Kafka 实战演练:创建、配置与测试 Kafka全面教程

文章目录 1.配置文件2.消费者1.注解方式2.KafkaConsumer 3.依赖1.注解依赖2.KafkaConsumer依赖 本文档只是为了留档方便以后工作运维&#xff0c;或者给同事分享文档内容比较简陋命令也不是特别全&#xff0c;不适合小白观看&#xff0c;如有不懂可以私信&#xff0c;上班期间都…

5G前传-介绍

1. 引用 知识分享系列一&#xff1a;5G基础知识-CSDN博客 5G前传的最新进展-CSDN博客 灰光和彩光_通信行业5G招标系列点评之二&#xff1a;一文读懂5G前传-光纤、灰光、彩光、CWDM、LWDM、MWDM...-CSDN博客 术语&#xff1a; 英文缩写描述‌BBU&#xff1a;Building Baseba…

review——C++中的右值引用

目录 前言 一、什么是左值、什么是右值 二、右值引用 1.右值引用与右值引用的一些性质 2.解释一下左值引用与右值应用于程序员之间的关系 3.右值引用与移动语义 4.右值引用右值后变成左值的必要性与完美转发 1.右值引用引用右值后变为左值属性的必要性 2.完美转发 Ⅰ…

【docker】docker 镜像仓库的管理

Docker 仓库&#xff08; Docker Registry &#xff09; 是用于存储和分发 Docker 镜像的集中式存储库。 它就像是一个大型的镜像仓库&#xff0c;开发者可以将自己创建的 Docker 镜像推送到仓库中&#xff0c;也可以从仓库中拉取所需的镜像。 Docker 仓库可以分为公共仓…

Java获取小程序码示例(三种小程序码)

首先我们可以看到官方文档上是有三种码的 获取小程序码 这里特别要注意的是第一种和第三种是有数量限制的&#xff0c;所以大家生成的时候记得保存&#xff0c;也不要一直瞎生成 还有一点要注意的是第一种和第二种是太阳码 第三种是方形码 好了直接上代码 这里要注意&#xff…

Golang | Leetcode Golang题解之第391题完美矩形

题目&#xff1a; 题解&#xff1a; func isRectangleCover(rectangles [][]int) bool {type point struct{ x, y int }area, minX, minY, maxX, maxY : 0, rectangles[0][0], rectangles[0][1], rectangles[0][2], rectangles[0][3]cnt : map[point]int{}for _, rect : range…

HTML生日蛋糕

目录 写在前面 完整代码 代码分析 系列文章 写在最后 写在前面 HTML实现的生日蛋糕来喽&#xff0c;小编亲测&#xff0c;发给好友可以直接打开哦。在代码的第183行可以写下对朋友的祝福&#xff0c;快拿去送给你的好朋友吧&#xff01; 完整代码 <!DOCTYPE html>…

C++ 上位软件通过Snap7开源库访问西门子S7-1200/S7-1500数据块的方法

C 上位软件通过Snap7开源库访问西门子S7-1200/S7-1500数据块的方法

【LeetCode】15.三数之和

题目要求 解题思路 这道题我们可以使用暴力解法来解决&#xff0c;时间复杂度为O&#xff08;N^3&#xff09;。但是会超时&#xff0c;因此我们需要对暴力解法进行优化&#xff0c;而这道题我们使用双指针来进行优化&#xff0c;即依次固定一个数&#xff0c;在接下来的区间中…

vue3整合antv x6实现图编辑器快速入门

安装&#xff1a; npm install antv/x6 --save如果使用 umd 包&#xff0c;可以使用下面三个 CDN 中的任何一个&#xff0c;默认使用 X6 的最新版&#xff1a; https://unpkg.com/antv/x6/dist/index.jshttps://cdn.jsdelivr.net/npm/antv/x6/dist/index.jshttps://cdnjs.clo…

mysql树形结构返回是否叶子节点

我们界面上展示树形结构的时候往往会用到懒加载&#xff0c;做懒加载需要知道哪个节点是叶子节点&#xff0c;这样叶子节点就不需要继续往下加载了&#xff0c;这种需求可以通过sql实现 先来看下表结构 方式一,通过sql语句直接获取leaf 什么是叶子节点&#xff1f;就是没有哪…

Alternative account/备选科目代码配置说明 【1:1和国家科目配置运营科目】

业务场景&#xff1a; 有个关于mapping的问题&#xff1a; 1. 如果在GL account的master data里面&#xff0c;之前已经做好了alternative account的mapping, 那我要改成另外的local account,这样 A 会不会影响本地子公司的报表&#xff1f; 2. 如果GL account和alternativ…

Redis 缓存深度解析:穿透、击穿、雪崩与预热的全面解读

Redis 缓存深度解析&#xff1a;穿透、击穿、雪崩与预热的全面解读 一 . 什么是缓存 ?二 . 使用 Redis 作为缓存三 . 缓存的更新策略3.1 定期生成3.2 实时生成 四 . 缓存预热、缓存穿透、缓存雪崩、缓存击穿4.1 缓存预热4.2 缓存穿透4.3 缓存雪崩4.4 缓存击穿 Hello , 大家好 …