【网络】3.HTTP(讲解HTTP协议和写HTTP服务)

news2025/3/7 5:15:48

目录

  • 1 认识URL
    • 1.1 URI的格式
  • 2 HTTP协议
    • 2.1 请求报文
    • 2.2 响应报文
  • 3 模拟HTTP
    • 3.1 Socket.hpp
    • 3.2 HttpServer.hpp
      • 3.2.1 start()
      • 3.2.2 ThreadRun()
      • 3.2.3 HandlerHttp()
  • 总结

1 认识URL

什么是URI?

URI 是 Uniform Resource Identifier的缩写,URI就是由某个协议方案表示的资源的定位标识符。采用HTTP协议时,协议方案就是http。除此之外,还有ftp、mailto、telnet等。

什么是URL?

URI用字符串标识某一互联网资源,而URL表示资源的地点(互联网上所处的位置)。可见URL是URI的子集。

1.1 URI的格式

在这里插入图片描述
登录信息认证

指定用户名和密码作为从五毒气短获取资源时必要的登录信息(身份认证)。此项是可选项。

服务器地址

使用绝对URI必须指定带访问的服务器地址。地址可以是DNS,或者是IPV4,IPV6格式。

服务器端口号

指定服务器连接的网络端口号,如果用户省略则自动使用默认端口号。

带层次的文件路径

指定服务器上的文件路径来定位特指的资源。这与UNIX系统的文件目录结构类似。如果不写,默认是首页,一般是index.html

查询字符串

针对已指定的文件路径内的资源,可以使用查询字符串兑换如任意参数。此项可选。

片段标识符

使用片段标识符通常可标记处已获取资源的子资源(文档内的某个位置)。但是在RFC中没有明确规定其使用方法。该项为可选项

2 HTTP协议

HTTP协议用于客户端和服务器之间的通信。
客户端

请求访问文本或图像等资源的一端称为客户端。

服务端

提供资源响应的一端称为服务端。

2.1 请求报文

下面是HTTP请求的格式:
在这里插入图片描述
真实的请求如下:
在这里插入图片描述
对比一下格式与真实情况:
在这里插入图片描述

2.2 响应报文

下面是HTTP响应的格式:
在这里插入图片描述
真实响应如下:
在这里插入图片描述
下面是对比:
在这里插入图片描述

3 模拟HTTP

下面的代码是socket套接字实现,主要为了http通信提供网络接口。

3.1 Socket.hpp

#pragma once

#include <iostream>
#include <string>
#include <unistd.h>
#include <cstring>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>

enum
{
    SocketErr = 2,
    BindErr,
    ListenErr,
};

const int backlog = 10;

class Sock
{
public:
    Sock()
    {}

    ~Sock()
    {}

public:
    void Socket()
    {
        _sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (_sockfd < 0)
        {
            std::cout << "sock error " << strerror(errno) << errno << std::endl;
            exit(SocketErr);
        }
    }

    void Bind(uint16_t port)
    {
        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(port);
        local.sin_addr.s_addr =INADDR_ANY;

        if (bind(_sockfd, (struct sockaddr*)&local, sizeof(local)) < 0)
        {
            std::cout << "bind error " << strerror(errno) << errno << std::endl;
            exit(BindErr);
        }
    }

    void Listen()
    {
        if (listen(_sockfd, backlog) < 0)
        {
            std::cout << "listen error " << strerror(errno) << errno << std::endl;
            exit(ListenErr);
        }
    }

    int Accept(std::string *clientip, uint16_t *clientport)
    {
        struct sockaddr_in peer;
        socklen_t len = sizeof(peer);
        int newfd = accept(_sockfd, (struct sockaddr*)& peer, &len);
        if (newfd < 0)
        {
            std::cout << "accept error " << strerror(errno) << errno << std::endl;
            return -1;
        }

        char ipstr[64];
        inet_ntop(AF_INET, &peer.sin_addr, ipstr, sizeof(ipstr));
        *clientip = ipstr;
        *clientport = ntohs(peer.sin_port);

       return newfd; 
    }

    bool Connnect(const std::string &ip, const uint16_t &port)
    {
        struct sockaddr_in peer;
        memset(&peer, 0, sizeof(peer));
        peer.sin_family =AF_INET;
        peer.sin_port = htons(port);
        inet_pton(AF_INET, ip.c_str(), &(peer.sin_addr));

        int n = connect(_sockfd, (struct sockaddr*)&peer, sizeof(peer));
        if (n == -1)
        {
            std::cout << "connect to" << ip << " : " << port << " error "  << std::endl;
            return false;
        }
        return true;
    }

