当前位置: 首页 > news >正文

应用层协议 HTTP 讲解实战:从0实现HTTP 服务器

🌈 个人主页:Zfox_
🔥 系列专栏:Linux

目录

  • 一:🔥 HTTP 协议
    • 🦋 认识 URL
    • 🦋 urlencode 和 urldecode
  • 二:🔥 HTTP 协议请求与响应格式
    • 🦋 HTTP 请求
    • 🦋 HTTP 响应
    • 🦋 HTTP 的方法
    • 🦋 HTTP 的状态码
    • 🦋 HTTP 常见 Header
    • 🦋 关于 connection 报头
  • 三:🔥 实现 HTTP 服务器
    • 🦋 完整代码移步我的Gitee仓库
  • 四:🔥 附录
    • 🦋 HTTP 历史及版本核心技术与时代背景
    • 🦋 HTTP/0.9
    • 🦋 HTTP/1.0
    • 🦋 HTTP/1.1
    • 🦋 HTTP/2.0
    • 🦋 HTTP/3.0
  • 五:🔥 共勉

一:🔥 HTTP 协议

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

🧑‍💻 在互联网世界中, HTTP(HyperText Transfer Protocol, 超文本传输协议) 是一个至关重要的协议。 它定义了客户端(如浏览器) 与服务器之间如何通信, 以及交换或传输超文本(如 HTML 文档) 。

🧑‍💻 HTTP 协议是客户端与服务器之间通信的基础。 客户端通过 HTTP 协议向服务器发送请求, 服务器收到请求后处理并返回响应。 HTTP 协议是一个无连接、 无状态的协议, 即每次请求都需要建立新的连接, 且服务器不会保存客户端的状态信息。

🦋 认识 URL

📚 平时我们俗称的 “网址” 其实就是说的 URL

在这里插入图片描述

🦋 urlencode 和 urldecode

📚 像 / ? : 等这样的字符, 已经被 url 当做特殊意义理解了. 因此这些字符不能随意出现.

比如, 某个参数中需要带有这些特殊字符, 就必须先对特殊字符进行转义.

📚 转义的规则如下:

  • 将需要转码的字符转为 16 进制, 然后从右到左, 取 4 位(不足 4 位直接处理), 每 2 位做一位, 前面加上%, 编码成 %XY 格式

📚 例如:
在这里插入图片描述
🧑‍💻 “+” 被转义成了 “%2B” urldecode 就是 urlencode 的逆过程;
urlencode 工具

二:🔥 HTTP 协议请求与响应格式

🦋 HTTP 请求

在这里插入图片描述

  • 首行: [方法] + [uri] + [版本]
  • Header: 请求报头, 冒号分割的键值对; 每组属性之间使用 \r\n 分隔; 遇到空行表示 Header 部分结束
  • Body: 空行后面的内容都是 Body. Body 允许为空字符串. 如果 Body 存在, 则在 Header 中会有一个 Content-Length 属性来标识 Body 的长度;
    在这里插入图片描述

🦋 HTTP 响应

在这里插入图片描述

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

🦋 HTTP 的方法

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

  1. GET 方法(重点)
  • 用途: 用于请求 URL 指定的资源。
  • 示例: GET /index.html HTTP/1.1
  • 特性: 指定资源经服务器端解析后返回响应内容。
  • form 表单: https://www.runoob.com/html/html-forms.html
  1. POST 方法(重点)
  • 用途: 用于传输实体的主体, 通常用于提交表单数据。
  • 示例: POST /submit.cgi HTTP/1.1
  • 特性: 可以发送大量的数据给服务器, 并且数据包含在请求体中。
  • form 表单: https://www.runoob.com/html/html-forms.htm
  1. PUT 方法(不常用)
  • 用途: 用于传输文件, 将请求报文主体中的文件保存到请求 URL 指定的位置。
  • 示例: PUT /example.html HTTP/1.1
  • 特性: 不太常用, 但在某些情况下, 如 RESTful API 中, 用于更新资源。
  1. HEAD 方法
  • 用途: 与 GET 方法类似, 但不返回报文主体部分, 仅返回响应头。
  • 示例: HEAD /index.html HTTP/1.1
  • 特性: 用于确认 URL 的有效性及资源更新的日期时间等。
  1. DELETE 方法(不常用)
  • 用途: 用于删除文件, 是 PUT 的相反方法。
  • 示例: DELETE /example.html HTTP/1.1
  • 特性: 按请求 URL 删除指定的资源。
  1. OPTIONS 方法
  • 用途: 用于查询针对请求 URL 指定的资源支持的方法。
  • 示例: OPTIONS * HTTP/1.1
  • 特性: 返回允许的方法, 如 GET、 POST 等。

🦋 HTTP 的状态码

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

