一文读懂HTTP协议:从原理到应用,女朋友都不会的原理

一文读懂HTTP协议:从原理到应用

HTTP协议

  • 在网络版计算器一文中,我们通过手动地定制协议来加深对协议的认识。虽然我说应用层协议是由程序猿自己定,但实际上已经有大佬们定义了一些现成的、又非常好用的应用层协议,供我们直接参考使用,其中 HTTP 协议就是其中之一。

什么是HTTP协议

  • HTTP(超文本传输协议)是一种应用层协议,用于在客户端和服务器之间传输超文本。它是 Web 的基础,可用于检索和提交信息,例如 HTML 文件、图像、样式表等。HTTP 是无状态的,也就是说每个请求都是独立的,服务器不会存储任何有关先前请求的信息。HTTP 协议常用于浏览器与 Web 服务器之间的通信。

认识URL

  • 平时我们俗称的网址,其实就是说的 URL。URL,全称是Uniform Resource Locator,即统一资源定位符,它是互联网上用来定位资源的标准方式。URL 是由多个部分组成,通常包含以下信息:
  • 协议:例如 http、https、ftp、file 等。
  • 域名:指向某个 IP 地址的可读性更好的别名,例如 www.example.com。
  • 端口:应用程序使用的端口号。我们所请求的网络服务对应的端口号都是众所周知的,如 HTTP 服务的默认端口号是80,而 HTTPS 服务的默认端口号是443。
  • 路径:资源在服务器上的路径。
  • 参数:向脚本传递参数。
  • 锚点:页面内部的位置。
  • URL 通常被用于定位 Web 页面、图像、视频、音频、文件等网络资源。它是一种标准化的格式,可以在浏览器中输入 URL,以访问特定的网络资源。
  • 我们平时上网的目的无非两种:一、我们想要获取资源,二、我们想要上传资源。假设我们现在想要获取资源,在我们没有获取到资源之前,这个资源在服务器上。而一个服务器上可能存在多种资源(本质就是文件),那服务器是如何找到我们需要的资源,并将该资源通过网络交给我们呢?
  • 其实我们在向服务器请求资源时,就会在 URL 内部带上资源所在的路径,这样服务器就可以通过该路径找到我们所需要的资源并交给我们。
  • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nq0913hE-1688737497873)(C:\Users\quyanliang\AppData\Roaming\Typora\typora-user-images\1688736598421.png)]

urlencode 和 urldecode

  • 在 URL 中,某些字符具有特殊含义。这些字符包括保留字符(如 /、?、& 等)和非 ASCII 字符(如中文、日文等)。因此,如果要在 URL 中包含这些字符,需要将它们进行编码。URL 编码是一种将 URL 中的特殊字符转换为标准 ASCII 字符的方法。
  • urlencode 是 URL 编码的过程,它将 URL 中的非 ASCII 字符和保留字符进行编码,以便在 URL 中安全地传输。具体来说,urlencode 会将非 ASCII 字符转换成它们的 UTF-8 编码,然后将每个字节转换成 %XX 的形式,其中 XX 是两个十六进制数字表示的字节值。转换的规则:将需要转码的字符转为十六进制,然后从右到左,取4位(不足4位直接处理),每两位做一位,前面加上 %,编码成 %XX。
  • 例如,“hello, 世界” 在进行 urlencode 之后会被转换为 “hello%2C%20%E4%B8%96%E7%95%8C”。
  • urldecode 是将 URL 编码的字符串还原为原始字符串的过程。它将 %XX 形式的编码转换为相应的字节,并将 UTF-8 编码的字节序列还原为原始的 Unicode 字符。
  • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-I1y0yTuj-1688737497874)(C:\Users\quyanliang\AppData\Roaming\Typora\typora-user-images\1688736641019.png)]

HTTP协议格式

  • 在 HTTP 协议中,客户端向服务器发送请求,服务器接收并响应请求。请求和响应都有特定的格式。
  • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TBoxVmWN-1688737497875)(C:\Users\quyanliang\AppData\Roaming\Typora\typora-user-images\1688736662551.png)]
