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

Linux网络的应用层协议HTTP

目录

1、HTTP协议

1.1 HTTP的请求与响应格式

 1.2 URL

1.3 HTTP的常用请求方法

1.3.1 GET

1.3.2 POST

1.4 HTTP的常见报头

1.5 HTTP的常用状态码及其描述

1.6 HTTP协议的介绍

2、Hello Http

2.1 大致思路

2.2 Http.hpp

2.3 Util.hpp

2.4 Main.cc

2.5 Tcpserver.hpp

2.6 示例及完整代码


1、HTTP协议

1.1 HTTP的请求与响应格式

  • HTTP版本不同的版本 提供 不同的服务
  • HTTP协议如何做到报头和有效载荷的分离?以空行为分隔符。
  • HTTP协议如何序列化和反序列化成熟的HTTP协议,不依赖于任何第三方库直接序列化为字节流再反序列化。为什么不是字符流?因为'\0'可能不是结尾
  • 传输交给传输层TCP/UDPHTTP应用层,只是解析请求和应答

 1.2 URL

  • URL,Uniform Resource Locator,统一资源定位符。
  • HTTP是基于TCP的协议;HTTPSHTTP经过加密的协议;HTTPS下一节讲解
  • 域名:对应着服务器的IP
  • 端口号HTTP默认80端口号HTTPS默认443端口号
  • 路径定位服务器里的 具体文件 / 功能”。/s是百度 “搜索功能” 的专属路径标识。
  • 参数(可选):向服务器传递具体需求”。wd是百度 “搜索关键词” 的参数名天气就是你要搜索的内容
  • 注意:
  1. 浏览器不写https://www.端口号,会自动补全
  2. 像 / : ? ,这样的符号,被当作是特殊字符如需使用,需要进行转义。UrlEncode编码/UrlDecode解码

1.3 HTTP的常用请求方法

  • 访问资源(数据),无非就是从远端拿(GET)数据将本地数据上传(POST)到远端。其实就是I/O
1.3.1 GET
  • 用于请求URL指定的资源
  • 可以通过URL的方式提交参数,进行交互式的动态服务。
  • 注意:
  • URL中,参数在?的后面,以key=value的显示,可用&连接多个参数,但是不建议太长。
  • 因为客户端发起请求前,已经通过 TCP 连接到了服务器的 IP 和端口,所以请求报文中的URL只剩下了路径+参数
1.3.2 POST
  • 向服务器提交数据让服务器对数据进行处理

  • 可以通过请求正文的方式提交参数不显示,可以传长数据

  • 注意:

  • GET,POST,无论显不显示参数,都不安全,因为,如Fiddler,可以抓取报文信息。所以HTTPS,进行加密。

1.4 HTTP的常见报头

  • Content-Type: 数据类型(text/html等)。根据请求资源的后缀指定数据类型,才能正确显示。
  • Content-Length: 正文的长度。知道了有效载荷的长度。
  • Connection: 是否支持长连接(一条连接,可以发更多的请求)。keep-alive支持长连接close不支持长连接
  • Host客户端告知服务器,所请求的资源是在哪个主机的哪个端口上
  • User-Agent: 声明用户的操作系统和浏览器版本信息
  • Referer: 当前页面是从哪个页面跳转过来的
  • Location: 搭配3xx状态码(重定向)使用,告诉客户端接下来要去哪里访问
  • Cookie: 用于在客户端存储少量信息session对这些信息进行了加密,但是整体的加密程度有限。如:账号密码经过session加密放到Cookie中,但是黑客依然可以使用这些加密的信息进行登录。

1.5 HTTP的常用状态码及其描述

  • 1xx:信息性状态码(请求已接收,继续处理)
    • 100 Continue:服务器已接收请求头部,允许客户端继续发送请求体(常用于客户端发送大型数据前的 “预请求”,如文件上传)。
  • 2xx:成功状态码(请求已被成功处理)
    • 200 OK:最常见的成功状态,请求被正常处理并返回响应体(如网页、接口数据等)。
  • 3xx:重定向状态码(需要客户端进一步操作)
    • 301 Moved Permanently:永久重定向。请求的资源已被永久迁移到新地址,客户端应更新本地缓存的 URL(如域名更换,旧域名跳转到新域名)。
    • 302 Found:临时重定向。资源临时迁移到新地址,客户端下次请求仍应使用原 URL(如网站维护时临时跳转到通知页)。
  • 4xx:客户端错误状态码(请求存在错误,服务器无法处理)
    • 400 Bad Request:请求格式错误(如参数缺失、格式不正确,服务器无法解析)。 401 Unauthorized:未认证。请求需要身份验证(如登录),但客户端未提供或验证失败(常见于需要登录的页面)。
    • 401 Unauthorized(未认证):请求需要身份验证(如登录),但客户端未提供有效凭证(如 token 过期、未携带 cookie)。场景:未登录时访问需要权限的接口(如/api/orders),服务器返回 401,提示用户登录。
    • 403 Forbidden:服务器拒绝处理。客户端已认证,但没有权限访问该资源(如普通用户访问管理员后台)。 404 Not Found:最常见的错误之一,请求的资源不存在(如网址输错、页面已删除)。
    • 404 Not Found:服务器无法找到请求的资源(URL 不存在)。

  • 5xx:服务器错误状态码(服务器处理请求时出错)
    • 一般不设置错误状态码,因为企业不能让别人"乘虚而入"
  • 注意:
  • 状态码:必须严格遵循标准,不能自定义

  • 状态描述可以自定义,但建议遵循标准

  • 为什么浏览器不显示状态码和状态描述?普通用户不需要关心 “404”“Not Found” 这些技术符号,浏览器会把它们翻译成 “页面加载成功”“页面找不到” 等易懂信息;而开发者可以通过工具随时查看原始的状态码和描述,用于调试问题。