状态码含义应用样例
100Continue上传大文件时, 服务器告诉客户端可以继续上传
200OK访问网站首页, 服务器返回网页内容
201Created发布新文章, 服务器返回文章创建成功的信息
204No Content删除文章后, 服务器返回“无内容”表示操作成功
301Moved Permanently网站换域名后, 自动跳转到新域名; 搜索引擎更新网站链接时使用
302Found 或 See Other用户登录成功后, 重定向到用户首页
304Not Modified浏览器缓存机制, 对未修改的资源返回304 状态码
400Bad Request填写表单时, 格式不正确导致提交失败
401Unauthorized访问需要登录的页面时, 未登录或认证失败
403Forbidden尝试访问你没有权限查看的页面
404Not Found访问不存在的网页链接
500Internal Server Error服务器崩溃或数据库错误导致页面无法加载
502Bad Gateway使用代理服务器时, 代理服务器无法从上游服务器获取有效响应
503Service Unavailable服务器维护或过载, 暂时无法处理请求

📚 以下是仅包含重定向相关状态码的表格

状态码含义是否为临时重定向应用样例
301Moved Permanently否(永久重定向)网站换域名后, 自动跳转到新域名;搜索引擎更新网站链接时使用
302Found 或 See Other是(临时重定向)用户登录成功后,重定向到用户首页
307Temporary Redirect是(临时重定向)临时重定向资源到新的位置(较少使用)
308Permanent Redirect否(永久重定向)永久重定向资源到新的位置(较少使用)

关于重定向的验证, 以 301 为代表:

HTTP 状态码 301(永久重定向) 和 302(临时重定向) 都依赖 Location 选项。 以下是关于两者依赖 Location 选项的详细说明:

HTTP 状态码 301(永久重定向) :

  • 当服务器返回 HTTP 301 状态码时, 表示请求的资源已经被永久移动到新的位置。
  • 在这种情况下, 服务器会在响应中添加一个 Location 头部, 用于指定资源的新位置。 这个 Location 头部包含了新的 URL 地址, 浏览器会自动重定向到该地址。
  • 例如, 在 HTTP 响应中, 可能会看到类似于以下的头部信息:
HTTP/1.1 301 Moved Permanently\r\n
Location: https://www.new-url.com\r\n

HTTP 状态码 302(临时重定向) :

  • 当服务器返回 HTTP 302 状态码时, 表示请求的资源临时被移动到新的位置。
  • 同样地, 服务器也会在响应中添加一个 Location 头部来指定资源的新位置。 浏览器会暂时使用新的 URL 进行后续的请求, 但不会缓存这个重定向。
  • 例如, 在 HTTP 响应中, 可能会看到类似于以下的头部信息:
HTTP/1.1 302 Found\r\n
Location: https://www.new-url.com\r\n

🦁 总结: 无论是 HTTP 301 还是 HTTP 302 重定向, 都需要依赖 Location 选项来指定资源的新位置。 这个 Location 选项是一个标准的 HTTP 响应头部, 用于告诉浏览器应该将请求重定向到哪个新的 URL 地址。

🦋 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 连接

🧑‍💻 下面附上一张关于 HTTP 常见 header 的表格

字段名含义样例
Accept客户端可接受的响应内容类型Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,/;q=0.8
AcceptEncoding客户端支持的数据压缩格式Accept-Encoding: gzip, deflate, br
AcceptLanguage客户端可接受的语言类型Accept-Language: zhCN,zh;q=0.9,en;q=0.8
Host请求的主机名和端口号Host: www.example.com:8080
User-Agent客户端的软件环境信息User-Agent: Mozilla/5.0 (Windows NT10.0; Win64; x64)AppleWebKit/537.36 (KHTML, likeGecko) Chrome/91.0.4472.124Safari/537.36
Cookie客户端发送给服务器的 HTTP cookie 信息Cookie: session_id=abcdefg12345;user_id=123
Referer请求的来源 URLReferer: http://www.example.com/previous_page.html
Content-Type实体主体的媒体类型Content-Type: application/x-wwwform-urlencoded (对于表单提交) 或Content-Type: application/json (对于JSON 数据)
Content-Length实体主体的字节大小Content-Length: 150
Authorization认证信息, 如用户名和密码Authorization: BasicQWxhZGRpbjpvcGVuIHNlc2FtZQ== (Base64编码后的用户名:密码)
Cache-Control缓存控制指令请求时: Cache-Control: no-cache 或Cache-Control: max-age=3600; 响应时:Cache-Control: public, maxage=3600
Connection请求完后是关闭还是保持连接Connection: keep-alive 或Connection: close
Date请求或响应的日期和时间Date: Wed, 21 Oct 2023 07:28:00 GMT
Location重定向的目标URL(与 3xx 状态码配合使用)Location:http://www.example.com/new_location.html (与 302 状态码配合使用)
Server服务器类型Server: Apache/2.4.41 (Unix)Last-Modified 资源的最后修改时间Last-Modified: Wed, 21 Oct 202307:20:00 GMT
ETag资源的唯一标识符, 用于缓存ETag: “3f80f-1b6-5f4e2512a4100”
Expires响应过期的日期和时间Expires: Wed, 21 Oct 2023 08:28:00 GMT