HTTP请求报文格式
  • 请求通常由请求行、请求报头、空行和请求正文四部分组成。请求行包括请求方法、请求 URL 和 HTTP 协议版本;请求报头是一组键值对,用来描述客户端发送的请求的一些信息,例如请求的 Host、User-Agent 等。;请求正文是可选的,可以没有,通常只有在请求方法为 POST 或 PUT 时才会有请求体,用于传输客户端提交的数据。

  • 在这里插入图片描述

  • 注:请求中的 HTTP 协议版本是客户端告知服务端,客户端所采用的的 HTTP 协议版本。

HTTP相应报文格式
  • 响应报文也由三部分组成:状态行、响应报头、空行和响应正文。状态行包括 HTTP 协议版本、状态码和状态码描述。响应报头和请求报头类似,也是一组键值对,用于描述服务器发送的响应的一些信息,例如响应的 Content-Type、Content-Length 等。响应正文用于传输服务器返回的数据。

  • 在这里插入图片描述

  • 注:响应中的 HTTP 协议版本是服务端告知客户端,服务端所采用的 HTTP 协议版本。

  • 我们知道,每一层协议都需要考虑封装和解包的问题,也就是如何区分报头和有效载荷(正文)的问题?那 HTTP 是如何区分报头和有效载荷的呢?很明显,HTTP 是通过 \r\n(区分一行的内容) 和空行来区分报头和有效载荷的。

  • 现在可以将报头和有效载荷区分开来了,那如何得知有效载荷的大小呢?如果有效载荷存在,那么报头中会有一个 Content-Length 属性来标识有效载荷的大小。