    void Close()
    {
        close(_sockfd);
    }

    int Fd()
    {
        return _sockfd;
    }
    
private:
    //文件描述符
    int _sockfd;  
};

3.2 HttpServer.hpp

class HttpServer
{
public:
    HttpServer(int port = defaultport)
    :_port(defaultport)
    {}
private:
    Sock _listensock;
    uint16_t _port;
};

模拟的是使用HTTP协议的过程,客户端需要向浏览器访问,
输入的内容为ip:port。 例如:http://124.223.90.51:8085

因此,需要创建一个函数来启动HTTP服务器:

3.2.1 start()

    bool Start()
    {
        //创建套接字
        _listensock.Socket();
        _listensock.Bind(_port);
        _listensock.Listen();

        for (;;)
        {
            std::string clientip;
            uint16_t clientport;
            int sockfd = _listensock.Accept(&clientip, &clientport);

            pthread_t tid;
            ThreadData *td = new ThreadData(sockfd, this);
            td->sockfd = sockfd;
            pthread_create(&tid, nullptr, ThreadRun, td);
        }
    }   

1.创建监听套接字

_listensock.Socket();  
_listensock.Bind(_port);
_listensock.Listen();
  • _listensock.Socket():创建 TCP 套接字(socket())。
  • _listensock.Bind(_port):绑定端口 _port(bind())。
  • _listensock.Listen():监听端口,等待客户端连接(listen())。

作用:服务器启动,监听 _port 端口,准备接受 HTTP 请求。.

2.接受客户端连接

int sockfd = _listensock.Accept(&clientip, &clientport);

  • Accept():阻塞等待客户端连接,成功返回新连接的套接字 sockfd,并获取客户端的 IP 和端口。
  • 客户端访问 http://服务器IP:端口,就会触发 Accept()。

作用:获取客户端连接,并准备创建线程处理。.

3.创建新线程处理客户端请求

pthread_t tid;
ThreadData *td = new ThreadData(sockfd, this);
td->sockfd = sockfd;
pthread_create(&tid, nullptr, ThreadRun, td);

  • 创建 ThreadData 结构体,存储 sockfd 和 this(服务器指针)。
  • pthread_create()创建新线程 ThreadRun(td),让线程处理 sockfd 连接。

作用:服务器为每个客户端请求创建一个新线程并行处理,提高并发能力。.

3.2.2 ThreadRun()

    static void* ThreadRun(void* args)
    {
        pthread_detach(pthread_self());
        ThreadData *td = static_cast<ThreadData*>(args);
        // char buffer[10240];
        // //ssize_t n = read(?; buffer, sizeof(buffer - 1));  可以使用read
        
        // //也可以使用recv
        // ssize_t n = recv(td->sockfd, buffer, sizeof(buffer - 1), 0);
        // if (n > 0)
        // {
        //     buffer[n] = 0;
        //     std::cout << buffer;
        // }

        HandlerHttp(td->sockfd, td->httpsvr);
        delete td;
        return nullptr;
    }
  • 这段代码是 HttpServer 服务器每个新线程的执行函数 ThreadRun(),用于处理客户端的 HTTP 请求。

  • 当客户端连接服务器时,服务器会创建一个新线程执行 ThreadRun(),读取 HTTP 请求并进行处理。

pthread_detach(pthread_self()) 作用:

  • 让线程在完成后自动释放资源,不需要 pthread_join() 手动回收。
  • 避免僵尸线程(已结束但未回收的线程)。
  • 适用于短生命周期的线程,如 HTTP 服务器的请求处理线程。

为什么要线程分离 ?

  • HTTP 请求通常是短暂的,处理完成后线程就不需要存在了。
  • 如果不分离,主线程需要 pthread_join() 逐个回收,会浪费资源。
  • 让线程自动销毁,提高服务器并发能力

为什么ThreadRun被设置为static?

  1. pthread_create() 需要一个 C 语言风格的函数指针

pthread_create的函数签名如下:

int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                   void *(*start_routine) (void *), void *arg);

  • 第三个参数 start_routine 是一个函数指针,必须符合 void* ()(void) 这种标准格式。

  • 普通成员函数有一个隐藏的 this 指针,无法直接传递给 pthread_create()。