三:🔥 实现 HTTP 服务器

🧑‍💻 设计模式:使用 模板方法模式 封装套接字 socket

🦁 模板方法模式是一种行为型设计模式,它在一个抽象类中定义了一个算法(业务逻辑)的骨架,具体步骤的实现由子类提供。它通过将算法的不变部分放在抽象类中,可变部分放在子类中,达到代码复用和扩展的目的。

  • 复用:所有子类可以直接复用父类提供的模板方法,即上面提到的不变的部分。
  • 扩展: 子类可以通过模板定义的一些扩展点就行不同的定制化实现。

模板方法模式的特点:

  1. 算法骨架 : 在基类中定义一个算法的固定执行步骤(模板方法),具体实现步骤交给子类完成。
  2. 复用代码: 子类复用基类中定义的通用逻辑,仅需实现特定步骤。
  3. 遵循开闭原则: 基类的骨架逻辑对扩展开放,对修改关闭。

一般用在什么场景?

  1. 定义算法骨架: 有一个固定的流程,但某些步骤需要根据具体情况自定义
  2. 复用公共逻辑: 多个子类共享相同的算法结构,仅需重写特定步骤。
  3. 控制执行顺序: 需要对子类执行方法的顺序进行控制时,

典型场景:

  • 数据处理流程(如读取数据、处理数据、输出结果)
  • Web 请求处理 (如解析请求、处理逻辑、返回响应)

📦 socket.hpp

#pragma once#include <iostream>
#include <string>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cstdlib>
#include "Log.hpp"
#include "Common.hpp"
#include "InetAddr.hpp"namespace SocketModule
{using namespace LogModule;class Socket;using SockPtr = std::shared_ptr<Socket>;// 基类,规定创建socket的方法// 提供一个/若干个/固定模式的socket方法class Socket{public:virtual void SocketOrDie() = 0;virtual void SetSocketOpt() = 0;virtual bool BindOrDie(int port) = 0;virtual bool ListenOrDie() = 0;virtual SockPtr Accepter(InetAddr *client) = 0;virtual void Close() = 0;virtual int Recv(std::string *out) = 0;virtual int Send(const std::string &in) = 0;virtual int Fd() = 0;virtual ~Socket() = default;// 其他方法,需要的时候再加// 提供一个创建 listensockfd 的固定套路// 设计模式:模板方法模式void BuildTcpSocketMethod(int port){SocketOrDie();SetSocketOpt();BindOrDie(port);ListenOrDie();}// #ifdef WIN//     // 提供一个创建 listensockfd 的固定套路//     void BuildTcpSocket()//     {//         SocketOrDie();//         SetSocketOpt();//         BindOrDie();//         ListenOrDie();//     }// #else // Linux// #endif// 提供一个创建 listensockfd 的固定套路//     void BuildTcpSocket()//     {//         SocketOrDie();//         SetSocketOpt();//         BindOrDie();//         ListenOrDie();//     }};class TcpSocket : public Socket{public:TcpSocket() : _sockfd(gdefaultsockfd){}TcpSocket(int sockfd) : _sockfd(sockfd){}virtual void SocketOrDie() override{_sockfd = ::socket(AF_INET, SOCK_STREAM, 0);if (_sockfd < 0){LOG(LogLevel::ERROR) << "socket error";exit(SOCKET_ERR);}LOG(LogLevel::DEBUG) << "socket create success: " << _sockfd;      }virtual void SetSocketOpt() override{// 保证服务器,异常断开之后,可以立即重启,不会有bind问题int opt = 1;int n = ::setsockopt(_sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));(void)n;}virtual bool BindOrDie(int port) override{if(_sockfd == gdefaultsockfd) return false;InetAddr addr(port);int n = ::bind(_sockfd, addr.NetAddr(), addr.NetAddrLen());if(n < 0){LOG(LogLevel::ERROR) << "bind error";exit(BIND_ERR);}LOG(LogLevel::DEBUG) << "bind success: " << _sockfd;      return true;}virtual bool ListenOrDie() override{if(_sockfd == gdefaultsockfd) return false;int n = ::listen(_sockfd, gbacklog);if(n < 0){LOG(LogLevel::ERROR) << "listen error";exit(LISTEN_ERR);}LOG(LogLevel::DEBUG) << "listen create success: " << _sockfd;      return true;}// 1. 文件描述符 2. client infovirtual SockPtr Accepter(InetAddr *client) override{if(!client) return nullptr;struct sockaddr_in peer;socklen_t len = sizeof(peer);int newsockfd = ::accept(_sockfd, CONV(&peer), &len);if(newsockfd < 0){LOG(LogLevel::WARNING) << "accept error";return nullptr;}client->SetAddr(peer, len);return std::make_shared<TcpSocket>(newsockfd);  // accept之后链接好的 sockfd}virtual void Close() override{if(_sockfd == gdefaultsockfd) return ;::close(_sockfd);}virtual int Recv(std::string *out) override{char buffer[1024 * 8];auto size = ::recv(_sockfd, buffer, sizeof(buffer), 0);if(size > 0){buffer[size] = 0;*out = buffer;}return size;}virtual int Send(const std::string &in) override{auto size = ::send(_sockfd, in.c_str(), in.size(), 0);return size;}virtual int Fd() override{return _sockfd;}virtual ~TcpSocket(){}private:int _sockfd;};
}

