LINUX网络编程:http

目录

1.认识http请求的字段

2.HTTP请求类

3.认识HTTP应答字段

4.HTTP应答类

5.源代码


协议就是一种约定,http也并不例外,使用http也无非就是,定义一个http请求的结构体,将结构体序列化为字符串,发送给服务器,服务器接收字符串,将字符串反序列化为结构化的数据,处理这些数据,将结果返回给客户端。

1.认识http请求的字段

这就是一个完整的http请求,这请求是由浏览器发送。

两张图对照着看

请求方法:GET

URI:/ 是请求服务器的根目录(请求服务器的哪一个目录或者服务)

HTTP版本:HTTP/1.1

Host,Connettion,User_Agent等等都是浏览器添加的请求报头。

因为有一个空行并且请求正文是空的,所以报头后面有两个空行。

2.HTTP请求类

因为请求是浏览器发送的,所以http请求类是不需要反序列化的,将http发送给我的请求序列化即可。

http请求发过来就是一个长字符串,我们要做的就是将字符串一个一个的解析出来。

1.第一步以"\r\n"为分隔符,将每一行拆分出来。

2.读到的第一行一定是状态行,将状态行的每个字段都拆出来。

如果请求方法是GET还有判断URI中是否有参数,因为GET方法提交表单的时候,参数是在URI中的,参数通常以?分割。

就像这样49.233.244.186:8888/login?user=41234&password=1234123

49.233.244.186:8888/login?user=41234&password=1234123将参数分离之后还需要判断URI请求的服务或者目录是否存在,如果URI访问的目录或者服务不存在返回404页面。

3.读到空行之前,读到的一定是请求报头,以": "为分隔符,将报头的key和value分离出来,并存储到unordered_map中。

4.读到空行之后说明,从此刻开始,剩下的只有正文部分了,直接保存起来即可。

3.认识HTTP应答字段

HTTP状态码:对请求的应答结果,可以去查一下http状态码表。

响应正文:例:可以返回一个网页,或图片,浏览器就会对你返回的内容做解析和渲染。

响应报头:这也是有一张表的,可以去查看一下,如果响应正文不是空的,响应报头就必须添加Content-Lenth,Content-Type,这样浏览器才知道你返回的内容到底是什么。

4.HTTP应答类

判断http请求是资源还是服务

1.如果http请求的是一个服务器的资源,要以二进制的方式将服务器的资源保存到字符串中,因为资源可能是图片,可能是视频。

判断读到的内容是否为空,如果为空说明,http请求了一个不存在的资源,这时候需要将状态码设置为404,构建应答,返回404页面。

不为空就添加对应的状态码,

添加响应头"Content-Type"和Content-Length,

添加响应正文

将应答返回

2.如果http请求是一个服务,需要将这个服务回调出去,将服务于http协议解耦。

5.源代码

cpp 复制代码
#pragma once
#include <iostream>
#include "log.hpp"
#include <vector>
#include <string>
#include <unordered_map>
#include <fstream>
#include <sstream>
static const std::string sep = "\r\n";// http分隔符
static const std::string blank = " "; //空格
static const std::string headerSep = ": ";//请求头响应头分隔符
static const std::string webroot = "./webroot";//服务器的根目录
static const std::string httpVersion = "http/1.0";//http版本
static const std::string suffixSep = ".";//文件类型分隔符
static const std::string defaultpage = "index.html";//主页
static const std::string argsep = "?";//get提交表单分隔符

class httpReq;
class httpRep;
//using fun_t = std::function<std::shared_ptr<httpRep> (std::shared_ptr<httpReq>&)>;

using fun_t = std::function<std::shared_ptr<httpRep>(std::shared_ptr<httpReq>)>;
class httpReq
{
private:
    std::string getOneLine(std::string &reqstr) // 获取请求的一行
    {
        if (reqstr.empty()) // 判断请求是否为空
        {
            Log(Error, "reqstr empty");
            return std::string();
        }

        auto pos = reqstr.find(sep);
        if (pos == std::string::npos) // 判断是否找到
        {
            Log(Error, "reqstr not found sep");
            return std::string();
        }

        std::string line = reqstr.substr(0, pos);
        reqstr.erase(0, pos + sep.size());
        return line.empty() ? sep : line; // 如line为空说明读到空行
    }

public:
    httpReq(const std::string root = webroot, const std::string blanksep = sep)
        : _root(root), _blankLine(blanksep)
    {
    }

    void serialize()
    {
    }