HTTP Demo

  • Log.hpp

  • #pragma once
    
    #include <cstdio>
    #include <cstdarg>
    #include <string>
    #include <iostream>
    #include <ctime>
    
    // 日志等级
    #define DEBUG   0
    #define NORMAL  1
    #define WARNING 2
    #define ERROR   3
    #define FATAL   4
    
    #define LOGFILE "./Calculate.log"
    
    const char* levelMap[] = 
    {
        "DEBUG",
        "NORMAL",
        "WARNING",
        "ERROR",
        "FATAL"
    };
    
    void logMessage(int level, const char* format, ...)
    {
        // 只有定义了DEBUG_SHOW,才会打印debug信息
        // 利用命令行来定义即可,如-D DEBUG_SHOW
    #ifndef DEBUG_SHOW
        if(level == DEBUG) return;
    #endif
    
        char stdBuffer[1024];   // 标准部分
        time_t timestamp = time(nullptr);
        // struct tm *localtime = localtime(&timestamp);
        snprintf(stdBuffer, sizeof stdBuffer, "[%s] [%ld] ", levelMap[level], timestamp);
    
        char logBuffer[1024];   // 自定义部分
        va_list args;   // va_list就是char*的别名
        va_start(args, format); // va_start是宏函数,让args指向参数列表的第一个位置
        // vprintf(format, args); // 以format形式向显示器上打印参数列表
        vsnprintf(logBuffer, sizeof logBuffer, format, args);
    
        va_end(args);   // va_end将args弄成nullptr
    
        FILE *fp = fopen(LOGFILE, "a");
        // printf("%s%s\n", stdBuffer, logBuffer);
        fprintf(fp, "%s%s\n", stdBuffer, logBuffer);
        fclose(fp);
    }
    
    
  • Sock.hpp

  • #pragma once
    
    #include <sys/socket.h>
    #include <sys/types.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <iostream>
    #include <string>
    #include <cstring>
    #include "Log.hpp"
    
    class Sock
    {
    private:
        const static int backlog = 20;
    
    public:
        Sock() {}
    
        // 返回值是创建的套接字
        int Socket()
        {
            int sock = socket(AF_INET, SOCK_STREAM, 0);
            if (sock < 0)
            {
                logMessage(FATAL, "Create Socket Error! Errno:%d Strerror:%s", errno, strerror(errno));
                exit(2);
            }
            logMessage(NORMAL, "Create Socket Success! Socket:%d", sock);
            return sock;
        }
    
        // 绑定端口号
        void Bind(int sock, uint16_t port, std::string ip = "0.0.0.0")
        {
            struct sockaddr_in local;
            memset(&local, 0, sizeof local);
            local.sin_family = AF_INET;
            local.sin_port = htons(port);
            local.sin_addr.s_addr = inet_addr(ip.c_str());
            if (bind(sock, (struct sockaddr *)&local, sizeof local) < 0)
            {
                logMessage(FATAL, "Bind Error! Errno:%d Strerror:%s", errno, strerror(errno));
                exit(3);
            }
        }
    
        // 将套接字设置为监听套接字
        void Listen(int listenSock)
        {
            if (listen(listenSock, backlog) < 0)
            {
                logMessage(FATAL, "Listen Error! Errno:%d Strerror:%s", errno, strerror(errno));
                exit(4);
            }
            logMessage(NORMAL, "Init Server Success!");
        }
    
        // 接收链接,返回值是为该连接服务的套接字
        // ip和port是输出型参数,返回客户端的ip和port
        int Accept(int listenSock, std::string *ip, uint16_t *port)
        {
            struct sockaddr_in src;
            socklen_t len = sizeof(src);
            int serviceSock = accept(listenSock, (struct sockaddr *)&src, &len);
            if (serviceSock < 0)
            {
                logMessage(FATAL, "Accept Error! Errno:%d Strerror:%s", errno, strerror(errno));
                return -1;
            }
            if (ip)
                *ip = inet_ntoa(src.sin_addr);
            if (port)
                *port = ntohs(src.sin_port);
            return serviceSock;
        }
    
        // 发起连接
        bool Connet(int sock, const std::string &serverIP, const int16_t &serverPort)
        {
            struct sockaddr_in server;
            memset(&server, 0, sizeof server);
            server.sin_family = AF_INET;
            server.sin_port = htons(serverPort);
            inet_pton(AF_INET, serverIP.c_str(), &server.sin_addr);
    
            if (connect(sock, (struct sockaddr *)&server, sizeof server) == 0)
                return true;
            else
                return false;
        }
    
        ~Sock() {}
    };
    
    
  • HttpServer.hpp

  • #pragma once
    
    #include <iostream>
    #include <functional>
    #include <unistd.h>
    #include <signal.h>
    #include "Sock.hpp"
    
    using func_t = std::function<void(int)>;
    
    class HttpServer
    {
    public:
        HttpServer(const uint16_t& port, func_t func)
            : _port(port)
            , _func(func)
        {
            _listenSock = _sock.Socket();
            _sock.Bind(_listenSock, _port);
            _sock.Listen(_listenSock);
        }
    
        ~HttpServer()
        {
            if(_listenSock >= 0)
                close(_listenSock);
        }
    
        void Start()
        {
            signal(SIGCHLD, SIG_IGN);
            while(true)
            {
                std::string clientIP;
                uint16_t clientPort;
                int sockfd = _sock.Accept(_listenSock, &clientIP, &clientPort);
                if(sockfd < 0) continue;
                // 创建子进程去处理请求
                if(fork() == 0)
                {
                    close(_listenSock);
                    _func(sockfd);
                    close(sockfd);
                    exit(0);
                }
            }
        }
    
    private:
        int _listenSock;
        uint16_t _port;
        Sock _sock;
        func_t _func; // 回调函数
    };
    
    
  • HttpServer.cc

  • #include <iostream>
    #include <memory>
    #include "HttpServer.hpp"
    
    void Usage(const std::string proc)
    {
        std::cout << "\nUsage" << proc << " Port" << std::endl;
    }
    
    void HandlerHttpRequest(int sock)
    {
    	// 1、读取请求
        char buffer[10240];
        ssize_t s = recv(sock, buffer, sizeof(buffer) - 1, 0);
        // 将接收到的数据直接当成字符串进行打印
        if(s > 0)
        {
            buffer[s] = '\0';
            std::cout << buffer << "-----------------------" << std::endl;
        }
        // 2、构建一个HTTP响应
        std::string HttpResponse = "HTTP/1.1 200 OK\r\n";
        HttpResponse += "\r\n";
        HttpResponse += "<html><h3>Singing, dancing, Rap and playing basketball are my favorites</h3></html>";
        send(sock, HttpResponse.c_str(), HttpResponse.size(), 0);
    }
    
    int main(int argc, char* argv[])
    {
        if(argc != 2)
        {
            Usage(argv[0]);
            exit(1);
        }
        std::unique_ptr<HttpServer> ptr(new HttpServer(atoi(argv[1]), HandlerHttpRequest));
        ptr->Start();
    
        return 0;
    }
    
  • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-059yOReD-1688737497877)(C:\Users\quyanliang\AppData\Roaming\Typora\typora-user-images\1688736817779.png)]

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