所以,必须用:
普通的 C 函数(static 成员函数), 或全局函数

3.2.3 HandlerHttp()

static void HandlerHttp(int sockfd, HttpServer *httpsvr)
    {
        char buffer[10240];
        ssize_t n = recv(sockfd, buffer, sizeof(buffer) - 1, 0);   //在这里,不能保证读到了完整的http请求
        if (n > 0)
        {
            buffer[n] = 0;
            std::cout << buffer << std::endl;  //假设我们读到的是一个完整的http请求

            HttpRequest req;
            req.Deserialize(buffer);
            req.Parse();

            std::string text;
            bool ok = true; 
            text = ReadHtmlContent(req.file_path);  //读取客户端请求的文件 --> "./index.html"

            if (text.empty())
            {
                ok = false;
                std::string err_html = wwwroot;
                err_html += "/";
                err_html += "err.html";
                text = ReadHtmlContent(err_html);
            }
            
            //返回响应的过程
            std::string response_lines;
            if (ok)
            {
                response_lines = "HTTP/1.0 200 OK\r\n";
            }
            else 
            {
                response_lines = "HTTP/1.0 404 Not Found\r\n";
            }

            std::string response_header = "Content-Length: ";
            response_header += std::to_string(text.size());
            response_header += "\r\n";
            response_header += "Content-Type: text/html\r\n";
            response_header += "\r\n";
            response_header += "Set-Cookie : name=haha&&passds=12345";
            response_header += "\r\n";
            
            std::string blank_lines = "\r\n";

            std::string response = response_lines;
            response += response_header;
            response += blank_lines;
            response += text;

            //将内容发送给客户端  -- 响应
            send(sockfd, response.c_str(), response.size(), 0);
        }
        
        close(sockfd);
    }

HandlerHttp函数的作用是处理HTTP请求并返回响应

HandlerHttp() 的作用是 处理 HTTP 请求并返回响应,它是 HTTP 服务器的核心逻辑,负责:

  1. 读取客户端请求数据
  2. 解析 HTTP 请求
  3. 获取请求的资源(如 HTML 页面)
  4. 构造 HTTP 响应
  5. 将 HTTP 响应返回给客户端
  6. 关闭连接

1. 如何解析HTTP请求?
根据前文所说,HTTP请求格式如下:
在这里插入图片描述
可以看到,HTTP请求是一个已经序列化的报文,因此,我们如果想要知道具体的Method, URI,Http_Version就必须进行反序列化。

void Deserialize(std::string req)
{
    while(true)
    {
        ssize_t pos = req.find(seq);  // 找到 "\r\n" 的位置
        if (pos == std::string::npos) // 没有找到 "\r\n",说明 HTTP 头部已经解析完毕
            break;
        
        std::string temp = req.substr(0, pos);  // 提取一行 HTTP 头部信息
        if (temp.empty()) // 如果这一行是空的,说明遇到了 HTTP 头部和正文的分隔行
            break;
        
        req_header.push_back(temp);  // 将解析出的头部信息存入 `req_header`
        req.erase(0, pos + seq.size());  // 删除已经解析的部分,继续处理剩下的内容
    }

    // req 现在去掉了所有头部,剩下的就是 HTTP 请求的正文
    text = req;
}

假设客户端发送了如下 HTTP 请求:
在这里插入图片描述
那么req_header 里的内容:
在这里插入图片描述
经过反序列化之后,req_header的内容如下:

req_header[0] = “GET /index.html HTTP/1.1”
req_header[1] = “Host: 124.223.90.51:8085”

text = “name=hello&password=1234”;

2. 如何获取Method/URI/Http_Version?

std::stringstream ss(req_header[0]);  
ss >> method >> url >> http_version;

① std::stringstream ss(req_header[0])

  • std::stringstream 是 C++ 的 字符串流(string stream),类似 std::cin,可以像读取标准输入一样 按空格分割字符串。
  • 这里 req_header[0]"GET /index.html HTTP/1.1",将其存>入 ss 后,ss 变成了一个可以逐个提取单词的输入流。

② ss >> method >> url >> http_version;

  • ss >> method → 提取 “GET”,存入 method。
  • ss >> url → 提取 “/index.html”,存入 url。
  • ss >> http_version → 提取 “HTTP/1.1”,存入 http_version。

最终:
在这里插入图片描述

3. 如何根据客户端的输入确定要访问哪个文件?

首先定义:

const std::string wwwroot = "./wwwroot";

