【网络】HTTP协议


一、三个预备知识

1、域名

在这里插入图片描述

2、URL(统一资源定位符)

在这里插入图片描述

3、编码(encode)和解码(decode)

在这里插入图片描述

二、HTTP请求和响应

1、画图解析

在这里插入图片描述

在这里插入图片描述
HTTP请求
在这里插入图片描述

HTTP响应
在这里插入图片描述

2、请求抓包工具fiddler和postman

fiddler download
在这里插入图片描述

百度的请求和响应报文信息(粗略一看):
在这里插入图片描述

postman download

在这里插入图片描述

3、最简单的HTTP服务器

代码部分:
前面的Socket和Log可以复制前面的代码:
httpserver.hpp:

#pragma once

#include <iostream>
#include <pthread.h>
#include "Socket.hpp"
#include "Log.hpp"

static const uint16_t defaultport = 8080;
struct ThreadData
{
    int sockfd;
};

class HttpServer
{
public:
    HttpServer(uint16_t port = defaultport)
        : _port(port)
    {
    }
    bool Start()
    {
        _listensockfd.Socket();
        _listensockfd.Bind(_port);
        _listensockfd.Listen();
        for ( ; ; )
        {
            std::string clientip;
            uint16_t clientport;
            int sockfd = _listensockfd.Accept(&clientip, &clientport);
            lg(Info, "get a new link sucessary...");
            pthread_t tid;
            ThreadData* td = new ThreadData;
            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 = recv(td->sockfd, buffer, sizeof(buffer) - 1, 0);
        if (n > 0)
        {  
            buffer[n] = 0;
            std::cout << buffer;
        }
        close(td->sockfd);
        return nullptr;
    }
    ~HttpServer()
    {
    }

private:
    Sock _listensockfd;
    uint16_t _port;
};

httpserver.cc:

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

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

现象:
在这里插入图片描述

4、处理函数及请求现象

利用recv收到消息,再做封装后send发送出去!
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

5、设计个好看的网页(前端)

w3cshool

在这里插入图片描述

在这里插入图片描述

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

前端代码路径问题,web路径问题:
在这里插入图片描述
在这里插入图片描述
注意看上面的实例,利用/区分出目录的地址,所以我们可以用一个目录文件来存放这些web前端的代码。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

6、http请求

在这里插入图片描述

在这里插入图片描述

7、访问根目录跳转到wwwroot文件中的index.html中

我们为了实现安全性,那么就要设计一下,当我们访问根目录的时候,我们就自动的跳转到我们指定的目录文件下的指定的index.html文件中,那么其实很简单了,我们只需要建立一个拼接字符串即可,遇到根目录或者是index.html目录我们就跳转到wwwroot中的index.html中,以后有人想访问我的根目录自动跳转,以确保安全性。

class HttpRequest
{
public:
    void Deserialize(std::string req)
    {
        while (true)
        {
            auto pos = req.find(sep);
            if (pos == std::string::npos)
            {
                break;
            }
            std::string tmp = req.substr(0, pos);
            if (tmp.empty()) // 找到空串
            {
                break;
            }
            _req_header.push_back(tmp);     // push一行
            req.erase(0, pos + sep.size()); // 移除一行
        }
        _text = req;
    }
    void Parse()
    {
        std::stringstream ss(_req_header[0]); // 请求行
        ss >> method >> url >> http_version;  // 把三个字符串放到三个string中
        _req_path = wwwroot; // ./wwwroot
        if (url == "/" || url == "/index.html")
        {
            _req_path += "/";
            _req_path += homepage; // ./wwwroot/index.html
        }
        else
        {
            _req_path += url; // ./wwwroot/a/b/c/d.html
        }
    }
    void DebugPrint()
    {
        std::cout << "----------------" << std::endl;
        for (auto &line : _req_header)
        {
            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 << "_req_path: " << _req_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 _req_path;
};

在这里插入图片描述

8、通过根目录加/的方式访问其他文件

在这里插入图片描述

三、HTTP的方法

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

1、GET方法

GET方法通过url来进行提参数。一般参数数量受限,不私密。
数据都是通过表单来提参的。get方法提参数是自动将路径拼接到url上面的。
如果我们要提交参数给我们的服务器的时候,我们使用get方法时候,我们提交的参数是通过url提交的。
在这里插入图片描述

2、POST方法

POST方法也支持参数提交,提交方式默认采用请求的正文提交参数,比GET私密,因为不会在url回显。
在这里插入图片描述

四、HTTP状态码

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

1、404 Not Found

wwwroot下的error.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>

HttpServer.hhp中的Handler函数改装,只需要改装成为判断是否要走到404的时候走到404就用error.html页面,没走到就正常页面。
在这里插入图片描述

fiddler抓包显示:
在这里插入图片描述

2、302重定向

谁掌握了浏览器谁就有了流量密码。

永久性重定向和临时性重定向。

在这里插入图片描述
在这里插入图片描述
httpserver.hpp的Handler函数:
在这里插入图片描述

五、HTTP请求属性

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

1、短连接和长连接

一次请求响应一个资源,关闭连接,是短连接。http/1.0
建立一个TCP连接,发送和返回多个http的request和response,就是长连接。http/1.1

我们的计算机上会有一个比较常见的现象,老版本的服务器响应新版本的客户端,新版本的客户端去请求老版本的服务器,这就会导致了服务器和客户端不匹配的情况,但我们要让他们匹配,因为是协商同意得,所以报文信息中有一个Connection: keep-alive的信息,表示的是服务端和客户端都是长连接。

2、加上图片二进制流的代码编写

index.html:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <!-- <form action="/a/b/hello.html" method="post">
        name: <input type="text" name="name"><br>
        password: <input type="password" name="passwd"><br>
        <input type="submit" value="提交">
    </form> -->
    <h1>这个是我们的首页</h1>
    <img src="/image/1.png" alt="俺牛爷爷来了">
    <img src="/image/2.png" alt="俺牛爷爷带着野牛来了">
</body>

</html>

HttpServer.hpp:

#pragma once

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

const std::string wwwroot = "./wwwroot"; // web根目录
const std::string homepage = "index.html";
const std::string sep = "\r\n";
static const uint16_t defaultport = 8080;
class HttpServer;
class ThreadData
{
public:
    ThreadData(int fd, HttpServer* ts) : sockfd(fd), tsv(ts)
    {
    }

public:
    int sockfd;
    HttpServer* tsv; // 对象,具体是static类内成员不支持this指针
};

class HttpRequest
{
public:
    void Deserialize(std::string req)
    {
        while (true)
        {
            auto pos = req.find(sep);
            if (pos == std::string::npos)
            {
                break;
            }
            std::string tmp = req.substr(0, pos);
            if (tmp.empty()) // 找到空串
            {
                break;
            }
            _req_header.push_back(tmp);     // push一行
            req.erase(0, pos + sep.size()); // 移除一行
        }
        _text = req;
    }
    void Parse()
    {
        std::stringstream ss(_req_header[0]); // 请求行
        ss >> method >> url >> http_version;  // 把三个字符串放到三个string中
        _req_path = wwwroot; // ./wwwroot
        if (url == "/" || url == "/index.html")
        {
            _req_path += "/";
            _req_path += homepage; // ./wwwroot/index.html
        }
        else
        {
            _req_path += url; // ./wwwroot/a/b/c/d.html
        }
        auto pos = _req_path.rfind(".");
        if (pos == std::string::npos)
        {
            suffix = ".html";
        }
        else suffix = _req_path.substr(pos);
    }
    void DebugPrint()
    {
        std::cout << "----------------" << std::endl;
        for (auto &line : _req_header)
        {
            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 << "_req_path: " << _req_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 _req_path;
    std::string suffix; // 文件后缀 
};

class HttpServer
{
public:
    HttpServer(uint16_t port = defaultport)
        : _port(port)
    {
        // 解析后缀对应表
        content_type.insert({".html", "text/html"});
        content_type.insert({".png", "image/png"});
        content_type.insert({".jpg", "image/jpg"});
    }
    bool Start()
    {
        _listensockfd.Socket();
        _listensockfd.Bind(_port);
        _listensockfd.Listen();
        for (;;)
        {
            std::string clientip;
            uint16_t clientport;
            int sockfd = _listensockfd.Accept(&clientip, &clientport);
            if (sockfd < 0)
                continue;
            lg(Info, "get a new link successary...sockfd: %d", sockfd);
            pthread_t tid;
            ThreadData *td = new ThreadData(sockfd, this); // 传对象上去
            pthread_create(&tid, nullptr, ThreadRun, td);
        }
    }
    static std::string ReadHtmlContent(const std::string &path)
    {
        std::ifstream in(path, 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 it = content_type.find(suffix);
        if (it == content_type.end())
        {
            return content_type[".html"];
        }
        else
        {
            return content_type[suffix];
        }
    }
    void Handler(int sockfd)
    {
        char buffer[10240];
        ssize_t n = recv(sockfd, buffer, sizeof(buffer) - 1, 0);
        if (n > 0)
        {
            buffer[n] = 0;
            std::cout << buffer;
            HttpRequest req;
            req.Deserialize(buffer); // 请求的反序列化
            req.Parse();
            req.DebugPrint();

            // // 返回响应
            std::string text;
            bool ok = true;
            text = ReadHtmlContent(req._req_path); // 要发送的字符串
            if (text.empty())
            {
                ok = false;
                std::string errhtml = wwwroot;
                errhtml += "/";
                errhtml += "error.html";
                text = ReadHtmlContent(errhtml);
            }
            std::string response_line;
            if (ok)
                response_line = "HTTP/1.0 200 OK\r\n";        // 命令行
            else
                response_line = "HTTP/1.0 404 Not Found\r\n";
            // response_line = "HTTP/1.0 302 Found\r\n";
            std::string response_header = "Content-Length: ";         // 头部
            response_header += std::to_string(text.size());           // 加上长度
            response_header += "\r\n";
            response_header += "Content-Type: ";
            response_header += SuffixToDesc(req.suffix);
            response_header += "\r\n";
            // response_header += "Location: https://www.qq.com\r\n"; // 新的地址
            std::string blank_line = "\r\n"; // 多了一个空行所以多加一个\r\n

            std::string response = response_line;
            response += response_header;
            response += blank_line;
            response += text;

            send(sockfd, response.c_str(), response.size(), 0);
        }
        close(sockfd);
    }
    static void *ThreadRun(void *args)
    {
        pthread_detach(pthread_self());
        ThreadData *td = static_cast<ThreadData *>(args);
        td->tsv->Handler(td->sockfd);
        delete td;
        return nullptr;
    }
    ~HttpServer()
    {
    }

private:
    Sock _listensockfd;
    uint16_t _port;
    std::unordered_map<std::string, std::string> content_type;
};

短连接:
在这里插入图片描述

3、Cookie

http协议是无状态的,http对登录用户的会话保持功能,就比如我们访问浏览器登录一次以后隔10分钟再访问一次不用登录了。

这种情况cookie被盗取并且个人信息泄露。
在这里插入图片描述
cookie有文件级和用户级。当cookie文件放到文件级中的时候,就是放到磁盘中了,所以下次打开浏览器依旧可以免登录了。而设置了自动消除时间,cookie就会自动消亡了。内存级的则浏览器不会免登录。

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

4、session

这种做法是将客户端信息统一放到服务端去进行管理,这样子个人信息泄露的风险比较小,但仍旧避免不了cookie被盗取的风险。而为了避免风险,那么就需要服务端那边对session id进行管理来减少风险。
在这里插入图片描述

  • 12
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

2022horse

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值