【计算机网络】应用层协议Http——构建Http服务服务器

06-01 1697阅读

🔥个人主页🔥:孤寂大仙V

🌈收录专栏🌈:计算机网络

🌹往期回顾🌹: 【Linux笔记】——进程间关系与守护进程

🔖流水不争,争的是滔滔不息


  • 一、Http协议
    • URL
    • urlencode 和 urldecode
    • HTTP协议请求与响应格式
    • HTTP常见方法
    • Http状态码
      • 关于重定向
      • HTTP常见Header
      • 二、构建http服务器
        • Util.hpp 工具类
        • Http.hpp应用层Http协议的代码
          • **HttpRequest 处理客户端http请求**
          • HttpRespose.hpp服务端做应答
          • Http类最核心调度逻辑
          • **main.cc**
          • 三、Cookie和Session

            一、Http协议

            HTTP(HyperText Transfer Protocol,超文本传输协议)是一种用于分布式、协作式和超媒体信息系统的应用层协议。它是万维网(WWW)数据通信的基础,设计用于客户端与服务器之间的请求-响应交互。

            HTTP 协议是客户端与服务器之间通信的基础。 客户端通过 HTTP 协议向服务器发送请求, 服务器收到请求后处理并返回响应。 HTTP 协议是一个无连接、 无状态的协议, 即每次请求都需要建立新的连接, 且服务器不会保存客户端的状态信息。

            URL

            http://www.example.com:8080/index.html
            

            我们平常所俗称的“网址”就是说的URL。上面的url,https是采用的协议,www.example.com是域名也就是ip,端口一般是:后面的,index.html是路径,也就是用户要访问的超文本文件所处在目标主机的位置。域名是ip地址具有唯一性,路径是目标主机上特定路径的一个文件,这两个就表示了全网内唯一的文件。

            但是我们发现好多URL没有端口号啊

            http://www.example.com/index.html
            

            实际上就是访问:http://www.example.com/index.html,因为有些协议有默认的端口号如http默认端口号是80.https默认端口号是443。

            urlencode 和 urldecode

            像 / ? : 等这样的字符, 已经被 url 当做特殊意义理解了,因此这些字符不能随意出现。比如, 某个参数中需要带有这些特殊字符, 就必须先对特殊字符进行转义。

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

            HTTP协议请求与响应格式

            HTTP请求

            【计算机网络】应用层协议Http——构建Http服务服务器

            【计算机网络】应用层协议Http——构建Http服务服务器

            首行:协议+url+协议版本

            请求报头(Header):请求的属性,冒号分割的键值对;每组属性之间用\r\n分割,遇到空行表示Header部分结束了,如上图。

            请求正文(Body):空行后面的内容都是Body,Body允许为空字符串,如果Body存在,则在Header 中会有一个 Content-Length 属性来标识 Body 的长度。

            换行符和空行等特殊字符,是http能够做到报头和有效载荷的分离。http协议,序列化和反序列化用的是特殊字符进行子串拼接,不依赖第三方库


            HTTP响应

            【计算机网络】应用层协议Http——构建Http服务服务器

            【计算机网络】应用层协议Http——构建Http服务服务器

            首行: [版本号] + [状态码] + [状态码解释]。

            Header: 请求的属性, 冒号分割的键值对;每组属性之间使用\r\n 分隔;遇到空行表示 Header 部分结束。

            Body: 空行后面的内容都是 Body. Body 允许为空字符串. 如果 Body 存在, 则在Header 中会有一个 Content-Length 属性来标识 Body 的长度; 如果服务器返回了一个 html 页面, 那么 html 页面内容就是在 body 中。

            HTTP常见方法

            GET方法

            你访问网址的时候,其实就是发了个 GET 请求。

            用于请求URL指定的资源。

            POST方法

            用于传输实体的主体, 通常用于提交表单数据。

            就是提交数据,参数放在请求正文中。

            PUT 方法

            用于传输文件, 将请求报文主体中的文件保存到请求 URL 指定的位置。

            HEAD 方法

            与 GET 方法类似, 但不返回报文主体部分, 仅返回响应头。

            DELETE 方法

            用于删除文件, 是 PUT 的相反方法

            OPTIONS 方法

            用于查询针对请求 URL 指定的资源支持的方法。

            Http状态码

            【计算机网络】应用层协议Http——构建Http服务服务器

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

            【计算机网络】应用层协议Http——构建Http服务服务器

            关于重定向

            HTTP 状态码 301(永久重定向) 和 302(临时重定向) 都依赖 Location 选项。

            HTTP 状态码 301(永久重定向)

            当服务器返回 HTTP 301 状态码时, 表示请求的资源已经被永久移动到新的位置。

            在这种情况下, 服务器会在响应中添加一个 Location 头部, 用于指定资源的新位置。 这个 Location 头部包含了新的 URL 地址, 浏览器会自动重定向到该地址。

            例如, 在 HTTP 响应中, 可能会看到类似于以下的头部信息

            HTTP/1.1 301 Moved Permanently\r\n
            Location: https://www.new-url.com\r\n
            

            HTTP 状态码 302(临时重定向)

            当服务器返回 HTTP 302 状态码时, 表示请求的资源临时被移动到新的位置。

            同样地, 服务器也会在响应中添加一个 Location 头部来指定资源的新位置。 浏览器会暂时使用新的 URL 进行后续的请求, 但不会缓存这个重定向。

            例如, 在 HTTP 响应中, 可能会看到类似于以下的头部信息

            HTTP/1.1 302 Found\r\n
            Location: https://www.new-url.com\r\n
            

            无论是 HTTP 301 还是 HTTP 302 重定向, 都需要依赖 Location 选项来指定资源的新位置。 这个 Location 选项是一个标准的 HTTP 响应头部, 用于告诉浏览器应该将请求重定向到哪个新的 URL 地址。

            临时重定向不是永久的,用在比如登录调整语言切换。永久重定向是永久的,用在域名变更。理解一下这两个用途的不同,就可以窥见两个重定向的不同了。

            HTTP常见Header

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

            Content-Length: Body 的长度;

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

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

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

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

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

            关于 connection 报头

            HTTP 中的 Connection 字段是 HTTP 报文头的一部分, 它主要用于控制和管理客户端与服务器之间的连接状态

            核心作用

            • 管理持久连接: Connection 字段还用于管理持久连接(也称为长连接) 。 持久连接允许客户端和服务器在请求/响应完成后不立即关闭 TCP 连接, 以便在同一个连接上发送多个请求和接收多个响应。

              持久连接(长连接)

              • HTTP/1.1: 在 HTTP/1.1 协议中, 默认使用持久连接。 当客户端和服务器都不明确指定关闭连接时, 连接将保持打开状态, 以便后续的请求和响应可以复用同一个连接。
              • HTTP/1.0: 在 HTTP/1.0 协议中, 默认连接是非持久的。 如果希望在 HTTP/1.0上实现持久连接, 需要在请求头中显式设置 Connection: keep-alive。

                语法格式

                • Connection: keep-alive: 表示希望保持连接以复用 TCP 连接。

                • Connection: close: 表示请求/响应完成后, 应该关闭 TCP 连接。

                二、构建http服务器

                基于TCP通信,服务器的传输层是用之前写的模版方法进行构造。

                Util.hpp 工具类

                #pragma once
                #include 
                #include 
                #include 
                using namespace std;
                class Util
                {
                public:
                    static bool Getoneline(string& in,string* out,const string& sep) //获取报文请求行
                    {
                        auto pos=in.find(sep);
                        if(pos==string::npos)
                        {
                            return false;
                        }
                        *out+=in.substr(0,pos);
                        in.erase(0, pos + sep.size());
                        return true;
                    }
                    static bool ReadFileConet(string &filename, string *out) // 把html文件写入应答正文
                    {
                        // version2 : 以二进制方式进行读取
                        int filesize = FileSize(filename);
                        if (filesize > 0)
                        {
                            std::ifstream in(filename);
                            if (!in.is_open())
                                return false;
                            out->resize(filesize);
                            in.read((char *)(out->c_str()), filesize);
                            in.close();
                        }
                        else
                        {
                            return false;
                        }
                        return true;
                    }
                    static int FileSize(const std::string &filename)  //获取正文长度
                    {
                        std::ifstream in(filename, std::ios::binary);
                        if (!in.is_open())
                            return -1;
                        in.seekg(0, in.end);
                        int filesize = in.tellg();
                        in.seekg(0, in.beg);
                        in.close();
                        return filesize;
                    }
                };
                

                Getoneline,是解析一行字符串,比如提取请求行。

                ReadFileConet,读取HTML、图片等资源文件。

                FileSize,获取文件大小,设置Content-Length。

                下面具体用到再具体问题具体分析。

                Http.hpp应用层Http协议的代码

                #pragma once
                #include "Tcpserver.hpp"
                #include "Util.hpp"
                #include "Log.hpp"
                #include 
                #include 
                using namespace std;
                using namespace SocketModule;
                using namespace LogModule;
                const std::string gspace = " ";
                const std::string glinespace = "\r\n";
                const std::string glinesep = ": ";
                const string wwwroot = "./wwwroot";
                const string homepage = "index.html";
                const string page_404 = "/404.html";
                class HttpRequest   //处理客户端http请求
                {
                public:
                    HttpRequest()
                        : _is_interact(false)
                    {
                    }
                    string Serialize()
                    {
                        return string();
                    }
                    void ParseReqLin(string &req_line)
                    {
                        stringstream ss(req_line);
                        ss >> _method >> _uri >> _version;
                    }
                    bool Deserialize(string &in)    //反序列化
                    {
                        string req_line;
                        bool r = Util::Getoneline(in, &req_line, glinespace);   //获取请求行
                        ParseReqLin(req_line);
                        if (_uri == "/")
                        {
                            _uri = wwwroot + _uri + homepage;
                        }
                        else
                        {
                            _uri = wwwroot + _uri;
                        }
                        //  _uri: ./wwwroot/login?username=zhangsan&password=123456
                        LOG(LogLevel::DEBUG) 
                            return true;
                        }
                        _args = _uri.substr(pos + temp.size());
                        _uri = _uri.substr(0, pos);
                        _is_interact = true;
                        return true;
                    }
                    string Uri() { return _uri; }
                    bool isInteract() { return _is_interact; }
                    std::string Args() { return _args; }
                    ~HttpRequest()
                    {
                    }
                public:
                    string _method;                         //请求方法
                    string _uri;                            //URI
                    string _version;                        //http 版本
                    unordered_map
                public:
                    HttpRespose()
                        : _blankline(glinespace)
                    {
                    }
                    string Serialize()  //序列化
                    {
                        string status_line = _version + gspace + to_string(_code) + gspace + _desc + glinespace;
                        string kv_line;
                        for (auto &head : _headers)
                        {
                            string head_line = head.first + glinesep + gspace + head.second + glinespace;
                            kv_line += head_line;
                        }
                        string resstr = status_line + kv_line + _blankline + _text;
                        return resstr;
                    }
                    bool Deserialize(string &in)
                    {
                        return true;
                    }
                    void SetTargetFile(const string &target)
                    {
                        _targetfile = target;
                    }
                    void SetCode(int code)
                    {
                        _code = code;
                        switch (_code)
                        {
                        case 200:
                            _desc = "OK";
                            break;
                        case 404:
                            _desc = "Not Found";
                            break;
                        case 302:
                            _desc = "See Other";
                            break;
                        default:
                            break;
                        }
                    }
                    void SetHeader(const string &key, const string &value)
                    {
                        auto iter = _headers.find(key);
                        if (iter != _headers.end())
                        {
                            return;
                        }
                        _headers.insert(make_pair(key, value));
                    }
                    string Uri2Suffix(const string &targetfile)
                    {
                        auto pos = targetfile.rfind('.');
                        if (pos == string::npos)
                        {
                            return "text/html";
                        }
                        string suffix = targetfile.substr(pos);
                        if (suffix == ".html" || suffix == ".htm")
                            return "text/html";
                        else if (suffix == ".jpg")
                            return "image/jpeg";
                        else if (suffix == ".png")
                            return "image/png";
                        else
                            return "";
                    }
                    bool MakeResponse()
                    {
                        _version = "HTTP/1.1";
                        if (_targetfile == "./wwwroot/redir_test") // 测试重定向
                        {
                            SetCode(302);
                            SetHeader("Location", "https://www.qq.com/");
                            return true;
                        }
                        int filesize;
                        string suffix;
                        bool re = Util::ReadFileConet(_targetfile, &_text);//读取目标文件的内容
                        if (!re)
                        {
                            // SetCode(302);
                            // SetHeader("Location","http://120.46.84.37:8080/404.html"); //通过重定向方式访问404页面
                            SetCode(404);
                            _targetfile = wwwroot + page_404;
                            Util::ReadFileConet(_targetfile, &_text);
                            filesize = Util::FileSize(_targetfile); // 拿到正文大小
                            suffix = Uri2Suffix(_targetfile);
                            SetHeader("Content-Length", to_string(filesize));
                            SetHeader("Content-Type", suffix);
                        }
                        else
                        {
                            SetCode(200);
                            filesize = Util::FileSize(_targetfile); // 拿到正文大小
                            suffix = Uri2Suffix(_targetfile);
                            SetHeader("Content-Length", to_string(filesize));
                            SetHeader("Content-Type", suffix);
                        }
                        return true;
                    }
                    void SetText(const std::string &t)
                    {
                        _text = t;
                    }
                    ~HttpRespose()
                    {
                    }
                public:
                    string _version;
                    int _code;
                    string _desc;
                    unordered_map
                public:
                    Http(uint16_t port)
                        : _tserver(make_unique
                    }
                    void HttpRequestserver(shared_ptr
                         
                        string reqstr;
                        int n = sockfd-Recv(&reqstr);
                        if (n  0)
                        {
                            cout 
                                if (_route.find(req.Uri()) == _route.end())
                                {
                                }
                                else
                                {
                                    _route[req.Uri()](req, resq);
                                    string response_str = resq.Serialize();
                                    sockfd-Send(response_str);
                                }
                            }
                            else // 静态
                            {
                                resq.SetTargetFile(req.Uri());
                                if (resq.MakeResponse()) // 封装报文
                                {
                                    string resq_str = resq.Serialize();
                                    sockfd-Send(resq_str);
                                }
                            }
                        }
                    }
                    void Start()
                    {
                        _tserver-Start([this](shared_ptr this-HttpRequestserver(sockfd, client); });
                    }
                    void RegisterService(const string name, http_func_t h)
                    {
                        string key = wwwroot + name;
                        auto iter = _route.find(key);
                        if (iter == _route.end())
                        {
                            _route.insert(make_pair(key, h));
                        }
                    }
                    ~Http()
                    {
                    }
                private:
                    unique_ptr
                public:
                    HttpRequest()
                        : _is_interact(false)
                    {
                    }
                    string Serialize()
                    {
                        return string();
                    }
                    void ParseReqLin(string &req_line)
                    {
                        stringstream ss(req_line);
                        ss  _method  _uri >> _version;
                    }
                    bool Deserialize(string &in)    //反序列化
                    {
                        string req_line;
                        bool r = Util::Getoneline(in, &req_line, glinespace);   //获取请求行
                        ParseReqLin(req_line);
                        if (_uri == "/")
                        {
                            _uri = wwwroot + _uri + homepage;
                        }
                        else
                        {
                            _uri = wwwroot + _uri;
                        }
                        //  _uri: ./wwwroot/login?username=zhangsan&password=123456
                        LOG(LogLevel::DEBUG) 
                            return true;
                        }
                        _args = _uri.substr(pos + temp.size());
                        _uri = _uri.substr(0, pos);
                        _is_interact = true;
                        return true;
                    }
                    string Uri() { return _uri; }
                    bool isInteract() { return _is_interact; }
                    std::string Args() { return _args; }
                    ~HttpRequest()
                    {
                    }
                public:
                    string _method;                         //请求方法
                    string _uri;                            //URI
                    string _version;                        //http 版本
                    unordered_map
                public:
                    HttpRespose()
                        : _blankline(glinespace)
                    {
                    }
                    string Serialize()  //序列化
                    {
                        string status_line = _version + gspace + to_string(_code) + gspace + _desc + glinespace;
                        string kv_line;
                        for (auto &head : _headers)
                        {
                            string head_line = head.first + glinesep + gspace + head.second + glinespace;
                            kv_line += head_line;
                        }
                        string resstr = status_line + kv_line + _blankline + _text;
                        return resstr;
                    }
                    bool Deserialize(string &in)
                    {
                        return true;
                    }
                    void SetTargetFile(const string &target)
                    {
                        _targetfile = target;
                    }
                    void SetCode(int code)
                    {
                        _code = code;
                        switch (_code)
                        {
                        case 200:
                            _desc = "OK";
                            break;
                        case 404:
                            _desc = "Not Found";
                            break;
                        case 302:
                            _desc = "See Other";
                            break;
                        default:
                            break;
                        }
                    }
                    void SetHeader(const string &key, const string &value)
                    {
                        auto iter = _headers.find(key);
                        if (iter != _headers.end())
                        {
                            return;
                        }
                        _headers.insert(make_pair(key, value));
                    }
                    string Uri2Suffix(const string &targetfile)
                    {
                        auto pos = targetfile.rfind('.');
                        if (pos == string::npos)
                        {
                            return "text/html";
                        }
                        string suffix = targetfile.substr(pos);
                        if (suffix == ".html" || suffix == ".htm")
                            return "text/html";
                        else if (suffix == ".jpg")
                            return "image/jpeg";
                        else if (suffix == ".png")
                            return "image/png";
                        else
                            return "";
                    }
                    bool MakeResponse()
                    {
                        _version = "HTTP/1.1";
                        if (_targetfile == "./wwwroot/redir_test") // 测试重定向
                        {
                            SetCode(302);
                            SetHeader("Location", "https://www.qq.com/");
                            return true;
                        }
                        int filesize;
                        string suffix;
                        bool re = Util::ReadFileConet(_targetfile, &_text);//读取目标文件的内容
                        if (!re)
                        {
                            // SetCode(302);
                            // SetHeader("Location","http://120.46.84.37:8080/404.html"); //通过重定向方式访问404页面
                            SetCode(404);
                            _targetfile = wwwroot + page_404;
                            Util::ReadFileConet(_targetfile, &_text);
                            filesize = Util::FileSize(_targetfile); // 拿到正文大小
                            suffix = Uri2Suffix(_targetfile);
                            SetHeader("Content-Length", to_string(filesize));
                            SetHeader("Content-Type", suffix);
                        }
                        else
                        {
                            SetCode(200);
                            filesize = Util::FileSize(_targetfile); // 拿到正文大小
                            suffix = Uri2Suffix(_targetfile);
                            SetHeader("Content-Length", to_string(filesize));
                            SetHeader("Content-Type", suffix);
                        }
                        return true;
                    }
                    void SetText(const std::string &t)
                    {
                        _text = t;
                    }
                    ~HttpRespose()
                    {
                    }
                public:
                    string _version;
                    int _code;
                    string _desc;
                    unordered_map
                public:
                    Http(uint16_t port)
                        : _tserver(make_unique
                    }
                    void HttpRequestserver(shared_ptr
                         
                        string reqstr;
                        int n = sockfd-Recv(&reqstr);
                        if (n  0)
                        {
                            cout 
                                if (_route.find(req.Uri()) == _route.end())
                                {
                                }
                                else
                                {
                                    _route[req.Uri()](req, resq);
                                    string response_str = resq.Serialize();
                                    sockfd-Send(response_str);
                                }
                            }
                            else // 静态
                            {
                                resq.SetTargetFile(req.Uri());
                                if (resq.MakeResponse()) // 封装报文
                                {
                                    string resq_str = resq.Serialize();
                                    sockfd-Send(resq_str);
                                }
                            }
                        }
                    }
                    void Start()
                    {
                        _tserver-Start([this](shared_ptr this-HttpRequestserver(sockfd, client); });
                    }
                    void RegisterService(const string name, http_func_t h)
                    {
                        string key = wwwroot + name;
                        auto iter = _route.find(key);
                        if (iter == _route.end())
                        {
                            _route.insert(make_pair(key, h));
                        }
                    }
                    ~Http()
                    {
                    }
                private:
                    unique_ptr
                        _tserver-Start([this](shared_ptr this-HttpRequestserver(sockfd, client); });
                    }
                
                    LOG(LogLevel::DEBUG) 
                    uint16_t port = std::stoi(argv[1]);
                    Enable_Console_Log_Strategy(); // 启用控制台输出
                    std::unique_ptr
                    LOG(LogLevel::DEBUG) 
                        string key = wwwroot + name;
                        auto iter = _route.find(key);
                        if (iter == _route.end())
                        {
                            _route.insert(make_pair(key, h));
                        }
                    }
                
免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们。

目录[+]

取消
微信二维码
微信二维码
支付宝二维码