./wwwroot路径是HTTP的根目录,所有的文件都放在根目录下,如果客户端不指定访问具体的哪个文件,那么默认访问根目录。

        file_path = wwwroot;
        if (url == "/" || url == "/index.html")  //根目录
        {
            file_path += "/";
            file_path += homepage;
        }
        else
        {
            file_path += url;
        }

通过上面的代码确定file_path的值,也就可以精准访问到具体的文件。

4. 如何读取html文件的内容?

之前确定了file_path的值,也就是确定了读取的具体的文件,接下来就是进入到文件内部读取文件的内容。

std::ifstream in(htmlpath, std::ios::binary);
  • std::ifstream 打开文件,std::ios::binary 以 二进制模式 读取(防止换行符转换)。
  • 如果 htmlpath 指定的文件 不存在或打不开,流对象 in 不会打开。
in.seekg(0, std::ios_base::end);
auto len = in.tellg();
in.seekg(0, std::ios_base::beg);

  • seekg(0, std::ios_base::end); → 将文件指针移动到文件末尾,这样 tellg() 可以获取 文件大小。
  • len = in.tellg(); → 获取文件的长度(字节数)。
  • seekg(0, std::ios_base::beg); → 将文件指针移动回文件开头,准备读取内容。
std::string content;
content.resize(len);
  • 创建字符串 content,并分配 len 个字符的空间,以存放 HTML 文件的内容。
in.read((char*)content.c_str(), content.size());
  • 读取文件内容到 content:
  • in.read() 读取 content.size() 个字符,并存入 content 中。
  • content.c_str() 获取 std::string 的底层 C 风格字符数组的指针,保证数据存储正确

5. 为什么要采用二进制的读法?

因为读取的不一定是html文件,有可能是图片,视频。如果是普通的read方法,可能无法读取图片资源。

6. 如何返回HTTP响应?
在这里插入图片描述
1. 根据读到的内容判断状态码:

if (text.empty())
            {
                ok = false;
                std::string err_html = wwwroot;
                err_html += "/";
                err_html += "err.html";
                text = ReadHtmlContent(err_html);
            }
            
            //返回响应的过程
            std::string response_lines;
            if (ok)
            {
                response_lines = "HTTP/1.0 200 OK\r\n";
            }
            else 
            {
                response_lines = "HTTP/1.0 404 Not Found\r\n";
            }

2. 添加Key:Value部分:

            std::string response_header = "Content-Length: ";
            response_header += std::to_string(text.size());
            response_header += "\r\n";
            response_header += "Content-Type: text/html\r\n";
            response_header += "\r\n";
            response_header += "Set-Cookie : name=haha&&passds=12345";
            response_header += "\r\n";

3.添加空行部分

std::string blank_lines = "\r\n";

4.整理所有的内容返回给客户端

            std::string response = response_lines;
            response += response_header;
            response += blank_lines;
            response += text;

            //将内容发送给客户端  -- 响应
            send(sockfd, response.c_str(), response.size(), 0);

总结

HttpServer.hpp的内容如下:

#pragma once

#include <iostream>
#include <pthread.h>
#include "Socket.hpp"
#include <string>
#include <vector>
#include <fstream>
#include <sstream>


static const int defaultport = 8085;

const std::string seq = "\r\n";
const std::string wwwroot = "./wwwroot";
const std::string homepage = "index.html";

class HttpServer;

class ThreadData
{
public:
    ThreadData(int fd, HttpServer *ts)
        :sockfd(fd)
        ,httpsvr(ts)
    {}
public:
    int sockfd;
    HttpServer *httpsvr;
};

class HttpRequest
{
public:

    //反序列化 -- 将从客户端读到的http请求push_back进req_header中
    void Deserialize(std::string req)
    {
        while(true)
        {
            ssize_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());
        }

        //req去掉前面的内容之后,剩下的全是文本
        text = req;
    }
    
    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 << "file path : " << file_path << std::endl;
        std::cout << "text : " << text << std::endl;
    }

    void Parse()
    {
        std::stringstream ss(req_header[0]);
        ss >> method >> url >> http_version;   //用stringstream将method等直接分割了
        file_path = wwwroot;
        if (url == "/" || url == "/index.html")  //根目录
        {
            file_path += "/";
            file_path += homepage;
        }
        else
        {
            file_path += url;
        }

        // auto pos = file_path.rfind(".");
        // if (pos == std::string::npos)

    }