    bool parseLine() // 解析请求行
    {
        if (_requestLine.empty())
        {
            return false;
        }
        // 解析请求行
        std::stringstream ss(_requestLine); // 1以空格为分隔符自动解析字符串
        ss >> _reqMethod >> _url >> _httpVersion;
        _path += webroot;

        // 解析参数
        if (strcasecmp(_reqMethod.c_str(), "get") == 0)
        {
            //Log(Debug, "before url %s", _url.c_str());
            auto pos = _url.find(argsep);
            if (pos != std::string::npos) // 有参数
            {   
                _args = _url.substr(pos + argsep.size());
                //Log(Info, "args: %s", _args);
                _url.resize(pos);
            }
            //Log(Debug, "after url %s", _url.c_str());
        }

        _path += _url;
        if (_path[_path.size() - 1] == '/')//如果请求的是根目录,跳转到主页
        {
            _path += defaultpage;
        }

        for (auto s : _requestHeader)
        {
            auto pos = s.find(headerSep);
            std::string k = s.substr(0, pos);
            std::string v = s.substr(pos + headerSep.size());
            _kv.insert(std::make_pair(k, v));
        }
        return true;
    }

    bool Deserialize(std::string &reqstr) // 反序列化
    {
        _requestLine = getOneLine(reqstr);

        while (true)
        {
            std::string line = getOneLine(reqstr);
            if (line.empty()) // getOneline 失败
            {
                break;
            }
            else if (line == sep) // 读到空行
            {
                _text = reqstr;
                break;
            }
            else // 读到请求头
            {
                _requestHeader.push_back(line);
            }
        }
        return parseLine();
    }

    bool isServiceReq() //是否为路径服务请求
    {
        return !_text.empty() || !_args.empty();
    }
    const std::string &path()
    {
        return _path;
    }

    const std::string &method()
    {
        return _reqMethod;
    }

    const std::string &text()
    {
        return _text;
    }

    const std::string &args()
    {
        return _args;
    }

    
    void pprint()
    {
        std::cout << "###" << _reqMethod << std::endl;
        std::cout << "###" << _url << std::endl;
        std::cout << "###" << _path << std::endl;
        std::cout << "###" << _httpVersion << std::endl;
        for (auto it : _kv)
        {
            std::cout << "@@@" << it.first << ": " << it.second << endl;
        }
        std::cout << "$$$" << _text << std::endl;
    }

    std::string getSuffix()//获取返回资源的类型
    {
        if (_path.empty())
        {
            return std::string();
        }
        auto pos = _path.rfind(suffixSep);
        if (pos == std::string::npos)
        {
            return ".html";
        }
        else
        {
            return _path.substr(pos);
        }
    }

private:
    std::string _requestLine;                // http请求第一行
    std::vector<std::string> _requestHeader; // http请求头
    std::string _blankLine;                  // 空行
    std::string _text;                       // http正文

    // 解析出每行具体内容
    std::string _root;                                // web的根目录
    std::string _reqMethod;                           // 请求方法
    std::string _url;                                 // url
    std::string _httpVersion;                         // http版本
    std::string _path;                                // 访问资源的路径
    std::string _args;                                // 请求的参数
    std::unordered_map<std::string, std::string> _kv; // 请求头的kv模型
};

class httpRep
{
public:
    httpRep(const std::string version = httpVersion, const std::string b = sep)
        : _httpVersion(version), _blankLine(b)
    {
    }

    void addStatusLine(const int code, const std::string &descrip)//添加状态行
    {
        _statuCode = code;
        _codeDescripetion = descrip;
    }

    void addHander(const std::string k, const std::string v)//添加响应头
    {
        _kv.insert(std::make_pair(k, v));
    }

    void addText(std::string &text)//添加响应文
    {
        _text = text;
    }

    std::string Serialize()
    {
        //序列化状态行
        _statusLine = _httpVersion + blank + std::to_string(_statuCode) + blank + _codeDescripetion + sep;
        //序列化响应头
        for (auto it : _kv)
        {
            _respondHeader += it.first;
            _respondHeader += headerSep;
            _respondHeader += it.second;
            _respondHeader += sep;
        }

        //构建应答
        std::string respond;
        respond += _statusLine;
        respond += _respondHeader;
        respond += sep;
        respond += _text;
        // std::cout << respond << std::endl;
        // Log(Info, "%s", _statusLine.c_str());
        // Log(Info, "%s", _respondHeader.c_str());
        return respond;
    }

    void pprint()
    {
        std::cout << "###" << _httpVersion << std::endl;
        std::cout << "###" << _statuCode << std::endl;
        std::cout << "###" << _codeDescripetion << std::endl;
        std::cout << "###" << _text << std::endl;
    }

private:
    // 构建响应必要的字段
    std::string _httpVersion;                         // http版本
    int _statuCode;                                   // 状态码
    std::string _codeDescripetion;                    // 状态码描述
    std::unordered_map<std::string, std::string> _kv; // 响应报头的kv模型

    // 构建响应的必要行
    std::string _statusLine;    // 状态行
    std::string _respondHeader; // 请求头
    std::string _blankLine;     // 空行
    std::string _text;          // 正文
};

class Factor
{
public:
    static std::shared_ptr<httpReq> BuildHttprequest() //使用智能指针构建应答
    {
        return std::make_shared<httpReq>();
    }

