HTTP协议

一、简单认识协议

什么是协议

协议就是一种约定,你客户端和服务器约定了某种方式去通讯

我们主机之间的连接通过硬件+代码可以实现,在这个连接好的基础上,再制定一种协议,通信双方通过这个协议的规则进行有条不紊的,有保障性的通信。

其实底层呢其实也就是一种大量的文本处理,这些约定好了的特殊的文本格式,使我们通信的时候更加的有保障。

我们平时使用的网址URL

我们在网络中访问一个网址的时候,我们一般会输入一段如下的字符串去访问

(https://soft.3dmgame.com/down/246856.html)

现在我们来认识一下这段网址

前面的https,指的是使用的协议名称

中间的//soft.3dmgame.com, 这个是域名(IP),域名也就是IP,浏览器会给我们自动解析

后面的/down/246856.html,是我们客户端请求的服务器上面的web资源的文件路径

注意嗷,我们如果要实现两个主机之间的通信是需要IP+端口号的, IP 确定互联网中唯一一台主机, 端口后确定该机器上给我提供服务的进程,这里的网址只有域名(IP), 没有端口号,那是因为,一般我们访问的服务,都是对所有人开放的,所以一般都是绑定的固定的端口号,这个端口号是默认知道的,比如https服务固定绑定的的就是443端口号。

好了,那么我们来复盘一下我们平时上网的操作,我们上网无非就是两件事

1.我们想要获取什么东西

2.我们想要上传什么东西

那么在这个资源没有被我们拿到的时候,在哪呢?

在服务器上,一般是Linux系统的

在这个服务器上有很多资源,也就是文件,当我们发送一个请求资源到本地时,服务器的进程打开你需要的文件, 通过我们请求的文件路径找到目标文件,读取改文件,再发送给客户端client。

在URL中的特殊字符处理

如果用户想在url中包含url本身用来作为特殊字符的字符时,那么url在形成的时候,浏览器会自动进行编码encode.

一般服务器在收到之后,需要进行转回特殊字符

例如

我们去网页搜索一个?号
在这里插入图片描述

我们搜索完后

HTTP协议_第1张图片

可以看到一个%3F的字符,为了对比我们在搜索一个hello字符

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pqR6TO0D-1689156357755)(C:\Users\maorui\AppData\Roaming\Typora\typora-user-images\image-20230710125311441.png)]

完成后可以看到请求里面有一个hello字符

HTTP协议_第2张图片

如上的例子,就可以证实,一些特殊意义的字符,确实是通过转码处理了的

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

这个规则不做掌握的要求,知道即可,毕竟那么多已经编写好了,公司说不定也不放心我们去编写。

二、HTTP协议

底层

HTTP底层实现,依旧是通过TCP协议实现的,也就是说,使用HTTP协议时,计算机之间就已经完成了三次握手的事件。

HTTP协议_第3张图片

1.关于HTTP的请求和报文格式

在报文的角度看,http可以是基于行的文本协议!

它大致分为

1.请求行

2.请求报头

3.空行

4.请求正文

每一行都是以\r\n结束,但是我们如何看待呢?

仍然是将其看成线性结构,所谓的文本不就是把**\r\n**在视觉上让我们看起来是换行了的,其本质也就是一个字符。

HTTP协议_第4张图片

2.关于HTTP demo

给大家看看水代码

#include 
#include 
#include 
#include 
#include "Http_Server.hpp"
#include "Log.hpp"
#include "Util.hpp"

// 一般http都要有自己的web根目录
#define ROOT "./wwwroot"
// 如果客户端只请求了一个/,我们默认返回首页
#define HOMEPAGE "index.html"

void Usage(char *port)
{
    std::cout << port << std::endl;
}