代码细化
  • #pragma once
    
    #include <iostream>
    #include <vector>
    
    class Util
    {
    public:
        static void cutString(std::string s, const std::string &sep, std::vector<std::string> *out)
        {
            std::size_t start = 0;
            while (start < s.size())
            {
                auto 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));
        }
    };
    
    
  • cutString 接口的作用是以 sep 为分割符,将传入的字符串 s 进行切分,并尾插到输出型参数 out 中。

  • #define ROOT "./wwwroot" // Web更目录
    #define HOMEPAGE "/index.html" // 如果没有指定请求的资源,服务端默认给客户端返回该资源
    
    void HandlerHttpRequest(int sock)
    {
    	// 1、读取请求
        char buffer[10240];
        ssize_t s = recv(sock, buffer, sizeof(buffer) - 1, 0);
        // 将接收到的数据直接当成字符串进行打印
        if(s > 0)
        {
            buffer[s] = '\0';
            std::cout << buffer << "-----------------------" << std::endl;
        }
        // 2、构建一个HTTP响应
        std::vector<std::string> vline; 
        Util::cutString(buffer, "\r\n", &vline); 
        std::vector<std::string> vblock;
        Util::cutString(vline[0], " ", &vblock); // 提取HTTP请求
    
        // 请求资源的路径
        std::string file = vblock[1]; // vblock[1]是要请求资源的路径
        std::string target = ROOT; // target为请求资源的路径
        if(file == "/") file = HOMEPAGE; // 如果请求资源的路径是Web根目录,那么给客户端返回默认的资源
        target += file;
        std::cout << 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::string HttpResponse;
        // content为空,说明请求的资源不存在
        if(content.empty()) HttpResponse = "HTTP/1.1 404 NotFound\r\n";
        else HttpResponse = "HTTP/1.1 200 OK\r\n";
        HttpResponse += "\r\n";
        HttpResponse += content;
        send(sock, HttpResponse.c_str(), HttpResponse.size(), 0);
    }
    
    

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

  • ./wwwroot/a/b/index.html

  • <!DOCTYPE html>
    <html>
      <head>
        <meta charset="UTF-8">
        <title>Summer Beach</title>
        <style>
          /* 设置背景图片和背景颜色 */
          body {
            background-image: url("https://images.unsplash.com/photo-1627259317488-8ecb5120a71a?ixid=MnwxMjA3fDB8MHxwcm9maWxlLXBhZ2V8NjJ8MzQ1fHxlbnwwfHx8fDE2MTk5MjIzNzA&ixlib=rb-1.2.1");
            background-color: #a9d9e7;
            background-size: cover;
            background-position: center;
            margin: 0;
            padding: 0;
            font-family: Arial, sans-serif;
          }
          
          /* 设置标题的样式 */
          h1 {
            text-align: center;
            margin-top: 50px;
            font-size: 64px;
            color: #fff;
            text-shadow: 2px 2px #000;
          }
          
          /* 设置主要内容区域的样式 */
          #main {
            width: 80%;
            margin: 50px auto;
            background-color: rgba(255, 255, 255, 0.8);
            padding: 20px;
            box-shadow: 0px 0px 20px #000;
          }
          
          /* 设置表格的样式 */
          table {
            margin-top: 20px;
            width: 100%;
            border-collapse: collapse;
            text-align: center;
          }
          
          /* 设置表格的表头样式 */
          th {
            background-color: #222;
            color: #fff;
            font-weight: bold;
            padding: 10px;
          }
          
          /* 设置表格的单元格样式 */
          td {
            padding: 10px;
          }
          
          /* 设置按钮的样式 */
          button {
            display: block;
            margin: 30px auto;
            padding: 10px;
            border-radius: 5px;
            border: none;
            background-color: #222;
            color: #fff;
            font-size: 24px;
            cursor: pointer;
            transition: all 0.3s ease-in-out;
          }
          
          /* 设置按钮的鼠标悬停样式 */
          button:hover {
            background-color: #333;
          }
        </style>
      </head>
      <body>
        <h1>Summer Beach</h1>
        <div id="main">
          <h2>Travel Destinations</h2>
          <table>
            <tr>
              <th>Location</th>
              <th>Description</th>
              <th>Photo</th>
            </tr>
            <tr>
              <td>Maldives</td>
              <td>A tropical paradise with crystal clear waters and white sandy beaches.</td>
              <td><img src="https://images.unsplash.com/photo-1526672312463-7d28e8f0d227?ixid=MnwxMjA3fDB8MHxzZWFyY2h8MXx8bWFsZGl2ZXN8ZW58MHx8MHx8&ixlib=rb-1.2.1&
    
    
  • ./wwwroot/index.html

  • <!DOCTYPE html>
    <html>
    <head>
    	<title>远方的诗和画</title>
    	<meta charset="UTF-8">
    	<meta name="viewport" content="width=device-width, initial-scale=1.0">
    	<style>
    		/* 样式表 */
    		body {
    			background-color: #f8f8f8;
    			font-family: Arial, sans-serif;
    			font-size: 16px;
    			line-height: 1.5;
    			margin: 0;
    			padding: 0;
    		}
    		header {
    			background-color: #333;
    			color: #fff;
    			padding: 20px;
    			text-align: center;
    		}
    		h1 {
    			margin: 0;
    		}
    		main {
    			max-width: 800px;
    			margin: 20px auto;
    			padding: 0 20px;
    		}
    		img {
    			max-width: 100%;
    			height: auto;
    			margin: 20px 0;
    		}
    		blockquote {
    			font-style: italic;
    			margin: 20px 0;
    			padding-left: 20px;
    			border-left: 2px solid #333;
    		}
    	</style>
    </head>
    <body>
    	<header>
    		<h1>远方的诗和画</h1>
    		<p>走过千山万水,追寻梦中的旅途</p>
    	</header>
    	<main>
    		<h2>关于旅游的诗句</h2>
    		<blockquote>
    			<p>夜泊牛渚怀古怨,<br>不堪看点滴,<br>尽向湖心画短鸟。</p>
    			<footer>——宋·杨万里《夜泊牛渚怀古怨》</footer>
    		</blockquote>
    		<blockquote>
    			<p>远山霭漫,<br>长河落日圆;<br>这一生,<br>与谁度过。</p>
    			<footer>——唐·岑参《旅怀》</footer>
    		</blockquote>
    		<h2>风景如画的地方</h2>
    		<p>以下是一些值得一去的风景胜地:</p>
    		<ul>
    			<li>黄山</li>
    			<li>张家界</li>
    			<li>九寨沟</li>
    			<li>桂林漓江</li>
    			<li>西湖</li>
    		</ul>
    		<h2>一些美丽的图片</h2>
    		<p>以下是一些关于旅游的美丽图片:</p>
    		<img src="https://picsum.photos/id/1015/800/400" alt="图片1">
    		<img src="https://picsum.photos/id/1024/800/400" alt="图片2">
    		<img src="https://picsum.photos/id/1035/800/400"
    
    
  • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KYvHdyUC-1688737497878)(C:\Users\quyanliang\AppData\Roaming\Typora\typora-user-images\1688736897755.png)]

