网络协议栈--应用层--HTTP协议

news2024/11/19 9:38:15

目录

  • 本节重点
    • 理解应用层的作用, 初识HTTP协议
  • 一、应用层
  • 二、HTTP协议
    • 2.1 认识URL
    • 2.2 urlencode和urldecode
    • 2.3 HTTP协议格式
    • 2.4 HTTP的方法
    • 2.4 HTTP的状态码
    • 2.5 HTTP常见的Header属性
  • 三、最简单的HTTP服务器
    • 3.1 HttpServer.hpp
    • 3.2 HttpServer.cc
    • 3.3 HttpClient.cc
    • 3.4 log.hpp
    • 3.5
    • 3.6 makefile
    • 3.7 wwwroot目录下的资源
  • 四、HTTP协议内容一览图

本节重点

理解应用层的作用, 初识HTTP协议

一、应用层

程序员们写的一个个解决我们实际问题, 满足我们日常需求的网络程序, 都是在应用层。

二、HTTP协议

虽然说, 应用层协议是程序员自己定的.
但实际上, 已经有大佬们定义了一些现成的, 又非常好用的应用层协议, 供我们直接参考使用. HTTP(超文本传输协议)就是其中之一。

2.1 认识URL

平时我们俗称的 “网址” 其实就是说的 URL。
在这里插入图片描述

2.2 urlencode和urldecode

像 / ? : 等这样的字符, 已经被url当做特殊意义理解了。 因此这些字符不能随意出现.比如, 某个参数中需要带有这些特殊字符, 就必须先对特殊字符进行转义。
转义的规则如下:
将需要转码的字符转为16进制,然后从右到左,取4位(不足4位直接处理),每2位做一位,前面加上%,编码成%XY格式。
在这里插入图片描述
“+” 被转义成了 “%2B”
urldecode就是urlencode的逆过程;

2.3 HTTP协议格式

HTTP请求:
在这里插入图片描述
(1)请求行: [请求方法] + [url] + [HTTP版本]。

(2)请求报头(Header): 请求的属性, 冒号分割的键值对;每组属性之间使用\n分隔;

(3)空行:遇到单单一个\n空行表示Header部分结束.

(4)请求正文: 空行后面的内容都是Body. Body允许为空字符串. 如果Body存在, 则在Header中会有一个Content-Length属性来标识Body的长度;

HTTP响应:
在这里插入图片描述
(1)状态行: [HTTP版本] + [状态码] + [状态码描述]
(2)响应报头(Header): 请求的属性, 冒号分割的键值对;每组属性之间使用\n分隔;
(3)空行:遇到单单一个\n空行表示Header部分结束。
(4)响应正文:空行后面的内容都是Body. Body允许为空字符串. 如果Body存在, 则在Header中会有一个Content-Length属性来标识Body的长度; 如果服务器返回了一个html页面, 那么html页面内容就是在body中。

2.4 HTTP的方法

在这里插入图片描述
其中最常用的就是GET方法和POST方法。

2.4 HTTP的状态码

在这里插入图片描述
最常见的状态码, 比如 200(OK), 404(Not Found), 403(Forbidden), 302(Redirect, 重定向), 504(Bad Gateway)

2.5 HTTP常见的Header属性

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

三、最简单的HTTP服务器

实现一个最简单的HTTP服务器, 只在网页上输出 “hello world”; 只要我们按照HTTP协议的要求构造数据, 就很容易能做到;
我们以下的服务器是添加了一点前端的代码的:

3.1 HttpServer.hpp

#pragma once

#include <iostream>
#include "Socket.hpp"
#include <functional>
#include <fstream>
#include <vector>
#include <string>
#include <sstream>
#include <unordered_map>

// HTTP协议可以以"\r\n"作为行分隔符,也可以是"\n"
const std::string sep = "\r\n";

// 传说中的web根目录
const std::string wwwroot = "./wwwroot";

// 服务器主页
const std::string homepage = "index.html";

const uint16_t DEFAULT_PORT = 8080;

class HttpServer;