public:
    std::vector<std::string> req_header;  //请求
    std::string text;

    //解析之后的结果  --> 这是http的请求报文的格式
    std::string method;
    std::string url;
    std::string http_version;
    std::string file_path;
};

class HttpServer
{
public:
    HttpServer(int port = defaultport)
    :_port(defaultport)
    {}

    bool Start()
    {
        //创建套接字
        _listensock.Socket();
        _listensock.Bind(_port);
        _listensock.Listen();

        for (;;)
        {
            std::string clientip;
            uint16_t clientport;
            int sockfd = _listensock.Accept(&clientip, &clientport);

            pthread_t tid;
            ThreadData *td = new ThreadData(sockfd, this);
            td->sockfd = sockfd;
            pthread_create(&tid, nullptr, ThreadRun, td);
        }
    }   

    static void* ThreadRun(void* args)
    {
        pthread_detach(pthread_self());
        ThreadData *td = static_cast<ThreadData*>(args);
        // char buffer[10240];
        // //ssize_t n = read(?; buffer, sizeof(buffer - 1));  可以使用read
        
        // //也可以使用recv
        // ssize_t n = recv(td->sockfd, buffer, sizeof(buffer - 1), 0);
        // if (n > 0)
        // {
        //     buffer[n] = 0;
        //     std::cout << buffer;
        // }

        HandlerHttp(td->sockfd, td->httpsvr);
        delete td;
        return nullptr;
    }

    //固定版本
    // static void HanderHttp(int sockfd, HttpServer *httpsvr)
    // {
    //     char buffer[10240];
    //     ssize_t n = recv(sockfd, buffer, sizeof(buffer) - 1, 0);   //在这里,不能保证读到了完整的http请求
    //     if (n > 0)
    //     {
    //         buffer[n] = 0;
    //         std::cout << buffer;

    //         //返回相应的过程
    //         std::string text = "mayue is a pig! xixi~";
    //         std::string response_lines = "HTTP/1.0 200 OK\r\n";
    //         std::string response_header = "Content-Length: ";
    //         response_header += std::to_string(text.size());
    //         response_header += "\r\n";
    //         std::string blank_lines = "\r\n";

    //         std::string response = response_lines;
    //         response += response_header;
    //         response += blank_lines;
    //         response += text;

    //         //将内容发送给发送方  -- 响应
    //         send(sockfd, response.c_str(), response.size(), 0);
    //     }
        
    //     close(sockfd);
    // }

    //读取文件内容
    static std::string ReadHtmlContent(const std::string &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());

        in.close();

        return content;
    }

    //显示不同的html,进行处理
    static void HandlerHttp(int sockfd, HttpServer *httpsvr)
    {
        char buffer[10240];
        ssize_t n = recv(sockfd, buffer, sizeof(buffer) - 1, 0);   //在这里,不能保证读到了完整的http请求
        if (n > 0)
        {
            buffer[n] = 0;
            std::cout << buffer << std::endl;  //假设我们读到的是一个完整的http请求

            HttpRequest req;
            req.Deserialize(buffer);
            req.Parse();

            std::string text;
            bool ok = true; 
            text = ReadHtmlContent(req.file_path);  //读取客户端请求的文件 --> "./index.html"

            if (text.empty())
            {
                ok = false;
                std::string err_html = wwwroot;
                err_html += "/";
                err_html += "err.html";
                text = ReadHtmlContent(err_html);
            }
            
            //返回响应的过程
            std::string response_lines;
            if (ok)
            {
                response_lines = "HTTP/1.0 200 OK\r\n";
            }
            else 
            {
                response_lines = "HTTP/1.0 404 Not Found\r\n";
            }

            std::string response_header = "Content-Length: ";
            response_header += std::to_string(text.size());
            response_header += "\r\n";
            response_header += "Content-Type: text/html\r\n";
            response_header += "\r\n";
            response_header += "Set-Cookie : name=haha&&passds=12345";
            response_header += "\r\n";
            
            std::string blank_lines = "\r\n";

            std::string response = response_lines;
            response += response_header;
            response += blank_lines;
            response += text;

            //将内容发送给客户端  -- 响应
            send(sockfd, response.c_str(), response.size(), 0);
        }
        
        close(sockfd);
    }

    ~HttpServer()
    {}

private:
    Sock _listensock;
    uint16_t _port;
};

HttpServer.cc

#include "Httpserver.hpp"
#include <iostream>
#include <memory>