HTTP的方法

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

  • 我们平时的上网行为其实就两:1. 从服务器端拿取资源(GET) 2. 把客户端的数据提交到服务端中(POST、GET)。

  • 在这里插入图片描述

  • GET 是获取资源的方法,而图片和音频都是网络资源,因此使用 GET 方法可以方便地获取这些资源。此外,GET方法是幂等的,即对同一资源的多次请求返回的结果是相同的,适合用于获取静态资源。

GET 和 POST 的区别
  • 想要知道 GET 和 POST的区别就需要了解表单。

  • 表单(Form)是 HTML 中一种常用的元素,它是用来接受用户输入的一种方式。表单包含了各种表单元素,如文本框、单选框、复选框、下拉框等,用户可以通过这些元素输入信息,然后通过表单提交(Submit)按钮将这些信息发送到后端服务器。

  • 表单的基本语法如下:

  • <form action="提交地址" method="提交方式">
        表单元素
        <input type="submit" value="提交">
    </form>
    
    
  • 其中,action 属性指定了表单提交的地址,method 属性指定了表单提交的方式。常见的提交方式有两种:GET 和 POST。

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

  • 表单提交后,后端服务器会接收到表单数据,可以通过对应的程序对表单数据进行处理,并给出响应。注:表单中的数据会变成 HTTP 请求中的一部分!!!

  • <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>阿亮Joy.</title>
    </head>
    
    <body>
        <h3>须知少年凌云志,曾许人间第一流</h3>
        <form name="input" action="/a/b/index.html" method="GET">
            Username: <input type="text" name="user"> <br/>
            Password: <input type="password" name="pwd"> <br/>
            <input type="submit" value="Submit">
        </form>
    </body>
    
    </html>
    
  • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CXoVUJHH-1688737497881)(C:\Users\quyanliang\AppData\Roaming\Typora\typora-user-images\1688737051126.png)]

  • 表单提交方式为 GET 最大的特点就是通过 URL 来传递参数,会将用户名和密码等私密信息回显到 URL中,称为 URL 的一部分。因为用户名和密码成为 URL 中的参数,而我们并没有对该参数进行处理,所以就出现该网页无法正常运作的提示。

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

  • HTTP 状态码是 Web 服务器返回给客户端的 3 位数字代码,用于表示当前 HTTP 请求的处理状态。以下是常见的 HTTP 状态码及其含义:

  • 200 OK:服务器成功处理了请求。

  • 201 Created:请求已经被实现,而且有一个新的资源已经依据请求的需要而建立。

  • 204 No Content:服务器成功处理了请求,但是没有返回任何内容。

  • 301 Moved Permanently:被请求的资源已永久移动到新位置。

  • 302 Found:请求的资源现在临时从不同的 URL 响应请求,但将来可能会恢复原始地址。

  • 304 Not Modified:请求的资源在服务器上没有被修改过,可以直接使用浏览器缓存。

  • 400 Bad Request:服务器无法理解请求的格式,客户端不应该重复提交这个请求。

  • 401 Unauthorized:请求需要认证或者认证失败。

  • 403 Forbidden:服务器理解请求,但是拒绝执行该请求。

  • 404 Not Found:服务器找不到请求的资源。

  • 500 Internal Server Error:服务器发生错误,无法完成请求。

  • 502 Bad Gateway:服务器作为网关或者代理,从上游服务器收到无效响应。

  • 503 Service Unavailable:服务器当前无法处理请求,一般用于临时维护或者过载状态。