// 业务编写
void HandlerHttpRequest(int sock)
{
    char buffer[10240];
    ssize_t s = recv(sock, buffer, sizeof(buffer) - 1, 0);
    if (s > 0)
    {
        buffer[s] = '\0';

        // std::cout << buffer << std::endl;
        // std::cout << "----------" << std::endl;
    }
    std::cout << buffer << std::endl;

    std::vector vline;
    Util::cutString(buffer, "\n", &vline);

    for (auto e: vline)
    {
        std::cout << e << std::endl;
    }
    std::cout << "---------" << std::endl;

    std::vector vblock;
    Util::cutString(vline[0], " ", &vblock);

    // for (auto e: vblock)
    // {
    //     std::cout << e << std::endl;
    // }

    std::string file = vblock[1];
    std::string target = ROOT;

    // std::cout << "file" << file << std::endl;

    if (file == "/")
        file = "/index.html";
    target += file;
    std::cout << "target:" << target << std::endl;

    std::string content;
    std::ifstream in(target);
    if (in.is_open())
    {
        std::string line;
        while (std::getline(in, line))
        {
            content += line;
        }
        in.close();
    }

    // std::cout << content << std::endl;

    std::string HttpResponse;
    if (content.empty())
    {
        HttpResponse = "HTTP/1.1 302 NotFound\r\n";
        HttpResponse += "Location:https://www.qq.com\r\n";
    }
    else
    {  
        HttpResponse = "HTTP/1.1 200 OK\r\n";
        HttpResponse += ("Content-Type: text/html\r\n");
        HttpResponse += ("Content-Length: " + std::to_string(content.size()) + "\r\n");
        HttpResponse += "Set-Cookie: 这是一个cookie\r\n";
    } 

    HttpResponse += "\r\n";
    HttpResponse += content;

    // std::string HttpResponse = "HTTP/1.1 200 OK\r\n";
    // HttpResponse += "\r\n";
    // HttpResponse += "

ZZL is dog!!!

"; send(sock, HttpResponse.c_str(), HttpResponse.size(), 0); } int main(int argc, char *argv[]) { if (argc != 2) { Usage(argv[0]); exit(4); } std::unique_ptr httpserver(new HttpServer(atoi(argv[1]), HandlerHttpRequest)); httpserver->Start(); return 0; }
#pragma once

#include 
#include 

class Util
{
public:
    static void cutString(std::string s, const std::string &sep, std::vector *out)
    {
        std::size_t start = 0;
        while (start < s.size())
        {
            std::size_t pos = s.find(sep, start);
            if (pos == std::string::npos)
            {
                break;
            }
            std::string sub = s.substr(start, pos - start);
            out->push_back(sub);
            start += sub.size();
            start += sep.size();
        }
        if (start < s.size()) out->push_back(s.substr(start));
    }
};
#pragma once 

#include "Log.hpp"
#include "Sock.hpp"
#include 

class HttpServer
{
    using func = std::function;
    Sock httpsock;
public:
    HttpServer(uint16_t port, func HanderHttpRequest)
    :_port(port)
    ,_ip("0.0.0.0")
    ,_HanderHttpRequest(HanderHttpRequest)
    {
        _listensock = httpsock.Socket();
        httpsock.Bind(_listensock, _ip, _port);
        httpsock.Listen(_listensock);
    }

    void Start()
    {
        while (true)
        {
            std::string clientIp;
            uint16_t clientPort;
            int serversock = httpsock.Accept(_listensock, clientIp, clientPort);
            if (serversock < 0) continue;
            //连接成功
            if (fork() == 0)
            {
                close(_listensock);
                _HanderHttpRequest(serversock);
                close(serversock);
                exit(-1);
            }
            close(serversock);
        }
    }

    ~HttpServer()
    {
        if (_listensock >= 0)
        close(_listensock);
    }
private:
    std::string _ip;
    uint16_t _port;
    int _listensock;
    func _HanderHttpRequest;
};
#pragma once 

#include 
#include 
#include 
#include 
#include 

#define DEBUG   0
#define NORMAL  1
#define WORNING 2
#define ERROR   3
#define FATAL   4

//映射
const char* gLevel[] = {
    "DEBUG",
    "NORMAL",
    "WORNING",
    "ERROR",
    "FATAL"
};

#define DEBUG_SHOW

void logMessage(int level, char* format, ...)
{
#ifndef DEBUG_SHOW
    if (level == DEBUG) return;
#endif
    //标准
    char stdBuffer[1024];
    const time_t curt = time(nullptr);
    struct tm* _curt = localtime(&curt);
    snprintf(stdBuffer, sizeof stdBuffer, "[%s] [%d年%d月%d日%d时%d分%d秒]", gLevel[level],
        _curt->tm_year + 1900, _curt->tm_mon + 1, _curt->tm_mday, (_curt->tm_hour + 12 )% 24 + 12, _curt->tm_min, _curt->tm_sec);

    //自定义
    char logBuffer[1024];
    va_list args;
    va_start(args, format);
    vsprintf(logBuffer, format, args);

    FILE* fd = fopen("./LogCalServer", "a");
    fprintf(fd, "%s-%s\n", stdBuffer, logBuffer);
    fclose(fd);
}
#pragma once

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "Log.hpp"

class Sock
{
public:
    Sock(){}

    int Socket()
    {
        int sock = socket(AF_INET, SOCK_STREAM, 0);
        if (sock < 0)
        {
            exit(1);
        }
        logMessage(NORMAL, "creat sock success sock:%d", sock);

        return sock;
    }

    void Bind(int sock, const std::string ip, const uint16_t port)
    {
        struct sockaddr_in addr;
        memset(&addr, 0, sizeof addr);
        socklen_t len = sizeof addr;
        addr.sin_family = AF_INET;
        addr.sin_port = htons(port);
        addr.sin_addr.s_addr = inet_addr(ip.c_str());

        if (bind(sock, (struct sockaddr*)&addr, len) < 0)
        {
            exit(2);
        }
        logMessage(NORMAL, "bind success");
    }

    void Listen(int listensock)
    {
        if (listen(listensock, 20) < 0)
        {
            exit(3);
        }
        logMessage(NORMAL, "set listen sock success");
    }

    int Accept(int listensock, std::string& ip, uint16_t& port)
    {
        struct sockaddr_in retaddr;
        socklen_t len = 0;

        while (true)
        {
            int sock = accept(listensock, (struct sockaddr*)&retaddr, &len);
            if (sock < 0)
            {
                logMessage(ERROR, "accpet fail again");
                continue;
            }

            ip = inet_ntoa(retaddr.sin_addr);
            port = ntohs(retaddr.sin_port);
            logMessage(NORMAL, "accpet success client-ip:%s port:%d", ip.c_str(), port);

           return sock;
        }
    }

    ~Sock(){}
};



    
    
    宝宝的世界


    

这是一个linux学习网址

Username:
Password:
一张图片



    
    
    宝宝的世界


    

你好呀 客人!!

3.方法

在请求行中的方法

这里我们在明确一下,我们平时的上网行为就两种

  1. 从服务器端拿下来资源数据
  2. 把客户端的数据提交到服务器

但是把数据拿下来和把数据提交,是需要指明方法的,这里的方法常用的有

  • GET :服务器端拿数据, 客户端提交数据
  • POST :客户端提交数据,不能用于服务器拿数据

GET:表单中用户提交的数据通过url传参,也就是说,通过GET上传的数据,会在url中体现出来,没有私密性

POST:表单中用户提交的数据通过转成http request中的一部分传参,它是通过正文提交参数的,不会回显,一般的私密性是有保障的

注意这里只是私密性,安全性需要通过加密和解密来保障。

4.状态码

  • 1XX 信息类状态码 接受的请求正在处理
  • 2XX 成功状态码 请求正常处理完毕
  • 3XX 重定向状态码 需要进行附加操作以完成请求
  • 4XX 客户端错误状态码 服务器无法处理请求
  • 5XX 服务器错误状态码 服务器请求出错

常见的状态码,比如200(OK), 404(Not Found), 403(Forbidden), 302(Redirect, 重定向), 504(Bad Gateway)

301 永久重定向

302、307 临时重定向

5.HTTP常见Header

Content-Type:数据类型(text/html等)

Content-Length:Body长度

Host:客户端告知服务器,所请求的资源时在哪个主机的哪个端口上;

User-Agent:声明用户的操作系统和浏览器版本信息

referer:当前页面是从哪个页面跳转过来的

Location:搭配3xx状态码使用,告诉客户端接下来要去哪里访问;

Cookie:用于在客户端存储少量信息,通常用于实现会话(session)的功能

会话管理

http特征:

  • 简单快速
  • 无连接
  • 无状态

说的是无状态,但是我们实际使用的时候,一般网站是会记录下我的状态的,这个是浏览器做的,不是http做的

cookie : 保存用户的临 时文件,登陆密码等

set_cookie:向用户写入一个cookie

 HttpResponse += "Set-Cookie: 这是一个cookie\r\n";

为了安全,cookie是不直接保存用户名和密码的,一般会生成一个session id保护自己的私密信息。

HTTP协议_第5张图片

Connection

  • Connection: keep-alive :长连接

  • Connection: close:短链接

一张完整的网页,是由非常多的资源构成的。

三、工具推荐

  1. POSTMAN

让客户端手动的向服务器发送HTTP请求

  1. fiddler

进行抓包的工具,主要是抓取HTTP的
原理:在我们进行网络请求的时候,先交给fiddler,再通过fiddler发送出去
HTTP协议_第6张图片

四、HTTP的问题

我们利用上面的代码实现一个提交表单的操作,也就是我们平时的提价密码登陆的操作

HTTP协议_第7张图片

我们点击登陆后,我们再使用fiddler工具抓包一下

HTTP协议_第8张图片

我擦勒,大家发现没,我们输入的密码再发送过去的请求中体现了出来,woc,如果有人简单的抓一下包,这踏马不就是把我们的密码给别人看嘛,哈哈哈
HTTP携带数据的时候,是明文的
所以HTTP面对的问题,它是不安全的,所以我们需要引入下一个东西,HTTPS

你可能感兴趣的:(计算机网络,http,网络协议,网络)