📦 http协议封装
HttpProtocol.hpp

#pragma  once#include <iostream>
#include <string>
#include <vector>
#include <sstream>
#include <unordered_map>
#include <fstream>
#include "Common.hpp"
#include "Log.hpp"const std::string Sep = "\r\n";
const std::string LineSep = " ";
const std::string HeaderLineSep = ": ";
const std::string BlankLine = Sep;
const std::string defaulthomepage = "wwwroot";
const std::string http_version = "HTTP/1.0";
const std::string page404 = "wwwroot/404.html";
const std::string firstpage = "index.html";using namespace LogModule;class HttpReauest
{
public:HttpReauest() {}~HttpReauest() {}bool IsHasArgs(){return _isexec;}bool ParseHeaderKv(){std::string key, value;for(auto &herder : _req_header){if(SplitString(herder, HeaderLineSep, &key, &value)){_headerkv.insert(std::make_pair(key, value));}}return true;}bool ParseHeader(std::string &request_str){std::string line;while(true){bool r = ParseOneLine(request_str, &line, Sep);if(r && !line.empty()){_req_header.push_back(line);}else if(r && line.empty()){_blank_line = Sep;break;}else {return false;}}ParseHeaderKv();return true;}void Deserialize(std::string &request_str){if(ParseOneLine(request_str, &_req_line, Sep)){// 提取请求行中的详细字段ParseReqLine(_req_line, LineSep);ParseHeader(request_str); // 解析报头_body = request_str;// 分析请求中是否含有参数if(_method == "POST"){_isexec = true;  // 参数在正文_path = _uri;_args = _body;}else if(_method  == "GET"){auto pos = _uri.rfind('?');if(pos != std::string::npos){_isexec = true;//  /login?name=zhangsan&passwd=123456_path = _uri.substr(0, pos);_args = _uri.substr(pos + 1);} }}}std::string GetContent(const std::string &path){// 二进制读std::string content;std::ifstream in(path, std::ios::binary);if(!in.is_open()) return std::string();in.seekg(0, in.end);int filesize = in.tellg();in.seekg(0, in.beg);content.resize(filesize);in.read((char*)content.c_str(), filesize);in.close();LOG(LogLevel::DEBUG) << "content length: " << content.size();return content;// 暂时做法// std::string content;// std::ifstream in(_uri);// if(!in.is_open()) return std::string();// std::string line;// while(getline(in, line))// {//     content += line;// }// in.close();// return content;}void Print(){std::cout << "_method: " << _method << std::endl;std::cout << "_uri: " << _uri << std::endl;std::cout << "_version: " << _version << std::endl;for(auto &kv : _headerkv){std::cout << kv.first << " # " << kv.second << std::endl;}std::cout << "_blank_line: " << _blank_line << std::endl;std::cout << "_body: " << _body << std::endl;}std::string Uri(){return _uri;}void SetUri(const std::string &newuri){_uri = newuri;}std::string Path() { return _path; }std::string Args() { return _args; }std::string Suffix(){auto pos = _uri.rfind(".");if(pos == std::string::npos) return std::string(".html");else return _uri.substr(pos);}
private:void  ParseReqLine(const std::string &_req_line, const std::string &LineSep)  // 请求行字段解析 {(void)LineSep;std::stringstream ss(_req_line);ss >> _method >> _uri >> _version;}private:std::string _req_line;std::vector<std::string> _req_header;std::string _blank_line;std::string _body;// 在反序列化的过程中,细化我们解析出来的字段std::string _method;std::string _uri;      // 用户想要这个std::string _path;std::string _args;std::string _version;std::unordered_map<std::string, std::string> _headerkv;bool _isexec = false;
};// 对于http请求,都要有应答
class HttpResponse
{
public:HttpResponse() : _version(http_version), _blank_line(Sep){}void Build(HttpReauest &req){std::string uri = defaulthomepage + req.Uri();if(uri.back() == '/'){uri += firstpage;// req.SetUri(uri);}_content = req.GetContent(uri);if(_content.empty()) {// 用户请求的资源并不存在_status_code = 404;_content = req.GetContent(page404);}else {_status_code = 200;}LOG(LogLevel::DEBUG) << "客户端在请求:" << req.Uri();_status_desc = Code2Desc(_status_code);     // 和状态码是强相关的if(!_content.empty()){SetHeader("Content-Length", std::to_string(_content.size()));std::string mime_type = Suffix2Desc(req.Suffix());SetHeader("Content-Type", mime_type);}_body = _content;}void SetCode(int code) {_status_code = code;_status_desc = Code2Desc(_status_code);}void SetBody(const std::string &body){_body = body;}void SetHeader(const std::string &k, const std::string &v){_header_kv[k] = v;}void Serialize(std::string *resp_str){for(auto &header : _header_kv){_resp_header.push_back(header.first + HeaderLineSep + header.second);}_resp_line = _version + LineSep + std::to_string(_status_code) + LineSep + _status_desc + Sep;   // 第一行// 序列化*resp_str = _resp_line;for(auto &line : _resp_header){*resp_str += (line + Sep);}*resp_str += _blank_line;*resp_str += _body;}~HttpResponse() {}
private:std::string Code2Desc(int code){switch (code){case 200:return "OK";case 404:return "Not Found";case 301:return "Move Permanently";case 302:return "Found";default:return std::string();}}std::string Suffix2Desc(const std::string &suffix){if(suffix == ".html")return "text/html";else if(suffix == ".jpg")return "application/x-jpg";else return "text/html";}
private:// 必备的要素std::string _version;int _status_code;std::string _status_desc;std::string _content;std::unordered_map<std::string, std::string> _header_kv;// 最终要这4部分,构建应答std::string _resp_line;std::vector<std::string> _resp_header;std::string _blank_line;std::string _body;
};

🦋 完整代码移步我的Gitee仓库

🧑‍💻 点击跳转 包含代码和详细注释

在这里插入图片描述
🧑‍💻 至此 成功访问http服务器上搭建的网站
在这里插入图片描述

备注:
此处我们使用 8080 端口号启动了 HTTP 服务器. 虽然 HTTP 服务器一般使用 80 端口,
但这只是一个通用的习惯. 并不是说 HTTP 服务器就不能使用其他的端口号.

四:🔥 附录

🦋 HTTP 历史及版本核心技术与时代背景

🧑‍💻 HTTP(Hypertext Transfer Protocol, 超文本传输协议) 作为互联网中浏览器和服务器间通信的基石, 经历了从简单到复杂、 从单一到多样的发展过程。 以下将按照时间顺序, 介绍 HTTP 的主要版本、 核心技术及其对应的时代背景。

🦋 HTTP/0.9

📚 核心技术:

  • 仅支持 GET 请求方法。
  • 仅支持纯文本传输, 主要是 HTML 格式。
  • 无请求和响应头信息。

📚 时代背景:

  • 1991 年, HTTP/0.9 版本作为 HTTP 协议的最初版本, 用于传输基本的超文本 HTML 内容。
  • 当时的互联网还处于起步阶段, 网页内容相对简单, 主要以文本为主。

🦋 HTTP/1.0

📚 核心技术:

  • 引入 POST 和 HEAD 请求方法。
  • 请求和响应头信息, 支持多种数据格式(MIME) 。
  • 支持缓存(cache) 。
  • 状态码(status code) 、 多字符集支持等。

📚 时代背景:

  • 1996 年, 随着互联网的快速发展, 网页内容逐渐丰富, HTTP/1.0 版本应运而生。
  • 为了满足日益增长的网络应用需求, HTTP/1.0 增加了更多的功能和灵活性。
  • 然而, HTTP/1.0 的工作方式是每次 TCP 连接只能发送一个请求, 性能上存在一定局限。

🦋 HTTP/1.1

📚 核心技术:

  • 引入持久连接(persistent connection) , 支持管道化(pipelining) 。
  • 允许在单个 TCP 连接上进行多个请求和响应, 提高了性能。
  • 引入分块传输编码(chunked transfer encoding) 。
  • 支持 Host 头, 允许在一个 IP 地址上部署多个 Web 站点。

📚 时代背景:

  • 1999 年, 随着网页加载的外部资源越来越多, HTTP/1.0 的性能问题愈发突出。
  • HTTP/1.1 通过引入持久连接和管道化等技术, 有效提高了数据传输效率。
  • 同时, 互联网应用开始呈现出多元化、 复杂化的趋势, HTTP/1.1 的出现满足了这些需求。

🦋 HTTP/2.0

📚 核心技术:

  • 多路复用(multiplexing) , 一个 TCP 连接允许多个 HTTP 请求。
  • 二进制帧格式(binary framing) , 优化数据传输。
  • 头部压缩(header compression) , 减少传输开销。
  • 服务器推送(server push) , 提前发送资源到客户端。

📚 时代背景:

  • 2015 年, 随着移动互联网的兴起和云计算技术的发展, 网络应用对性能的要求越来越高。
  • HTTP/2.0 通过多路复用、 二进制帧格式等技术, 显著提高了数据传输效率和网络性能。
  • 同时, HTTP/2.0 还支持加密传输(HTTPS) , 提高了数据传输的安全性。

🦋 HTTP/3.0

📚 核心技术:

  • 使用 QUIC 协议替代 TCP 协议, 基于 UDP 构建的多路复用传输协议。
  • 减少了 TCP 三次握手及 TLS 握手时间, 提高了连接建立速度。
  • 解决了 TCP 中的线头阻塞问题, 提高了数据传输效率。

📚 时代背景:

  • 2022 年, 随着 5G、 物联网等技术的快速发展, 网络应用对实时性、 可靠性的要求越来越高。
  • HTTP/3.0 通过使用 QUIC 协议, 提高了连接建立速度和数据传输效率, 满足了这些需求。
  • 同时, HTTP/3.0 还支持加密传输(HTTPS) , 保证了数据传输的安全性

五:🔥 共勉

以上就是我对 应用层协议 HTTP 讲解&实战:从0实现HTTP 服务器 的理解,想要完整代码可以私信博主噢!觉得这篇博客对你有帮助的,可以点赞收藏关注支持一波~😉
在这里插入图片描述

相关文章:

应用层协议 HTTP 讲解实战:从0实现HTTP 服务器

&#x1f308; 个人主页&#xff1a;Zfox_ &#x1f525; 系列专栏&#xff1a;Linux 目录 一&#xff1a;&#x1f525; HTTP 协议 &#x1f98b; 认识 URL&#x1f98b; urlencode 和 urldecode 二&#xff1a;&#x1f525; HTTP 协议请求与响应格式 &#x1f98b; HTTP 请求…...

DDD-全面理解领域驱动设计中的各种“域”

一、DDD-领域 在领域驱动设计&#xff08;Domain-Driven Design&#xff0c;DDD&#xff09;中&#xff0c;**领域&#xff08;Domain&#xff09;**指的是软件系统所要解决的特定业务问题的范围。它涵盖了业务知识、规则和逻辑&#xff0c;是开发团队与领域专家共同关注的核心…...

PHP防伪溯源一体化管理系统小程序

&#x1f50d; 防伪溯源一体化管理系统&#xff0c;品质之光&#xff0c;根源之锁 &#x1f680; 引领防伪技术革命&#xff0c;重塑品牌信任基石 我们自豪地站在防伪技术的前沿&#xff0c;为您呈现基于ThinkPHP和Uniapp精心锻造的多平台&#xff08;微信小程序、H5网页&…...

纯css实现div宽度可调整

<!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /><title>纯css实现div尺寸可调整</title><style…...

C# 中使用Hash用于密码加密

通过一定的哈希算法&#xff08;典型的有MD5&#xff0c;SHA-1等&#xff09;&#xff0c;将一段较长的数据映射为较短小的数据&#xff0c;这段小数据就是大数据的哈希值。他最大的特点就是唯一性&#xff0c;一旦大数据发生了变化&#xff0c;哪怕是一个微小的变化&#xff0…...

如何建设一个企业级的数据湖

建设一个企业级的数据湖是一项复杂且系统化的工程&#xff0c;需要从需求分析、技术选型、架构设计到实施运维等多个方面进行综合规划和实施。以下是基于我搜索到的资料&#xff0c;详细阐述如何建设企业级数据湖的步骤和关键要点&#xff1a; 一、需求分析与规划 明确业务需…...

目标跟踪之sort算法(3)

这里写目录标题 1 流程1 预处理2 跟踪 2 代码 参考&#xff1a;sort代码 https://github.com/abewley/sort 1 流程 1 预处理 1.1 获取离线检测数据。1.2 实例化跟踪器。2 跟踪 2.1 轨迹处理。根据上一帧的轨迹预测当前帧的轨迹&#xff0c;剔除到当前轨迹中为空的轨迹得到当前…...

【java数据结构】HashMapOJ练习题

【java数据结构】HashMapOJ练习题 一、只出现一次的数字二 、随机链表的复制三 、宝石与石头四、坏键盘打字五、前K个高频单词 博客最后附有整篇博客的全部代码&#xff01;&#xff01;&#xff01; 一、只出现一次的数字 只出现一次的数字 思路&#xff1a; 先遍历一遍数组…...

Nginx前端后端共用一个域名如何配置

在 Nginx 中配置前端和后端共用一个域名的情况&#xff0c;通常是通过路径或子路径将请求转发到不同的服务。以下是一个示例配置&#xff0c;假设&#xff1a; 前端静态文件在 /var/www/frontend/。 后端 API 服务运行在 http://127.0.0.1:5000。 域名是 example.com&#xff…...

SpringBoot3+Vue3开发学生选课管理系统

功能介绍 分三个角色登录&#xff1a;学生登录&#xff0c;老师登录&#xff0c;教务管理员登录&#xff0c;不同用户功能不同&#xff01; 1.学生用户功能 选课记录&#xff0c;查看选课记录&#xff0c;退选。选课管理&#xff0c;进行选课。通知管理&#xff0c;查看通知消…...

Linux系统 C/C++编程基础——基于GTK+的图形用户界面编程

ℹ️大家好&#xff0c;我是练小杰&#xff0c;今天星期三了&#xff0c;距离除夕又少了一天&#xff0c;新年的钟声就快敲响了&#x1f606; 本文是有关Linux C/C编程中的基于GTK的图形用户界面编程知识点&#xff0c;后续会不断添加相关内容 ~~ 回顾:【使用make工具和Makefil…...

【Leetcode 每日一题】40. 组合总和 II

问题背景 给定一个候选人编号的集合 c a n d i d a t e s candidates candidates 和一个目标数 t a r g e t target target&#xff0c;找出 c a n d i d a t e s candidates candidates 中所有可以使数字和为 t a r g e t target target 的组合。 c a n d i d a t e s c…...

python 变量范围的定义与用法

文章目录 1. 局部变量&#xff08;Local Scope&#xff09;示例&#xff1a; 2. 嵌套函数变量&#xff08;Enclosing Scope&#xff09;示例&#xff1a;说明&#xff1a; 3. 全局变量&#xff08;Global Scope&#xff09;示例&#xff1a;说明&#xff1a; 4. 内置变量&#…...

TRTC实时对话式AI解决方案,助力人机语音交互极致体验

近年来&#xff0c;AI热度持续攀升&#xff0c;无论是融资规模还是用户热度都大幅增长。2023 年&#xff0c;中国 AI 行业融资规模达2631亿人民币&#xff0c;较2022年上升51%&#xff1b;2024年第二季度&#xff0c;全球 AI 初创企业融资规模为 240 亿美金&#xff0c;较第一季…...

dev c++ ‘unordered_set‘ does not name a type

参考:https://blog.csdn.net/Zaczc/article/details/142531525 启用C11标准步骤 工具->编译选项 勾选编译时加入以下命令 在空白处添加:-stdc11 单击确定&#xff0c;启用成功...

算法每日双题精讲 —— 二分查找(寻找旋转排序数组中的最小值,点名)

&#x1f31f;快来参与讨论&#x1f4ac;&#xff0c;点赞&#x1f44d;、收藏⭐、分享&#x1f4e4;&#xff0c;共创活力社区。 &#x1f31f; 别再犹豫了&#xff01;快来订阅我们的算法每日双题精讲专栏&#xff0c;一起踏上算法学习的精彩之旅吧&#x1f4aa; 在算法的…...

three.js+WebGL踩坑经验合集(4.2):为什么不在可视范围内的3D点投影到2D的结果这么不可靠

上一篇&#xff0c;笔者留下了一个问题&#xff0c;three.js内置的THREE.Vector3.project方法算出来的结果对于超出屏幕可见范围的点来说错得相当离谱。 three.jsWebGL踩坑经验合集(4.1):THREE.Line2的射线检测问题&#xff08;注意本篇说的是Line2&#xff0c;同样也不是阈值…...

Kafka运维宝典 (二)- kafka 查看kafka的运行状态、broker.id不一致导致启动失败问题、topic消息积压量告警监控脚本

Kafka运维宝典 &#xff08;二&#xff09; 文章目录 Kafka运维宝典 &#xff08;二&#xff09;一、kafka broker.id冲突问题1. broker.id 冲突的影响2. 如何发现 broker.id 冲突3. 解决 broker.id 冲突的方法4. broker.id 配置管理5. 集群启动后确认 broker.id 唯一性6. brok…...

全球AI模型百科全书,亚马逊云科技Bedrock上的100多款AI模型

今天小李哥给大家介绍的是亚马逊云科技上的AI模型管理平台Amazon Bedrock上的Marketplace&#xff0c;这是亚马逊云科技在今年re:Invent发布的一个全新功能&#xff0c;将亚马逊的电商基因带到了其云计算平台&#xff0c;让我们能够通过Amazon Bedrock访问100多种流行、新兴和专…...

微信小程序中常见的 跳转方式 及其特点的表格总结(wx.navigateTo 适合需要返回上一页的场景)

文章目录 详细说明总结wx.navigateTo 的特点为什么 wx.navigateTo 最常用&#xff1f;其他跳转方式的使用频率总结 以下是微信小程序中常见的跳转方式及其特点的表格总结&#xff1a; 跳转方式API 方法特点适用场景wx.navigateTowx.navigateTo({ url: 路径 })保留当前页面&…...

【Elasticsearch】index:false

在 Elasticsearch 中&#xff0c;index 参数用于控制是否对某个字段建立索引。当设置 index: false 时&#xff0c;意味着该字段不会被编入倒排索引中&#xff0c;因此不能直接用于搜索查询。然而&#xff0c;这并不意味着该字段完全不可访问或没有其他用途。以下是关于 index:…...

新版IDEA创建数据库表

这是老版本的IDEA创建数据库表&#xff0c;下面可以自己勾选Not null&#xff08;非空),Auto inc&#xff08;自增长),Unique(唯一标识)和Primary key&#xff08;主键) 这是新版的IDEA创建数据库表&#xff0c;Not null和Auto inc可以看得到&#xff0c;但Unique和Primary key…...

输入带空格的字符串,求单词个数

输入带空格的字符串&#xff0c;求单词个数 __ueooe_eui_sjje__ ---->3syue__jdjd____die_ ---->3shuue__dju__kk ---->3 #include <stdio.h> #include <string.h>// 自定义函数来判断字符是否为空白字符 int isSpace(char c) {return c || c \t || …...

C语言程序设计十大排序—希尔排序

文章目录 1.概念✅2.希尔排序&#x1f388;3.代码实现✅3.1 直接写✨3.2 函数✨ 4.总结✅ 1.概念✅ 排序是数据处理的基本操作之一&#xff0c;每次算法竞赛都很多题目用到排序。排序算法是计算机科学中基础且常用的算法&#xff0c;排序后的数据更易于处理和查找。在计算机发展…...

Excel制作合同到期自动提醒!

大家好&#xff0c;我是小鱼。 今天分享一下如何利用Excel制作合同到期提醒表&#xff0c;实现Excel表格自动计算合同到期日和天数&#xff0c;根据合同状态和到期天数自动填充颜色提醒&#xff0c;超实用。先看一下效果&#xff0c;已经到期的合同会自动被填充为红色&#xf…...

“AI质量评估系统:智能守护,让品质无忧

嘿&#xff0c;各位小伙伴们&#xff01;今天咱们来聊聊一个在现代社会中越来越重要的角色——AI质量评估系统。你知道吗&#xff1f;在这个快速发展的时代&#xff0c;产品质量已经成为企业生存和发展的关键。而AI质量评估系统&#xff0c;就像是我们的智能守护神&#xff0c;…...

爬虫基础之爬取某基金网站+数据分析

声明: 本案例仅供学习参考使用&#xff0c;任何不法的活动均与本作者无关 网站:天天基金网(1234567.com.cn) --首批独立基金销售机构-- 东方财富网旗下基金平台! 本案例所需要的模块: 1.requests 2.re(内置) 3.pandas 4.pyecharts 其他均需要 pip install 模块名 爬取步骤: …...

使用 Aryn DocPrep、DocParse 和 Elasticsearch 向量数据库实现高质量 RAG

作者&#xff1a;来自 Elastic Hemant Malik 及 Jonathan Fritz 组织依靠自然语言查询从非结构化数据中获取见解&#xff0c;但要获得高质量的答案&#xff0c;首先要进行有效的数据准备。Aryn DocParse 和 DocPrep通过将复杂文档转换为结构化 JSON 或 markdown 来简化此过程&a…...

Couchbase UI: Server

在 Couchbase UI 中的 Server&#xff08;服务器&#xff09;标签页主要用于管理和监控集群中的各个节点。以下是 Server 标签页的主要内容和功能介绍&#xff1a; 1. 节点列表 显示集群中所有节点的列表&#xff0c;每个节点的详细信息包括&#xff1a; 节点地址&#xff1…...

Web3.0时代的挑战与机遇:以开源2+1链动模式AI智能名片S2B2C商城小程序为例的深度探讨

摘要&#xff1a;Web3.0作为互联网的下一代形态&#xff0c;承载着去中心化、开放性和安全性的重要愿景。然而&#xff0c;其高门槛、用户体验差等问题阻碍了Web3.0的主流化进程。本文旨在深入探讨Web3.0面临的挑战&#xff0c;并提出利用开源21链动模式、AI智能名片及S2B2C商城…...