class ThreadData
{
public:
    ThreadData(int sockfd, HttpServer *svr)
        : _sockfd(sockfd), _svr(svr)
    {
    }

public:
    int _sockfd;
    HttpServer *_svr;
};

class Request
{
public:
    // 反序列化
    void Deserialize(std::string req)
    {
        while (true)
        {
            // 用行分隔符"\r\n",分割请求报头中的属性信息
            auto pos = req.find(sep);
            if (pos == string::npos)
            {
                break;
            }
            std::string tmp = req.substr(0, pos);
            if (tmp.empty())
            {
                // 说明读取到了空行,即报头已经读完了
                req.erase(pos, sep.size());
                break;
            }
            _req_head.push_back(tmp);
            // 没获取一条属性信息记得在原报头中删除
            req.erase(0, pos + sep.size());
        }
        // 取出报头之后得到的剩余的就是请求正文
        _body = req;
    }
    // 解析http协议
    void Parse()
    {
        std::string req_line = _req_head[0];
        //stringstream默认是以空格作为分隔符的
        std::stringstream ss(req_line);
        //必须按顺序
        ss >> _method >> _url >> _http_version;

        // 拼接资源的路径
        _filepath = wwwroot;
        if (_url == "/" || _url == "/index.html")
        {
            _filepath += "/";
            _filepath += homepage;
        }
        else
        {
            _filepath += _url;
        }

        //从后往前找到'.',从而找出后缀
        auto pos = _filepath.rfind('.');
        if (pos == string::npos)
        {
            _suffix = ".html";
        }
        else
        {
            _suffix = _filepath.substr(pos);
        }
    }

    //用来调试的
    void DebugPrint()
    {
        for (auto &line : _req_head)
        {
            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: " << _filepath << std::endl;
        std::cout << _body << std::endl;
    }

    std::string GetFilePath()
    {
        return _filepath;
    }

    std::string GetFileSuffix()
    {
        return _suffix;
    }

private:
    std::string _method;                     //请求方法
    std::string _url;                        //请求的资源的url
    std::string _http_version;               //http协议版本号
    std::vector<std::string> _req_head;      //请求报头:包括请求行、报头属性信息
    std::string _body;                       //请求正文
    std::string _filepath;                   //文件的路径,从web根目录开始,即wwwroot/url
    std::string _suffix;                     //url的后缀
};

class HttpServer
{
public:
    HttpServer(const uint16_t &port = DEFAULT_PORT)
        : _port(port)
    {
        //.html对应的网页是文本类型
        _content_type[".html"] = "text/html";
        //  _content_type[".html"] = "application/json";
        //.png对应的网页是图片
        _content_type[".png"] = "image/png";
    }

    ~HttpServer()
    {
    }

    void InitHttpServer()
    {
        // 1、创建套接字
        _listen_sock.Socket();

        // 2、绑定
        _listen_sock.Bind(_port);

        // 3、监听
        _listen_sock.Listen();
    }

    static string ReadIndexHtml(const std::string &path)
    {
        // 有坑?
        std::string str;
        //注意,这里一定要用二进制的方式去读取,否则类似于图片的文件就有可能会读取出
        //错从而在访问的时候看不到图片
        ifstream in(path, std::ios::binary);
        if (!in.is_open())
        {
            return "";
        }
        
        //对于seekg函数,0表示偏移量,std::ios_base::end是基准,
        //意思是:设置当前位置相对于最后一个位置的偏移量是0,
        //说明当前指针的位置就指向文件内容的最后一个位置
        in.seekg(0, std::ios_base::end);
        //对于tellg函数,是获取当前读取位置的,因为上面已经设置了
        //当前指针指向的位置是文件内容的最后一个位置,所以当前位置
        //的数值就等于文件内容的大小,即一共有len个自己的内容
        int len = in.tellg();
        //同上,即设置指针指向文件的开始位置
        in.seekg(0, std::ios_base::beg);
        std::string content;
        content.resize(len);
        //把内容读取到content中
        in.read((char *)content.c_str(), len);

        in.close();
        return content;
    }