HTTP常见的报头

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

HTTP的主要特征

  • 无状态:每个 HTTP 请求都是独立的,服务器不会保存任何客户端的请求信息,因此 HTTP 被称为无状态协议。为了维护客户端状态,通常使用 Cookie 和 Session技术。

  • 可扩展:HTTP 报头可以通过添加自定义报头实现扩展功能。

  • 灵活:HTTP 可以传输任何类型的数据,如 HTML、图片、音频、视频等。

  • 明文传输:HTTP 是明文传输的,请求和响应中的所有内容都可以被窃听,因此使用 HTTPS 进行加密。

  • 请求 / 响应模型:HTTP 采用客户端-服务器模型,客户端发送请求,服务器发送响应。

  • 无连接:HTTP 协议不维护连接,连接是 TCP 协议维护的,HTTP直接发起请求和响应即可。

  • 缓存:HTTP 支持缓存,可以通过在响应报头中添加缓存信息控制客户端和服务器的缓存机制。

  • 虽然 HTTP 协议本身是无状态的,但是在实际应用中,为了实现用户的登录状态等功能,网站会在服务器端保存用户的会话状态,并分配给用户一个唯一的会话标识符(Session ID),这个会话标识符可以在每次请求时传递给服务器,服务器就可以根据这个标识符识别用户,从而实现用户状态的保持。

  • 具体来说,当用户登录时,服务器会创建一个会话对象,保存用户的相关信息,同时生成一个唯一的 Session ID,并将其发送给客户端,通常是通过 Cookie 技术实现。之后,客户端每次请求都会携带这个 Session ID,服务器就可以根据 Session ID 查找对应的会话对象,获取用户的相关信息,从而实现用户状态的保持。

  • 需要注意的是,由于 HTTP 协议是无状态的,因此服务器端保存的会话状态只存在于一定的时间范围内,一般会设置一个过期时间,超过过期时间后,会话状态会被清除。
    - [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tK44ZfrV-1688737497882)(C:\Users\quyanliang\AppData\Roaming\Typora\typora-user-images\1688737364488.png)]

  • Cookie 是基于 HTTP 协议实现的,可以分为文件级的 Cookie 文件和内存级的 Cookie 文件。

  • 文件级的 Cookie 文件是存储在用户计算机上的硬盘上,是一种持久性的 Cookie。它们的过期时间可以设置为一段时间,也可以永不过期。在访问同一个网站时,浏览器会自动发送该网站存储在本地计算机上的 Cookie文件,以便在服务器端进行身份验证和授权操作。

  • 内存级的 Cookie 文件是存储在内存中的临时 Cookie。当浏览器关闭时,它们会自动删除。内存级的 Cookie 可以用于存储一些敏感信息,如密码和银行账户信息等,以提高安全性。

  • HTTP 协议中的 Cookie 和 Set-Cookie 是 HTTP 请求和响应中的两个重要报头。其中 Cookie 是客户端请求时携带的用于身份验证和状态管理的信息,而 Set-Cookie 是服务端响应时用于设置 Cookie 的报头。

  • 具体来说,当客户端向服务端发送 HTTP 请求时,如果之前已经设置了 Cookie,则会在请求报头中添加一个 Cookie 字段,将之前设置的 Cookie 信息携带过去,供服务端进行验证和状态管理。而当服务端向客户端发送 HTTP 响应时,如果需要设置新的 Cookie,则会在响应报头中添加一个 Set-Cookie 字段,指定新的 Cookie 信息,包括 Cookie 的名称、值、过期时间、作用域等。