    static std::shared_ptr<httpRep> BuildHttprepond()
    {
        return std::make_shared<httpRep>();
    }
};

class httpserver
{
public:
    httpserver()
    {
        _mime.insert(std::make_pair(".html", "text/html"));
        _mime.insert(std::make_pair(".jpg", "image/jpeg"));
        _mime.insert(std::make_pair(".png", "application/x-plt"));

        _Statuscode_Descripetion.insert(std::make_pair(100, "continue"));
        _Statuscode_Descripetion.insert(std::make_pair(200, "ok"));
        _Statuscode_Descripetion.insert(std::make_pair(301, "Moved Permanently")); // 永久重定向
        _Statuscode_Descripetion.insert(std::make_pair(302, "Found"));             // 临时重定向
        _Statuscode_Descripetion.insert(std::make_pair(400, "Bad Request"));
        _Statuscode_Descripetion.insert(std::make_pair(404, "Not Found"));
        _Statuscode_Descripetion.insert(std::make_pair(404, "Not Found"));
    }

    std::string readFileContent(const std::string &path, int &filesize)
    {
        // std::cout<< path <<std::endl;
        std::ifstream in(path, std::ios::binary);
        if (!in.is_open())
        {
            return std::string();
        }
        in.seekg(0, in.end);
        filesize = in.tellg();
        in.seekg(0, in.beg);
        // std::cout << filesize << std::endl;
        if (filesize < 0)
        {
            filesize = 0;
        }
        std::string content;
        content.resize(filesize);
        in.read((char *)content.c_str(), filesize);
        in.close();
        return content;
    }

    void addhander(std::string path, fun_t handler)
    {
        std::string tmp = webroot + path;
        _funcs.insert(std::make_pair(tmp, handler));
    }

#define VERSION_1
    std::string httpHandler(std::string req)
    {
#ifdef VERSION_1
        std::cout << req << std::endl;
        auto request = Factor::BuildHttprequest();
        request->Deserialize(req);
        //Log(Info, "method %s", request->method().c_str());
        // Log(Info,"s","redir ----------------------");
        //  if(request->path() == "./webroot/redir")//进行重定向
        //  {
        //      code = 302;
        //      respond->addHander("Location", "https://www.csdn.net/?spm=1011.2266.3001.4476");
        //      respond->addStatusLine(code, _Statuscode_Descripetion[code]);

        // }
        if (request->isServiceReq())//当前请求的是一个服务
        {
            Log(Info, "method %s", request->method().c_str());
            Log(Info, "method %s", request->path().c_str());
            auto response = _funcs[request->path()](request);
            return response->Serialize();
        }
        else // 当前请求的是一个服务器资源
        {
            int code = 200;
            int filesize = 0;
            auto respond = Factor::BuildHttprepond();
            std::string content = readFileContent(request->path(), filesize);
            if (content.empty())//没有读到任何内容,说明请求不存在
            {
                code = 404;
                respond->addStatusLine(code, _Statuscode_Descripetion[code]);
                respond->addHander("Content-Type", ".html");
                std::string content404 = readFileContent("./webroot/404.html", filesize);
                respond->addText(content404);
            }
            else//请求存在
            {
                respond->addStatusLine(code, _Statuscode_Descripetion[code]);/
                std::string suffix = request->getSuffix();//获取资源的后坠
                respond->addHander("Content-Type", _mime[suffix]);
                respond->addHander("Content-Length", std::to_string(filesize));
                respond->addText(content);
            }
            return respond->Serialize();
        }

#else
        std::cout << "version control" << endl;
#endif
    }

private:
    std::unordered_map<std::string, std::string> _mime;            // 文件后缀,对应的content type类型
    std::unordered_map<int, std::string> _Statuscode_Descripetion; // 状态码 对应的描述
    std::unordered_map<std::string, fun_t> _funcs;                  // 将请求回调出去
};
相关推荐
麻瓜也要学魔法3 小时前
链路状态路由协议-OSPF
网络
Estar.Lee3 小时前
查手机号归属地免费API接口教程
android·网络·后端·网络协议·tcp/ip·oneapi
傻啦嘿哟4 小时前
代理IP在后端开发中的应用与后端工程师的角色
网络·网络协议·tcp/ip
Red Red4 小时前
网安基础知识|IDS入侵检测系统|IPS入侵防御系统|堡垒机|VPN|EDR|CC防御|云安全-VDC/VPC|安全服务
网络·笔记·学习·安全·web安全
向阳12184 小时前
Dubbo HTTP接入之triple协议
网络协议·http·dubbo
亚远景aspice6 小时前
ISO 21434标准:汽车网络安全管理的利与弊
网络·web安全·汽车
Estar.Lee6 小时前
时间操作[计算时间差]免费API接口教程
android·网络·后端·网络协议·tcp/ip
友友马6 小时前
『 Linux 』网络层 - IP协议(一)
linux·网络·tcp/ip
码老白7 小时前
【老白学 Java】Warshipv2.0(二)
java·网络