    //url的后缀转换成对应的后缀描述,用于构建响应
    std::string SuffixToDesc(const std::string &suffix)
    {
        auto ret = _content_type.find(suffix);
        if (ret == _content_type.end())
        {
            //如果从类型中没有找到,那就统一当成是.html后缀
            //返回对应的文本类型的描述"text/html"
            return _content_type[".html"];
        }
        else
        {
            return _content_type[suffix];
        }
    }

    static void *Handler(void *args)
    {
        //线程分离
        pthread_detach(pthread_self());

        ThreadData *ptd = static_cast<ThreadData *>(args);
        int sockfd = ptd->_sockfd;

        while (true)
        {
            char buffer[10240] = {0};
            ssize_t n = recv(sockfd, buffer, sizeof(buffer) - 1, 0);
            if (n > 0)
            {
                buffer[n] = '\0';
                std::cout << buffer << std::endl;

                //解析http协议请求
                Request req;
                req.Deserialize(buffer);
                req.Parse();
                req.DebugPrint();

                // 构建http响应
                std::string response;
                bool ok = true;
                //根据请求的url,读取对应的文件内容作为响应的正文
                std::string response_body = ReadIndexHtml(req.GetFilePath());
                if (response_body.empty())
                {
                    ok = false;
                    std::string err_html = wwwroot;
                    err_html += '/';
                    err_html += "err.html";
                    response_body = ReadIndexHtml(err_html);
                }

                // 状态行
                std::string state_line;
                if (ok)
                {
                    state_line = "HTTP/1.0 200 OK\r\n";
                }
                else
                {
                    state_line = "HTTP/1.0 404 Not Found\r\n";
                }
                response += state_line;

                //做重定向的状态行
                //response = "HTTP/1.0 302 Found\r\n";

                // 响应报头
                std::string response_head;
                //属性1:content-lenth
                std::string content_lenth = "Content-Lenth: ";
                content_lenth + to_string(response_body.size());
                //属性2:content-type
                std::string content_type = "Content-Type: ";
                content_type += ptd->_svr->SuffixToDesc(req.GetFileSuffix());
                response_head += content_lenth;
                response_head += "\r\n";
                response_head += content_type;
                response_head += "\r\n";
                //属性3:set cookie
                response_head += "Set-Cookie: name=kobe&&passwd=123456";
                response_head += "\r\n";
                //属性4:Location,设置重定向时需要访问的网址
                // response_head += "Location: http://www.qq.com/";
                // response_head += "\r\n";
                response += response_head;
                // 空行
                response += "\r\n";
                // 正文
                response += response_body;
                ssize_t n = send(sockfd, response.c_str(), response.size(), 0);
            }
            else if (n == 0)
            {
                log(Info, "client quit...,关闭连接:%d", sockfd);
                close(sockfd);
                return nullptr;
            }
            else if (n < 0)
            {
                std::cout << "n=" << n << std::endl;
                log(Error, "recv error,关闭连接:%d", sockfd);
                close(sockfd);

                return nullptr;
            }
        }
        log(Info, "服务器退出,关闭连接:%d", sockfd);
        close(sockfd);
        return nullptr;
    }