using namespace std;

int main()
{
    //std::unique<HttpServer> svr(new HttpServer());
    HttpServer *svr = new HttpServer();
    svr->Start();

    return 0;
}

在这里插入图片描述

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

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

相关文章

优雅管理Python2 and python3

python2 和 python3&#xff0c; 由于没有像其他软件的向下兼容&#xff0c;必须同时安装Python2 和Python3 &#xff0c;介绍在linux和windows下优雅管理。 一、linux中安装Python2和Python3 linux 中用conda 创建虚拟环境&#xff0c;来管理不同版版工具 由于主流使用Python3…

Python从0到100(八十六):神经网络-ShuffleNet通道混合轻量级网络的深入介绍

前言&#xff1a; 零基础学Python&#xff1a;Python从0到100最新最全教程。 想做这件事情很久了&#xff0c;这次我更新了自己所写过的所有博客&#xff0c;汇集成了Python从0到100&#xff0c;共一百节课&#xff0c;帮助大家一个月时间里从零基础到学习Python基础语法、Pyth…

大模型本地化部署(Ollama + Open-WebUI)

文章目录 环境准备下载Ollama模型下载下载Open-WebUI 本地化部署的Web图形化界面本地模型联网查询安装 Docker安装 SearXNG本地模型联网查询 环境准备 下载Ollama 下载地址&#xff1a;Ollama网址 安装完成后&#xff0c;命令行里执行命令 ollama -v查看是否安装成功。安装成…

【Linux系统】进程间通信:共享内存

认识共享内存 通过 一些系统调用&#xff0c;在物理内存中开辟一块空间&#xff0c;然后将该空间的起始地址&#xff0c;通过页表映射到两个进程的虚拟地址空间的共享区中&#xff0c;这样不就共享了一块空间吗&#xff01;&#xff01;&#xff01; 这种技术就是共享内存&am…

渗透测试之WAF组合条件绕过方式手法详解以及SQL注入参数污染绕过

目录 组合绕过waf ​先看一些语句 绕过方式 我给出的注入语句是&#xff1a; 这里要注意的几点是&#xff1a; 组合绕过方式 完整过狗注入语句集合 http请求分块传输方法 其它方式绕过 http参数污染绕过waf 面试题:如何参数污染绕过waf 可以通过http参数污染绕过wa…

oracl:多表查询>>表连接[内连接,外连接,交叉连接,自连接,自然连接,等值连接和不等值连接]

SQL&#xff08;Structured Query Language&#xff0c;结构化查询语言&#xff09;是一种用于管理和操作关系数据库的标准编程语言。 sql分类: 数据查询语言&#xff08;DQL - Data Query Language&#xff09; 查询的关键词 select 多表查询>>表连接 表连接: 把2个…

ARIMA详细介绍

ARIMA&#xff08;AutoRegressive Integrated Moving Average&#xff0c;自回归积分滑动平均模型&#xff09;是一种用于时间序列分析和预测的统计模型。它结合了自回归&#xff08;AR&#xff09;、差分&#xff08;I&#xff09;和移动平均&#xff08;MA&#xff09;三种方…

飞致云开源社区月度动态报告(2025年1月)

自2023年6月起&#xff0c;中国领先的开源软件公司飞致云以月度为单位发布《飞致云开源社区月度动态报告》&#xff0c;旨在向广大社区用户同步飞致云旗下系列开源软件的发展情况&#xff0c;以及当月主要的产品新版本发布、社区运营成果等相关信息。 飞致云开源运营数据概览&…

【搜索回溯算法篇】:拓宽算法视野--BFS如何解决拓扑排序问题

✨感谢您阅读本篇文章&#xff0c;文章内容是个人学习笔记的整理&#xff0c;如果哪里有误的话还请您指正噢✨ ✨ 个人主页&#xff1a;余辉zmh–CSDN博客 ✨ 文章所属专栏&#xff1a;搜索回溯算法篇–CSDN博客 文章目录 一.广度优先搜索&#xff08;BFS&#xff09;解决拓扑排…

WPS怎么使用latex公式?

1、下载并安装mathtype https://blog.csdn.net/weixin_43135178/article/details/125143654?sharetypeblogdetail&sharerId125143654&sharereferPC&sharesourceweixin_43135178&spm1011.2480.3001.8118 2、将mathtype嵌入在WPS MathType面板嵌入器,免费工具…

