• HTTP详解及代码实现


    HTTP

      HTTP的也称为超文本传输协议。解释HTTP我们可以将其分为三个部分来解释:超文本,传输,协议。

    超文本

      加粗样式超文本从字面意思就是超越了文本。在计算机发展初期,计算机只能传输使用文本数据,随着技术发展,出现了图片,视频,音频,超链接等,这些数据就可以统称为超文本

    传输

      传输这个词从字面上很好理解,没有什么需要介绍的,事实也是如此。http中的传输意味着数据需要由发送方发送到接收方,这是废话,但是真正需要我们注意的是,HTTP是在两点之间传输的
      那可以实现多点之间传输吗?HTTP是基于TCP的,TCP是只能两点之间传输数据的。所以HTTP也只能是两点之间传输的。
      但是HTTP3.0是基于UDP的啊,可以多点传输吗?对于HTTP/3.0(基于QUIC),虽然它是基于UDP的,但它主要是为了解决传统TCP在性能上的一些限制而设计的,并不是为了直接支持多点传输。

    协议

      协议这个词在我们的印象里面就是规则,在计算机世界里面,就是用来约束某些行为的规则。它是用计算机能够理解的语言,确立的一种计算机之间进行交流通信的一种规范,以及相关的控制和错误处理方式。


      所以HTTP是什么呢? 总结一下,就是在计算机世界里面,用来在两点之间传输图片,音频,视频,超链接等超文本数据的一种约定和规范。

    URL简述

    URL也叫做统一资源定位器,用来唯一定位全网中的资源。可以认为有了它就能够没有歧义的直接在网络中找到相应的资源。

    https://editor.csdn.net/md?not_checkout=1&spm=1010.2135.3001.5352&articleId=137425440

    上面这一串就是我当前写这篇博客的URL(当然你没有账号密码访问不了)。

    • https:// :这是协议名称,有http和https可选。
    • editor.csdn.net/ : 这是资源所在的服务器地址,也就是域名。
    • md : 这是资源的路径名。
    • ? : 这个是分割开资源路径名和查询参数的分隔符,**#**也有分隔的作用,不过上面URL中没有体现。
    • spm=1010.2135.3001.5352&articleId=137425440 : 这个是查询参数,用&分割开来,这些参数由客户端给服务端,用来指定资源。
    • # : 上面URL中没有体现这个。#后面是片段标识符id, 浏览器在加载该URL时会滚动到页面中对应的片段位置。

    状态码

      HTTP的状态码是存在于响应报文中的。
    它有以下五类:

    • 1** : 表示请求正在被处理,是一种中间状态。
    • 2** :表示请求成功被处理。
    • 3** :重定向状态码,这里的重定向指的是当前请求资源的位置发生变动,需要使用新的URL去请求新的资源。
    • 4** : 客户端的请求报文有误,服务端无法处理请求。
    • 5** : 服务端在处理请求时内部出现的问题导致无法正常处理请求。

    常见的状态码

    • 200:请求成功,响应的头部会有body数据。
    • 204:请求成功,响应的头部没有body数据。
    • 301:永久重定向,请求的资源已经不存在了,需要使用新的URL请求资源。
    • 302:临时重定向,请求的资源还在,但是需要暂时的访问新的URL。
    • 304:这个是服务端告诉客户端,资源没有修改,浏览器本地缓存的资源依旧可以使用,重定向到本地资源。
    • 400:笼统的表示客户端发送的报文有误。
    • 403:服务器禁止访问,客户端并没有错误。
    • 404:在服务端中没有找到相应的资源或是资源不存在。
    • 500:笼统的表示服务端发生了错误。
    • 501:客户端请求的功能还不支持。
    • 502:通常是服务器作为网关或者代理的时候返回的状态码,表示自己没有问题,但是发送给后端服务器时发生了错误。
    • 503:服务器繁忙,暂时无法响应客户端。

    请求方法

      出现在请求报文中,用来说明使用什么样的方法请求资源。
    在这里插入图片描述

    请求报文

      这是HTTP请求资源时发送的报文。
    在这里插入图片描述
    在这里插入图片描述
      首行包括:请求方法 + URL + HTTP版本号
    在这里插入图片描述
      首行下面的就是请求头部,使用key:value的格式,简单且利于理解,每一对key:value占据一行,行末使用\r\n回车换行来分割每一对key:value。
      头部末尾空出一行来将头部和body分割开来。

    在这里插入图片描述
      这是Body,可以发送一些数据来指定资源或者对指定资源做出处理,允许为空,若不为空,头部会使用Content-Length来表示Body长度。
      大家或许发现了Body中的数据和URL中?后面的参数很像,这并不是偶然。他们两个的作用都是为了获取指定资源或是对指定的资源做出处理,如果请求方法为POST,则参数在Body中,如果请求方法为GET,则参数在URL中,具体细节本文不做展开解释。当然Body中不仅仅局限于传递参数的作用。

    响应报文

      响应报文是服务端用来对客户端请求的响应的。
    在这里插入图片描述

    • 首行:版本号 + 状态码 + 状态码解释
    • 响应头部:跟请求头部一样使用key:value的格式。
    • Body:如果有Body,则Body和头部之间会有一个空行(\r\n),上图中的Body里面就是一个html代码,向客户端返回了一个html页面。

    HTTP常见的Header

    • Host:请求报文中。用于指明请求的服务器的域名。
    • Connection:请求和响应报文中都有。用于说明是否使用长连接,如果使用,则设为 keep-alive。如果不使用长连接,也就是使用短连接,则设为:clone
    • Content-Type:请求报文和响应报文都有。其中的value是Body数据的长度。Header和Body的区分是通过一个空行实现的,有了Content-Type字段,也就可以确定了Body的界限了。
    • Content-Type:响应报文中。用来告诉客户端返回的数据是什么格式的。
    • Accept-Type:请求报文中,用来告诉服务端自己能够接受什么类型的。
    • Content-Encoding:响应报文。用来告诉服务器返回的数据使用了什么压缩格式。
    • Accept-Encoding:请求报文:用来告诉服务端自己能够接受什么压缩格式。
    • Cookie: 请求报文和响应报文都有。用于存储客户端的少量信息. 通常用于实现会话(session)的功能;
    • Location:响应报文中。当状态码为3**时,说明为重定向,Location字段就是指明了重定向的位置,可以为URL外部资源,也可以指向本地资源(比如304)。
    • Referer: 当前页面是从哪个页面跳转过来的;

    HTTP服务器代码

    // http.cpp
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    #include 
    #include 
    #include 
    #include 
    
    #define ROOT "./wwwroot"
    #define HOMEPAGE "a.html"
    
    #include "Util.hpp"
    
    int main(int argc, char *argv[])
    {
        if (argc != 3)
        {
            std::cout << "You should " << argv[0] << " ip port\n";
            return 1;
        }
    
        int sock = socket(AF_INET, SOCK_STREAM, 0);
        if (sock < 0)
        {
            std::cout << "Fail to sock: " << strerror(errno) << std::endl;
            return 2;
        }
    
        int opt = 1;
        struct sockaddr_in local;
        local.sin_family = AF_INET;
        local.sin_port = htons(atoi(argv[2]));
        local.sin_addr.s_addr = inet_addr(argv[1]);
    
        int bind_flag = bind(sock, (struct sockaddr *)&local, sizeof(local));
        if (bind_flag < 0
        {
            std::cout << "Fail to bind: " << strerror(errno) << std::endl;
            return 3;
        }
    
        int listen_flag = listen(sock, 10);
        if (listen_flag < 0)
        {
            std::cout << "Fail to listen: " << strerror(errno) << std::endl;
            return 4;
        }
    
        // Start Server
        while (true)
        {
            struct sockaddr_in client_addr;
            socklen_t client_len = sizeof(client_addr);
            int client_sock = accept(sock, (struct sockaddr *)&client_addr, &client_len);
            if (client_sock < 0)
            {
                std::cout << "Fail to accept: " << strerror(errno) << std::endl;
                continue;
            }
    
            // char input_buffer[1024 * 10] = {0};
            // ssize_t input_size = read(client_sock, input_buffer, sizeof(input_buffer) - 1);
            // if (input_size < 0)
            // {
            //     std::cout << "Fail to read: " << strerror(errno) << std::endl;
            //     close(client_sock);
            //     continue;
            // }
    
            // char Server_buffer[1024 * 10] = {0};
            // std::cout << "[client request] " << input_buffer << std::endl;
    
            // Json::Value root;
            // int fd = open("../a.html", O_RDONLY);
            // const char *buf[1024 * 10];
            // read(fd, buf, sizeof(buf));
            // root["a"] = buf;
            // //Json::StyledWriter writer; // 这样会竖式的表示出来,便于调试
            // Json::FastWriter writer; // 这样会横式的表示出来
            // std::string s = writer.write(root);
    
            // const char *hello = "




    你好你好!!我是汤环!!!

    ";
    // sprintf(Server_buffer, "HTTP/1.0 200 OK\nConnection: keep-alive\nContent-Length: %lu\n\n%s", sizeof(hello), hello); char buffer[10240]; ssize_t s = recv(client_sock, buffer, sizeof(buffer) - 1, 0); if (s > 0) { buffer[s] = 0; std::cout << buffer << "--------------------\n" << std::endl; } std::vector<std::string> vLine; Util::cutString(buffer, "\r\n", &vLine); std::vector<std::string> vBlock; Util::cutString(vLine[0], " ", &vBlock); std::string file = vBlock[1]; // 得到客户端请求中的文件路径 std::string target = ROOT; target += file; std::string content; std::ifstream in(target); if (!in.is_open()) { } else { std::string line; while (getline(in, line)) { content += line; } in.close(); Z } std::string HttpResponse; if (content.empty()) HttpResponse = "HTTP/1.1 404 NOT FOUND\r\n"; else HttpResponse = "HTTP/1.1 200 OK\r\n"; HttpResponse += "\r\n"; HttpResponse += content; send(client_sock, HttpResponse.c_str(), HttpResponse.size(), 0); // write(client_sock, HttpResponse.c_str(), HttpResponse.size()); close(client_sock); } return 0; }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    // Util.hpp
    
    #pragma once
    
    #include 
    #include 
    
    class Util
    {
    public:
        static void cutString(const 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;
                out->push_back(s.substr(start, pos - start));
                start += (pos - start);
                start += sep.size();
            }
    
            if (start < s.size())
                out->push_back(s.substr(start));
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    // makefile
    
    http:http.cpp
    	g++ -o $@ $^ -std=c++11 -ljsoncpp
    
    .PHANY:clean
    clean:
    	rm -f http
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

         😄 创作不易,你的点赞和关注都是对我莫大的鼓励,再次感谢您的观看😄

  • 相关阅读:
    Hive窗口函数回顾
    遗传算法的改进——跳出局部最优机制的研究(选择算子、交叉算子、变异算子的改进)
    geopandas 笔记: datasets 数据集
    网络安全(黑客)—自学手册
    【web服务】nginx为什么这么受企业欢迎?看完这边文章你就懂了
    希尔排序详解
    B3612 【深进1.例1】求区间和洛谷题解
    怎么压缩视频?最新压缩技巧大分享
    小白学3D建模最常见的几个问题!
    以太坊 layer2: optimism 源码学习 (一)
  • 原文地址:https://blog.csdn.net/tang20030306/article/details/137425440