    void Start()
    {
        while (true)
        {
            std::string client_ip;
            uint16_t client_port;
            int sockfd = _listen_sock.Accept(client_ip, client_port);
            if (sockfd < 0)
            {
                continue;
            }
            pthread_t tid;
            ThreadData td(sockfd, this);

            // 创建线程
            pthread_create(&tid, nullptr, Handler, (void *)(&td));
        }
    }

private:
    Sock _listen_sock;
    uint16_t _port;
    std::unordered_map<std::string, std::string> _content_type;
};

3.2 HttpServer.cc

#include "HttpServer.hpp"
#include <memory>

void Usage(const std::string& proc)
{
    std::cout<<"\t\n"<<std::endl;
    std::cout<<"Usage: "<<proc<<" server_port[>=1024]"<<std::endl<<std::endl;
}

int main(int argc, char* argv[])
{
    if(argc!=2)
    {
        Usage(argv[0]);
        exit(1);
    }
    uint16_t server_port=(uint16_t)stoi(argv[1]);
    std::unique_ptr<HttpServer> svr(new HttpServer(server_port));
    svr->InitHttpServer();
    svr->Start();

    return 0;
}

3.3 HttpClient.cc

提示一下:如果直接用HttpClient.cc访问服务器,那么需要按照HTTP协议的标准格式构建报文才能正确地返回,否则服务器会发生段错误的。

#include "HttpServer.hpp"
#include "Socket.hpp"

int main()
{
    Sock sock;
    sock.Socket();
    sock.Connect("43.138.156.240",8081);

    string buffer;
    while(true)
    {
        std::cout<<"Please Enter:";
        std::getline(std::cin,buffer);
        send(sock.Sockfd(),buffer.c_str(),buffer.size(),0);
        sleep(1);

        char tmp[10240]={0};
        recv(sock.Sockfd(),tmp,sizeof(tmp),0);
        std::cout<<tmp<<std::endl;
    }

    return 0;
}

3.4 log.hpp

#pragma once

#include <iostream>
using namespace std;
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <string>
#include <time.h>
#include <stdarg.h>

// 日志等级
#define Info 0
#define Debug 1
#define Warning 2
#define Error 3
#define Fatal 4

#define Screen 1
#define OneFile 2
//向多个文件打印
#define Classfile 3
#define SIZE 1024

#define LogFile "log.txt"



class Log
{
public:
    Log()
    {
        printMethod = Screen;
        path = "./log/";
    }

    void Enable(int mothod)
    {
        printMethod = mothod;
    }

    string LevelToString(int level)
    {
        switch (level)
        {
        case Info:
        {
            return "Info";
        }
        case Debug:
        {
            return "Debug";
        }
        case Warning:
        {
            return "Warning";
        }
        case Error:
        {
            return "Error";
        }
        case Fatal:
        {
            return "Fatal";
        }
        default:
        {
            return "None";
        }
        }
    }

    void printlog(int level,const string& logtxt)
    {
        switch(printMethod)
        {
        case Screen:
        {
            cout<<logtxt<<endl;
            break;
        }
        case OneFile:
        {
            PrintOneFile(LogFile,logtxt);
            break;
        }
        case Classfile:
        {
            PrintClassfile(level,logtxt);
            break;
        }
        default:
        {
            break;
        }
        }
    }

    void PrintOneFile(const string& logname,const string& logtxt)
    {
        string _logname=path+logname;
        int fd=open(_logname.c_str(),O_WRONLY|O_CREAT|O_APPEND,0666);
        if(fd<0)
        {
            perror("open fail");
            return;
        }

        write(fd,logtxt.c_str(),logtxt.size());

        close(fd);

    }

    void PrintClassfile(int level,const string& logtxt)
    {
        string filename=LogFile;
        filename+='.';
        filename+=LevelToString(level);
        PrintOneFile(filename,logtxt);
    }

    void operator()(int level,const char* format,...)
    {
        time_t t=time(nullptr);
        struct tm* ctime=localtime(&t);
        char leftbuffer[SIZE];
        snprintf(leftbuffer,SIZE,"[%s][%d-%d-%d %d:%d:%d]",LevelToString(level).c_str(),
        ctime->tm_year+1900,ctime->tm_mon+1,ctime->tm_mday,
        ctime->tm_hour,ctime->tm_min,ctime->tm_sec);

        va_list s;
        va_start(s,format);
        char rightbuffer[SIZE]={0};
        vsnprintf(rightbuffer,SIZE,format,s);
        va_end(s);


        char logtxt[SIZE*2];
        snprintf(logtxt,sizeof(logtxt),"%s %s",leftbuffer,rightbuffer);

        printlog(level,logtxt);
    }

    ~Log()
    {
    }

private:
    // 打印方法
    int printMethod;
    string path;
};

//定义一个全局的log
Log log;

3.5

#pragma once

#include <iostream>
using namespace std;
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "log.hpp"
#include <unistd.h>
#include <strings.h>
#include <cstring>
#include <string>

int backlog = 10;

enum
{
    SockErr = 2,
    BindErr,
    ListenErr,
    ConnectErr,
};

class Sock
{
public:
    Sock()
        : _sockfd(-1)
    {
    }
    ~Sock()
    {
        if(_sockfd>0)
        {
            close(_sockfd);
        }
    }