1.6 HTTP协议的介绍

  • HTTP为了获取"资源"而采取的协议
  • HTTP,HyperText Transfer Protocol, “超文本传输协议”。因为正文传的是字节,显示器以对应的数据类型(Content-Type)显示。
  • HTTP无连接(传输层TCP来连接,HTTP是应用层),无状态(不保存客户端的信息,所以报头里使用了Cookie保存了一些信息)。

2、Hello Http

2.1 大致思路

  • 浏览器作为客户端向服务器(myhttp)发起请求服务器(myhttp)向浏览器发送应答
  • 客户端(浏览器)默认发送'/'访问首页。如果访问其他页面,就临时重定向到404页面。后续可以添加更多服务。

 2.2 Util.hpp

  • 一些工具类的方法。
#pragma once#include <fstream>
#include <string>class Util
{
public:static bool ReadOneLine(std::string &bigstr, std::string *out, const std::string &sep_line /*"\r\n"*/){auto pos = bigstr.find(sep_line);if (pos == std::string::npos)return false;*out = bigstr.substr(0, pos);bigstr.erase(0, pos + sep_line.size());return true;}static int FileSize(const std::string &file_name){// 按文本方式读, 读到'\0'可能会结束, 所以按照二进制的方式读std::ifstream in(file_name, std::ios::binary);if (!in.is_open())return -1;in.seekg(0, in.end);        // 将文件读指针移动到文件末尾int file_size = in.tellg(); // 获取文件的字节数in.seekg(0, in.beg); // 将指针移回文件开头in.close();return file_size;}static bool ReadFileContent(const std::string &file_name, std::string *out){int file_size = FileSize(file_name);if (file_size > 0){std::ifstream in(file_name, std::ios::binary);if (!in.is_open())return false;out->resize(file_size);in.read(out->data(), file_size);  // C++11后 data() 可返回 char*(非const)in.close();return true;}return false;}
};

2.3 Http.hpp

  • 如何读到完整的请求?
  • 1. 空行 -> 读取到请求行+请求报头
  • 2. 请求报头中的Content-Length -> 请求正文
#pragma once#include "Socket.hpp"
#include "TcpServer.hpp"
#include "Util.hpp"
#include "Log.hpp"
#include <string>
#include <unordered_map>const static std::string sep_space = " ";
const static std::string sep_line = "\r\n";
const static std::string sep_kv = ": ";const static std::string wwwroot = "./wwwroot";
const static std::string home_page = "/home.html";
const static std::string page_404 = "/404.html";class HttpRequest
{
public:void ParseReqLine(std::string &req_line){// GET / HTTP/1.1std::stringstream ss(req_line);ss >> _method >> _url >> _http_version;}bool Deserialize(std::string& req_str){// 请求行std::string req_line;bool res = Util::ReadOneLine(req_str, &req_line, sep_line);if(!res)return false;ParseReqLine(req_line);LOG(LogLevel::DEBUG) << "reqline: " << req_line;if(_url == "/")_url = wwwroot + home_page; // "./wwwroot/home.html";else _url = wwwroot + _url; // "./wwwroot/..."// 假设没有?后面的参数...LOG(LogLevel::DEBUG) << "_method: " << _method;LOG(LogLevel::DEBUG) << "_url: " << _url;LOG(LogLevel::DEBUG) << "_http_version: " << _http_version;// 请求报头 请求正文 ...return true;}std::string GetUrl(){return _url;}private:std::string _method;std::string _url;std::string _http_version;std::unordered_map<std::string, std::string> _headers;std::string _blank_line;std::string _text;
};class HttpResponse
{
public:HttpResponse():_blank_line(sep_line),_http_version("HTTP/1.1"){}// 实现: 成熟的http,应答做序列化,不要依赖任何第三方库!std::string Serialize(){std::string status_line = _http_version + sep_space + std::to_string(_code) + sep_space + _code_desc + sep_line;std::string headers;for(auto& header : _headers){headers += header.first + sep_kv + header.second + sep_line;}return status_line + headers + _blank_line + _text;}void SetCode(int code){_code = code;switch(code){case 200:_code_desc = "OK";break;case 301:_code_desc = "Found";break;case 404:_code_desc = "Not Found";break;default:break;}}void SetHeaders(const std::string& key, const std::string& value){auto it = _headers.find(key);if(it == _headers.end())_headers.insert({key,value});}// 默认访问的是.htm/.html文件void SetTargetFile(const std::string& file){_target_file = file;}void SetText(const std::string text){_text = text;}bool MakeResponse(uint16_t port){// 浏览器要访问图标, 忽略他if(_target_file == "./wwwroot/favicon.ico"){LOG(LogLevel::INFO) << "用户请求图标: " << _target_file << "忽略他";return false;}bool res = Util::ReadFileContent(_target_file, &_text);if(!res){LOG(LogLevel::WARNING) << "没有文件: " << _target_file << " , 404";SetCode(302); // 没有这个文件, 重定向到404SetHeaders("Location","http://124.221.189.63:"+std::to_string(port)+"/404.html");}else{LOG(LogLevel::WARNING) << "读取文件: " << _target_file; SetCode(200); // 成功int file_size = Util::FileSize(_target_file);SetHeaders("Content-Length", std::to_string(file_size));}return true;}private:std::string _http_version;int _code;std::string _code_desc;std::unordered_map<std::string, std::string> _headers;std::string _blank_line;std::string _text;// 其他属性std::string _target_file;
};class Http
{
public:Http(uint16_t port):_tsvrp(std::make_unique<TcpServer>(port)),_port(port){}void HandleHttpRequest(std::shared_ptr<Socket>& sockfd, const InetAddr& client){LOG(LogLevel::DEBUG) << "收到新连接,准备读取请求"; std::string http_req;int n = sockfd->Recv(&http_req); // 大概率一次读到完整的请求报文if(n > 0){std::cout << "##########################" << std::endl;std::cout << http_req;std::cout << "##########################" << std::endl;HttpRequest req;HttpResponse resp;req.Deserialize(http_req);resp.SetTargetFile(req.GetUrl());if(resp.MakeResponse(_port)){sockfd->Send(resp.Serialize());}}}void Start(){_tsvrp->Start([this](std::shared_ptr<Socket>& sockfd, const InetAddr& client){this->HandleHttpRequest(sockfd, client);});}private:std::unique_ptr<TcpServer> _tsvrp;uint16_t _port;
};

2.4 Main.cc

#include "Http.hpp"
#include <memory>// ./myhttp server_port
int main(int argc, char* argv[])
{if(argc != 2){std::cerr << "Usage: " << argv[0] << " server_port" << std::endl;exit(USAGE_ERROR);}Enable_Console_Log_Strategy();uint16_t server_port = std::stoi(argv[1]);std::unique_ptr<Http> httpsvrp = std::make_unique<Http>(server_port);httpsvrp->Start();return 0;
}

2.5 Tcpserver.hpp

#pragma once#include "Common.hpp"
#include "Log.hpp"
#include <memory>using namespace LogModule;const static int default_sockfd = -1;
const static int default_backlog = 16;class Socket
{
public:virtual void SocketOrDie() = 0; // = 0, 不需要实现 virtual void BindOrDie(uint16_t port) = 0;virtual void ListenOrDie(int backlog) = 0;virtual void ConnectOrDie(std::string &server_ip, uint16_t server_port) = 0;virtual std::shared_ptr<Socket> Accept(InetAddr* client) = 0;virtual int Recv(std::string* out) = 0;virtual int Send(const std::string& in) = 0;virtual void Close() = 0;
public:void BuildTcpServer(uint16_t port){SocketOrDie();BindOrDie(port);ListenOrDie(default_backlog);}void BuildTcpClient(std::string &server_ip, uint16_t server_port){SocketOrDie();ConnectOrDie(server_ip,server_port);}
};class TcpSocket : public Socket
{
public:TcpSocket(int sockfd = default_sockfd): _sockfd(sockfd){}virtual void Close() override{if (_sockfd != default_sockfd)::close(_sockfd); // ::表示调用 全局作用域 中的 close 函数}virtual void SocketOrDie() override{_sockfd = ::socket(AF_INET, SOCK_STREAM, 0);if (_sockfd < 0){LOG(LogLevel::FATAL) << "socket error!";exit(SOCKET_ERROR);}LOG(LogLevel::INFO) << "socket success, socket: " << _sockfd;}virtual void BindOrDie(uint16_t port) override{InetAddr local(port);int n = ::bind(_sockfd, CONST_CONV(local.Addr()), local.AddrLen());if (n < 0){LOG(LogLevel::FATAL) << "bind error!";exit(BIND_ERROR);}LOG(LogLevel::INFO) << "bind success, socket: " << _sockfd;}virtual void ListenOrDie(int backlog) override{int n = ::listen(_sockfd, default_backlog);if (n < 0){LOG(LogLevel::FATAL) << "listen error!";exit(LISTEN_ERROR);}LOG(LogLevel::INFO) << "listen success, sockfd: " << _sockfd;}virtual void ConnectOrDie(std::string &server_ip, uint16_t server_port) override{InetAddr server(server_ip, server_port);int n = ::connect(_sockfd, CONST_CONV(server.Addr()), server.AddrLen());if (n < 0){LOG(LogLevel::FATAL) << "connect error!";exit(CONNECT_ERROR);}LOG(LogLevel::INFO) << "connect success, sockfd: " << _sockfd;}virtual std::shared_ptr<Socket> Accept(InetAddr* client) override{std::cout << std::endl;struct sockaddr_in addr;socklen_t len = sizeof(addr);int fd = ::accept(_sockfd, CONV(addr), &len);if (fd < 0){LOG(LogLevel::WARNING) << "accept failed";return nullptr;}client->SetAddr(addr);LOG(LogLevel::INFO) << "accept success, client: " << client->StringAddr();return std::make_shared<TcpSocket>(fd); // 这个server的sockfd就可以调用Recv和Send方法。}virtual int Recv(std::string* out) override{char buf[1024*8];ssize_t n = ::recv(_sockfd,buf,sizeof(buf)-1,0);if(n > 0){buf[n] = 0;*out += buf; // += 可能要不断的读 }return n;}virtual int Send(const std::string& in) override{return ::send(_sockfd,in.c_str(),in.size(),0);}private:int _sockfd; // 既可以是listen_sockfd,也可以是sockfd,复用代码。
};

2.6 示例及完整代码

  • 示例:
  • 默认请求首页'/',还会请求图标'/favicon.ico'(忽略)。获取首页时,如果首页中有n个资源,会再次发起n次请求。注意资源有类型,服务器指定Content-Type,浏览器才能正确显示。当然现在的浏览器已经非常智能了,会自动识别。
  • 完整代码:Http
  • 注意:
  • 有些服务器没有开放一些端口(如:8080),我用的是腾讯云,在设置了防火墙模板(开放一些端口),应用在我的实例上,但是会覆盖之前的模板,所以如果ssh连不上,就再开放22端口
  • 可以通过w3school,学习一些前端知识。
http://www.dtcms.com/a/524217.html

相关文章:

  • SQLite Group By 指令详解
  • 监理建设协会网站wordpress关注微信登陆
  • 常用串行通信协议核心区别(含CAN、SPI、I2C、UART、RS-485、Ethernet、USB)
  • LangChain1.0发布
  • 压缩与缓存调优实战指南:从0到1根治性能瓶颈(五)
  • 使用 RPM 包在 Linux 7 上安装 MySQL 8
  • 云服务器2008做网站wordpress用thinkphp
  • 仓颉标准库std源码深度解析:构建全场景智能应用的基石
  • C4D域力场的应用之粒子随风飘散解析
  • 自己做的网站别人怎么访问安康网站建设公司电话
  • uniapp小程序实现手动向上滑动窗口
  • vue3:uniapp全局颜色变量配置思路:使用js变量
  • wordpress调用 别的网站昆明seo网站排名
  • 网站建设模板素材重庆互联网大厂
  • 网络爬虫指南:从原理到实战
  • 小杰-自然语言处理(four)——transformer系列——注意力机制
  • Java SpringAOP --- AOP的使用,AOP的源码
  • 阿里云渠道商:如何设置阿里云的安全组规则?
  • 网站设计速成如何让百度快速收录网站文章
  • 北京平台网站建设多少钱学院网站建设的特色
  • 外贸soho建站多少钱山东省住房和城乡建设厅官方网站
  • 芯科科技推出智能开发工具Simplicity Ecosystem软件开发套件开启物联网开发的新高度
  • 报错: lfstackPack redeclared in this block / go版本混乱,清理旧版本
  • 和鲸科技入选《大模型一体机产业图谱》,以一体机智驱科研、重塑教学
  • Go语言:关于怎么在线学习go语言的建议
  • 树 B树和B+树
  • 【arXiv2025】Real-Time Object Detection Meets DINOv3
  • 绍兴网站建设专业的公司4000-262-怎么在百度上发帖推广
  • AH2203输入12v输出3v 6v 9v/2A同步降压LED驱动器芯片
  • C如何调用Go