抓包工具

常见的抓包工具

  • 抓包工具是一种网络工具,可以捕捉和分析网络通信数据包,以便进行网络故障诊断、协议分析、网络安全评估等工作。以下是几种常用的抓包工具:
  • Wireshark:Wireshark 是一款开源的网络协议分析工具,支持多种操作系统,可以用来分析和解码多种协议。Wireshark 功能强大,支持实时抓包和离线分析。
  • tcpdump:tcpdump 是一款常用的命令行抓包工具,支持多种操作系统。它可以捕获网络接口上的数据包,并将其保存到文件中,同时也可以实时分析数据包。
  • Fiddler:Fiddler 是一款基于 Windows 平台的抓包工具,主要用于 HTTP 和 HTTPS 协议的分析和调试。它可以拦截和修改客户端和服务器之间的 HTTP 流量。
  • Charles:Charles 是一款跨平台的抓包工具,支持多种操作系统。它可以拦截和分析 HTTP 和 HTTPS 流量,支持实时分析和修改。
  • Burp Suite:Burp Suite 是一款专业的网络安全测试工具,可以用来检测和利用 Web 应用程序的安全漏洞。它包含了代理服务器、扫描器、攻击工具等多种功能。

抓包工具的原理

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

  • 抓包工具是用来捕获网络数据包的工具,其原理是通过在计算机与网络之间插入一个“中间人”来监听、记录和分析数据包的传输情况。

  • 当我们使用抓包工具进行数据包捕获时,抓包工具会将计算机和网络之间的通信流量截获并保存在本地,然后对这些数据包进行分析和解析,以便我们可以查看和分析其中的详细信息,如协议、请求头、响应头、数据内容等。

  • 抓包工具实现数据包捕获的主要方式有两种,一种是通过网络适配器(网卡)捕获数据包,另一种是通过端口监听捕获数据包。在捕获数据包后,抓包工具会将数据包保存在本地,并提供可视化的界面,让用户可以方便地查看和分析数据包的内容。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

IT枫斗者

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

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

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

打赏作者

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

抵扣说明:

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

余额充值