    // 创建套接字
    void Socket()
    {
        _sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (_sockfd < 0)
        {
            log(Fatal, "socket failed,errno:%d,errstring:%s", errno, strerror(errno));
            exit(SockErr);
        }
        int opt=1;
        if(setsockopt(_sockfd,SOL_SOCKET,SO_REUSEADDR|SO_REUSEPORT,&opt,sizeof(opt))<0)
        {
            log(Warning, "setsockopt failed, sockfd:%d", _sockfd);
        }
        log(Info, "setsockopt successed, sockfd:%d", _sockfd);
        log(Info, "socket successed, sockfd:%d", _sockfd);
    }
    // 绑定
    void Bind(const uint16_t &serverPort)
    {
        struct sockaddr_in local;
        bzero(&local, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(serverPort);
        local.sin_addr.s_addr = INADDR_ANY;

        if (bind(_sockfd, (struct sockaddr *)(&local), sizeof(local)) < 0)
        {
            log(Fatal, "bind failed,errno:%d,errstring:%s", errno, strerror(errno));
            exit(BindErr);
        }
        log(Info, "bind successed...");
    }
    // 监听
    void Listen()
    {
        if (listen(_sockfd, backlog) < 0)
        {
            log(Fatal, "set listen state failed,errno:%d,errstring:%s", errno, strerror(errno));
            exit(ListenErr);
        }
        log(Info, "set listen state successed");
    }
    //获取连接
    int Accept(string& clientip,uint16_t& clientport)
    {
        struct sockaddr_in client;
        socklen_t len=sizeof(client);
        bzero(&client,sizeof(client));

        int sockfd=accept(_sockfd,(struct sockaddr*)(&client),&len);
        if(sockfd<0)
        {
            log(Warning, "accept new link failed,errno:%d,errstring:%s", errno, strerror(errno));
            return -1;
        }
        log(Info,"accept a new link...,sockfd:%d",sockfd);

        clientip=inet_ntoa(client.sin_addr);
        clientport=(uint16_t)(ntohs(client.sin_port));

        return sockfd;
    }
    // 连接
    void Connect(const string &serverIp, const uint16_t &serverPort)
    {
        struct sockaddr_in server;
        bzero(&server, sizeof(server));
        server.sin_family = AF_INET;
        server.sin_addr.s_addr = inet_addr(serverIp.c_str());
        server.sin_port = htons(serverPort);

        if (connect(_sockfd, (struct sockaddr *)(&server), sizeof(server)) < 0)
        {
            log(Fatal, "connect server failed,errno:%d,errstring:%s", errno, strerror(errno));
            exit(ConnectErr);
        }
        log(Info, "connect server succeeded...");
    }

    void Close()
    {
        if(_sockfd>0)
        {
            close(_sockfd);
        }
    }

    int Sockfd()
    {
        return _sockfd;
    }

private:
    int _sockfd;
};

3.6 makefile

.PHONY:all
all:http_server http_client

http_server:HttpServer.cc
	g++ -o $@ $^ -std=c++11 -lpthread 

http_client:HttpClient.cc
	g++ -o $@ $^ -std=c++11 

.PHONY:clean
clean:
	rm -f http_server http_client

3.7 wwwroot目录下的资源

自行创建wwwroot目录并把资源按要求放到目录下:
web根目录下的资源

在这里插入图片描述
备注:
此处我们使用 8081 端口号启动了HTTP服务器. 虽然HTTP服务器一般使用80端口,但这只是一个通用的习惯. 并不是说HTTP服务器就不能使用其他的端口号.使用Edge测试我们的服务器时, 可以看到服务器打出的请求中还有一个
GET /favicon.ico HTTP/1.1 这样的请求。favicon.ico是用来设置网页上的小图标的:
在这里插入图片描述

可以试试把返回的状态码改成404, 403, 504等, 看浏览器上分别会出现什么样的效果。

四、HTTP协议内容一览图

在这里插入图片描述

以上就是今天想要跟大家分享的关于HTTP协议的所有内容了,你学会了吗?如果感觉到有所收获的话,那就点点小心心,再点点关注呗,后期还会持续更新有关Linux网络编程的相关知识哦,我们下期见!!!!!

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

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

相关文章

MySQL之表基本操作及重要概念

前言 表就相当于文件夹中的excel文件&#xff0c;表中的每一条记录就是excel中的一条记录&#xff0c;字段就是表格的表头&#xff0c;记录就是表格中的每一行数据本文将详细介绍表相关操作、mysql存储引擎、字段数据类型及约束条件等知识。 存储引擎 计算机中存储的文件格式…

Discuz IIS上传附件大于28M失败报错Upload Failed.修改maxAllowedContentLength(图文教程)

下图&#xff1a;Discuz X3.5的系统信息&#xff0c;上传许可为1024MB(1GB) 论坛为局域网论坛&#xff0c;仅供内部同事交流使用&#xff01; 使用官方最新的Discuz! X3.5 Release 20231221 UTF-8 下图&#xff1a;选择上传附件&#xff08;提示可以最大上传100M&#xff09;…

新模型Claude 3实测!各项能力超强,确实比GPT-4好用

2024最新教程Claude 3注册账号&#xff0c;小白教程包教包会 过去不到一个月&#xff0c;OpenAI 扔出 Sora 这个重磅炸弹后成为全球焦点&#xff0c;不断推出的视频更是赚足眼球、热度不减。昨晚&#xff0c;Anthropic 突然惊喜上线&#xff0c;时隔八个月携着 Claude 3 走来&…

【Java EE 】认识文件与Java文件操作

目录 &#x1f340;认识文件&#x1f338;树型结构组织 和 目录&#x1f338;文件路径&#xff08;Path&#xff09;&#x1f338;其他知识 &#x1f333;Java 中操作文件&#x1f338;File 概述&#x1f33b;属性&#x1f33b;构造方法&#x1f33b;方法 &#x1f338;代码示例…

西安雁塔未来人工智能计算中心算力成本分析

先看一例旧闻&#xff1a;西部“最强大脑”落户雁塔——30亿亿次超算能力助力创新之城建设 其中提到一期算力为 300PFLOPS FP16&#xff08;每秒30亿亿次半精度浮点计算&#xff09;&#xff0c;项目总投资约为19亿元。 这个算力是什么概念呢&#xff1f; 我们以深度学习训练中…

100%开源大模型OLMo:代码/权重/数据集/训练全过程公开,重定义AI共享

前言 近日&#xff0c;艾伦人工智能研究所联合多个顶尖学术机构发布了史上首个100%开源的大模型“OLMo”&#xff0c;这一举措被认为是AI开源社区的一大里程碑。OLMo不仅公开了模型权重&#xff0c;还包括了完整的训练代码、数据集和训练过程&#xff0c;为后续的开源工作设立…

C语言回顾学习

一、数据类型 1.常量 2.float浮点表示 3.字符型 4.char&#xff08;大小写&#xff09; #include <stdio.h> //根据数字输出字符--int值可以直接输出为char int main() {int value;while (1){scanf("%d",&value);if(value<65||value>122){printf(&…

【Redis】RedisTemplate和StringRedisTemplate的区别

两者的关系是 StringRedisTemplate 继承 RedisTemplate 。 两者的数据是不共通的&#xff1a;也就是说 StringRedisTemplate 只能管理 StringRedisTemplate 里面的数据&#xff0c;RedisTemplate 只能管理 RedisTemplate 中的数据。 RedisTemplate 看这个类的名字后缀是 Temp…

android开发游戏加速器,Android架构组件Room功能详解

Java基础 Java Object类方法HashMap原理&#xff0c;Hash冲突&#xff0c;并发集合&#xff0c;线程安全集合及实现原理HashMap 和 HashTable 区别HashCode 作用&#xff0c;如何重载hashCode方法ArrayList与LinkList区别与联系GC机制Java反射机制&#xff0c;Java代理模式Jav…

UOS 20 安装redis 7.0.11 安装redis 7.0.11时 make命令 报错 /bin/sh: cc: command not found

UOS 20 安装redis 7.0.11 1、下载redis 7.0.112、安装redis 7.0.113、启动停止redis 7.0.114、安装过程问题记录 UOS 20 安装redis 7.0.11 安装redis 7.0.11时 make命令 报错 /bin/sh: cc: command not found、zmalloc.h:50:31: fatal error: jemalloc/jemalloc.h: No such fil…

jquery选择器有哪些

jQuery是一个功能强大的JavaScript库&#xff0c;它提供了丰富的选择器来帮助开发者更方便地选择和操作DOM元素。以下是jQuery的一些常用选择器及其示例代码&#xff1a; 1.基本选择器&#xff1a; // 通过ID选择元素 $("#myId").css("color", "red…

java实现文件上传到本地

很多时候我们都需要进行文件上传和下载的操作&#xff0c;具体怎么实现网上的代码其实也是挺多的&#xff0c;刚好我的项目中也遇到了文件上传和下载的需求&#xff0c;本篇博文具体讲解上传操作&#xff0c;下篇博文讲解下载操作。 我们具体来想一想要将一个从前端传来的文件…

一命通关二分搜索

二分法 简介 和双指针一样&#xff0c;二分法也是一种优化方法&#xff0c;或者说二分法就是双指针的一类。不过&#xff0c;二分法的思想比双指针诞生更早也更广泛&#xff0c;在我们日常生活里也无时不刻在使用二分的思想。 比如我们想回顾某些影片&#xff0c;但是只记得…

书籍推荐:ChatGPT,大模型的预训练、迁移和中间件编程学习。

前言 OpenAI 在 2022 年 11 月推出了人工智能聊天应用—ChatGPT。它具有广泛的应用场景&#xff0c;在多项专业和学术基准测试中表现出的智力水平&#xff0c;不仅接近甚至有时超越了人类的平均水平。这使得 ChatGPT 在推出之初就受到广大用户的欢迎&#xff0c;被科技界誉为人…

【Redis | 第二篇】Redis的五种数据类型和相关命令

文章目录 2.Redis的数据类型和相关命令2.1常用数据类型2.2特性和用途2.2.1字符串&#xff08;String&#xff09;2.2.2哈希&#xff08;Hash&#xff09;2.2.3列表&#xff08;List&#xff09;2.2.4集合&#xff08;Set&#xff09;2.2.5有序集合&#xff08;Sorted Set&#…

网络编程 24/3/4 作业

1、广播 发送端 #include <myhead.h> int main(int argc, const char *argv[]) {//创建套接字int sfdsocket(AF_INET,SOCK_DGRAM,0);if(sfd-1){perror("socket error");return -1;}//设置当前套接字允许广播属性int broadcast1;if(setsockopt(sfd,SOL_SOCKET…

【IO】进程间通信

A程序代码&#xff1a; #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include <errno.h> #include <fcntl.h> #include <string.h> int main(int argc, const char *argv[]) {if(mkfifo…

MySQL面试题纯享版

基础内容 1、MySQL的架构分层 2、一条 SQL 查询语句的执行流程 3、如何查看 MySQL 服务被多少个客户端连接了&#xff1f; 4、 空闲连接会一直占用着吗&#xff1f; 5、MySQL 的连接数有限制吗&#xff1f; 6、 怎么解决长连接占用内存的问题&#xff1f; 7、执行器与存储引擎…

Java 并发编程 面试题及答案整理,最新面试题

Java中的volatile关键字有什么作用&#xff1f; volatile关键字在Java中的作用包括&#xff1a; 1、保证可见性&#xff1a; 确保变量的修改对其他线程立即可见。 2、防止指令重排&#xff1a; 防止编译器对操作进行重排序&#xff0c;保证代码的执行顺序。 3、非原子性&am…

xss.haozi.me:0x0B

<svg><script>(1)</script>