简单的爱心跳动表白网页(附源码)

一&#xff1a;准备工作 在开始之前&#xff0c;确保已经具备基础的 HTML、CSS 和 JavaScript 知识。同时&#xff0c;也要准备好一个代码编辑器&#xff0c;比如 VS Code 或 Sublime Text。接下来&#xff0c;我们需要创建三个文件&#xff1a;index.html、styles.css 和 scr…

【AI】DeepSeek 概念/影响/使用/部署

在大年三十那天&#xff0c;不知道你是否留意到&#xff0c;“deepseek”这个词出现在了各大热搜榜单上。这引起了我的关注&#xff0c;出于学习的兴趣&#xff0c;我深入研究了一番&#xff0c;才有了这篇文章的诞生。 概念 那么&#xff0c;什么是DeepSeek&#xff1f;首先百…

【4Day创客实践入门教程】Day3 实战演练——桌面迷你番茄钟

Day3 实战演练——桌面迷你番茄钟 目录 Day3 实战演练——桌面迷你番茄钟1. 选择、准备元件、收集资料2. 硬件搭建3.编写代码 Day0 创想启程——课程与项目预览Day1 工具箱构建——开发环境的构建Day2 探秘微控制器——单片机与MicroPython初步Day3 实战演练——桌面迷你番茄钟…

AndroidCompose Navigation导航精通1-基本页面导航与ViewPager

文章目录 前言基本页面导航库依赖导航核心部件简单NavHost实现ViewPagerPager切换逻辑图阐述Pager导航实战前言 在当今的移动应用开发中,导航是用户与应用交互的核心环节。随着 Android Compose 的兴起,它为开发者提供了一种全新的、声明式的方式来构建用户界面,同时也带来…

Node.js——body-parser、防盗链、路由模块化、express-generator应用生成器

个人简介 &#x1f440;个人主页&#xff1a; 前端杂货铺 &#x1f64b;‍♂️学习方向&#xff1a; 主攻前端方向&#xff0c;正逐渐往全干发展 &#x1f4c3;个人状态&#xff1a; 研发工程师&#xff0c;现效力于中国工业软件事业 &#x1f680;人生格言&#xff1a; 积跬步…

C语言指针专题四 -- 多级指针

目录 1. 多级指针的核心原理 1. 多级指针的定义 2. 内存结构示意图 3. 多级指针的用途 2. 编程实例 实例1&#xff1a;二级指针操作&#xff08;修改一级指针的值&#xff09; 实例2&#xff1a;动态二维数组&#xff08;二级指针&#xff09; 实例3&#xff1a;三级指…

深度学习的应用

目录 一、机器视觉 1.1 应用场景 1.2 常见的计算机视觉任务 1.2.1 图像分类 1.2.2 目标检测 1.2.3 图像分割 二、自然语言处理 三、推荐系统 3.1 常用的推荐系统算法实现方案 四、图像分类实验补充 4.1 CIFAR-100 数据集实验 实验代码 4.2 CIFAR-10 实验代码 深…

RabbitMQ 多种安装模式

文章目录 前言一、Windows 安装 RabbitMq1、版本关系2、Erlang2.1、下载安装 Erlang 23.12.2、配置 Erlang 环境变量 3、RabbitMQ3.1、下载安装 RabbitMQ 3.8.93.2、环境变量3.3、启动RabbitMQ 管理插件3.3、RabbitMQ3.4、注意事项 二、安装docker1、更新系统包&#xff1a;2、…

吴恩达深度学习——有效运作神经网络

内容来自https://www.bilibili.com/video/BV1FT4y1E74V&#xff0c;仅为本人学习所用。 文章目录 训练集、验证集、测试集偏差、方差正则化正则化参数为什么正则化可以减少过拟合Dropout正则化Inverted Dropout其他的正则化方法数据增广Early stopping 归一化梯度消失与梯度爆…

DDD - 微服务架构模型_领域驱动设计(DDD)分层架构 vs 整洁架构(洋葱架构) vs 六边形架构(端口-适配器架构)

文章目录 引言1. 概述2. 领域驱动设计&#xff08;DDD&#xff09;分层架构模型2.1 DDD的核心概念2.2 DDD架构分层解析 3. 整洁架构&#xff1a;洋葱架构与依赖倒置3.1 整洁架构的核心思想3.2 整洁架构的层次结构 4. 六边形架构&#xff1a;解耦核心业务与外部系统4.1 六边形架…