【Linux后端服务器开发】HTTP协议

news2024/10/3 2:22:27

目录

一、HTTP协议概述

二、HTTP应用层服务器实现

Util.hpp

Protocal.hpp

Http_Server.hpp

http_server.cc

indext.html


一、HTTP协议概述

请求和响应怎么保证应用层完整读取完毕了?

  1. 读取完整的一行(识别行分隔符),while(完整的一行),直到将所有的请求行+请求报头全部读完,直到空行
  2. 我们能保证把报头读完,报头有一个属性:Content-Length 表明报头长度,解析内容长度,根据内容长度读取全部内容

HTTP的请求方法

方法

说明

支持的HTTP协议版本

GET

获取资源

1.0/1.1

POST

传输实体主题

1.0/1.1

PUT

传输文件

1.0/1.1

HEAD

获得报文首部

1.0/1.1

DELETE

删除文件

1.0/1.1

OPTIONS

询问支持的方法

1.1

TRACE

追踪路径

1.1

CONNECT

要求用隧道协议连接代理

1.1

LINK

建立和资源之间的联系

1.0

UNLINE

断开连接关系

1.0

最常用的是GET和POST方法:

  • GET通过url传递参数,具体:http://ip:port/XXX/YY?name1=value1&name2=value2
  • POST通过http请求正文传递参数
  • POST通过请求正文提交参数,所以用户一般看不到,私密性更好,但是私密性≠安全性,GET和POST方法都不安全
  • URL传参参数不能太大,POST方法通过请求体正文传参,参数可以很大,也可以是其他类型数据

HTTP的状态码

类型

原因短语

1XX

Informational(信息性状态码)

接收的请求正在处理

2XX

Success(成功状态码)

请求正常处理完毕

3XX

Redirection(重定向状态码)

需要进行附加操作以完成请求

4XX

Client Error(客户端错误状态码)

处理器无法处理请求

5XX

Server Error(服务端错误状态码)

服务器处理请求出错

互联网公司并不会严格遵守HTTP协议的状态码规定,即使是服务器出错,绝大多数情况也不会返回5XX状态码,一是返回5XX的错误码会“掉价”,二是避免入侵。

HTTP的请求头Header

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

二、HTTP应用层服务器实现

服务器获取网页连接请求,返回响应体给客户端(html、图片等资源),展现网页信息

服务器获取网页的请求体之后,分析请求体的字符串数据,生成请求体结构体,根据请求体结构体生成响应体结构体

生成请求体req:①从inbuffer缓冲区读取第一行,里面包含method、url、httpversion信息;②根据url设置资源路径path;③根据path获取资源后缀;④获取资源的大小st

生成响应体resp:①根据req.path获取资源位置;②根据资源后缀返回对应的资源类型;③添加响应体的报头和空行信息;④将资源的二进制信息通过响应正文传输

编写对应的html文件,展示网页,html的根目录设置为wwwroot,index.html是默认主页,404.html是访问错误的默认页面,其他的一些网页资源也可以放在wwwroot目录下,一些二级网页可以放入wwwroot目录下的子目录中

Util.hpp

#pragma once

#include <iostream>
#include <string>
#include <fstream>
#include <fcntl.h>

using namespace std;

class Util
{
public:
    static string Get_Line(string& buffer, const string& sep)
    {
        auto pos = buffer.find(sep);
        if (pos == string::npos)
            return "";
            
        string sub = buffer.substr(0, pos);
        buffer.erase(0, sub.size() + sep.size());
        
        return sub;
    }

    static bool Read_File(const string resource, char* buffer, int size)
    {
        ifstream in(resource, ios_base::binary);
        if (!in.is_open())
            return false;

        in.read(buffer, size);

        in.close();
        return true;
    }
};

Protocal.hpp

#pragma once

#include <iostream>
#include <string>
#include <vector>
#include <sstream>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

#include "Util.hpp"

using namespace std;

static const string g_sep = "\r\n";
static const string g_default_root = "./wwwroot";
static const string g_homepage = "index.html";
static const string g_html404 = "./wwwroot/404.html";

class HttpRequest
{
public:
    HttpRequest()
    {}

    void Parse()
    {
        // 1. 从inbuffer里拿到第一行,分隔符 "\r\n"
        string line = Util::Get_Line(inbuffer, g_sep); 
        if (line.empty())
            return;

        // 2. 提取三个字段
        cout << line << endl;
        stringstream ss(line);
        ss >> method >> url >> httpversion;

        // 3. 添加web默认路径
        path = g_default_root;
        path += url;    
        if (path[path.size() - 1] == '/')
            path += g_homepage;

        // 4. 获取path对应的资源后缀
        auto pos = path.rfind(".");
        if (pos == string::npos)
            suffix = ".html";
        else
            suffix = path.substr(pos);

        // 5. 获取资源的大小
        struct stat st;
        int n = stat(path.c_str(), &st);
        if (n == 0)
            size = st.st_size;
        else
            size = -1;
    }

public:
    string inbuffer;
    // std::string reqline;
    // std::vector<std::string> reqheader;
    // std::string body;
    string method;
    string url;
    string httpversion;
    string path;
    string suffix;
    int size;
};

class HttpResponse
{
public:
    string outbuffer;
};

Http_Server.hpp

#pragma once

#include <iostream>
#include <cstring>
#include <string>
#include <unistd.h>
#include <sys/wait.h>
#include <functional>

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

// #include <unordered_map>

#include "Protocal.hpp"

static const uint16_t g_port = 8080;
static const int g_backlog = 5;

using namespace std;
using func_t = function<bool(const HttpRequest&, HttpResponse&)>;

class HttpServer
{
public:
    HttpServer(func_t func, const uint16_t& port = g_port)
        : _listenfd(-1), _func(func), _port(port)
    {}

    void Init()
    {
        _listenfd = socket(AF_INET, SOCK_STREAM, 0);
        if (_listenfd < 0)
        {
            std::cerr << "socket create error" << std::endl;
            exit(1);
        }

        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(_port);
        local.sin_addr.s_addr = INADDR_ANY;
        if (bind(_listenfd, (struct sockaddr*)&local, sizeof(local)) < 0)
        {
            cerr << "bind error" << endl;
            exit(1);
        }

        if (listen(_listenfd, g_backlog) < 0)
        {
            cerr << "listen error" << endl;
            exit(1);
        }
    }

    void Start()
    {
        while (true)
        {
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);
            int sock = accept(_listenfd, (struct sockaddr*)&peer, &len);
            if (sock < 0)
                continue;

            pid_t id = fork();
            if (id == 0)
            {
                close(_listenfd);
                if (fork() > 0)
                    exit(0);
                Handler_Http(sock);
                close(sock);
                exit(0);
            }
            waitpid(id, nullptr, 0);
        }
    }

    void Handler_Http(int sock)
    {
        // 1. 读取完整http请求
        // 2. 反序列化请求
        // 3. 回调
        // 4. 序列化响应
        // 5. send

        char buffer[1024];
        HttpRequest req;
        HttpResponse resp;
        ssize_t n = recv(sock, buffer, sizeof(buffer) - 1, 0);
        if (n > 0)
        {
            buffer[n] = 0;
            req.inbuffer = buffer;
            req.Parse();
            _func(req, resp);
            // funcs[req.path](req, resp);     
            send(sock, resp.outbuffer.c_str(), resp.outbuffer.size(), 0);
        }
    }

    // void register_cb(std::string serverceName, func_t cb)
    // {
    //     funcs.insert(std::make_pair(serverceName, cb));
    // }

private:
    int _listenfd;
    uint16_t _port;
    func_t _func;
    // std::unordered_map<std::string, func_t> funcs;
};

http_server.cc

#include "Http_Server.hpp"
#include <memory>

using namespace std;

string Suffix_To_Desc(const string suffix)
{
    string ct = "Content-Type: ";
    if (suffix == ".html")
        ct += "text/html";
    else if (suffix == ".jpg")
        ct += "application/x-jpg"; 

    ct += "\r\n";
    return ct;
}

// 1. 服务器和网页分离
// 2. 根据url获取资源位置
// 3. 根据访问资源的后缀返回对应的资源类型
bool Get_Resp(const HttpRequest& req, HttpResponse& resp)
{
    // if (req.path == "./seach")
    // {
    //     // 根据path提供特定的服务
    //     // 建立进程间通信,进程替换
    //     // 父进程将 req.parm 通过管道
    // }

    cout << "------------   http start ----------------" << endl;
    cout << req.inbuffer << endl;
    cout << "mothod: " << req.method << endl;
    cout << "url: " << req.url << endl;
    cout << "httpversion: " << req.httpversion << endl;
    cout << "path: " << req.path << endl;
    cout << "suffix: " << req.suffix << endl;
    cout << "size: " << req.size << " 字节" << endl;
    cout << "-------------  http end   ----------------" << endl << endl;

    string respline = "HTTP/1.1 200 OK\r\n";
    string respheader = Suffix_To_Desc(req.suffix);

    if (req.size > 0)
    {
        respheader += "Content-Length: ";
        respheader += to_string(req.size);
        respheader += "\r\n";
    }

    respheader += "Set-Cookie: name=12345678qwer; Max-Age=120\r\n";      // Max-Age是到期时间,单位为秒
    // 往后每次http请求,都会自动携带cookie,帮浏览器进行鉴权功能

    string respblank = "\r\n";

    string body;
    body.resize(req.size + 1);
    if (!Util::Read_File(req.path, (char*)body.c_str(), req.size))
    {
        Util::Read_File(g_html404, (char*)body.c_str(), req.size);
    }

    resp.outbuffer += respline;
    resp.outbuffer += respheader;
    resp.outbuffer += respblank;

    cout << "------------   resp start ----------------" << endl;
    cout<< resp.outbuffer << endl;
    cout << "------------   resp end   ----------------" << endl;


    resp.outbuffer += body;

    return true;
}

// ./http_server port
int main(int argc, char* argv[])
{
    if (argc != 2)
    {
        cout << "usage error" << endl;
        exit(1);
    }

    uint16_t port = atoi(argv[1]);
    
    unique_ptr<HttpServer> httpsvr(new HttpServer(Get_Resp, port));

    // httpsvr->register_cb("/", get_resp);
    // httpsvr->register_cb("/a/b/test.py", py_mothod);

    httpsvr->Init();
    httpsvr->Start();

    return 0;
}

indext.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=egde">
    <meta name="viewport" content="width=device-width, inital-scale-1.0">
    <title>我的首页</title>
</head>
</html>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/787837.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MobPush Android SDK 厂商推送限制

概述 厂商推送限制 每个厂商通道都有对应的厂商配额和 QPS 限制&#xff0c;当请求超过限制且已配置厂商回执时&#xff0c;MobPush会采取以下措施&#xff1a; 当开发者推送请求超过厂商配额时&#xff0c;MobPush将通过自有通道进行消息下发。当开发者推送请求超过厂商 QP…

elementui el-table 封装表格

ps: 1.3版本 案例&#xff1a; 完整代码&#xff1a; 可直接复制粘贴&#xff0c;但一定要全看完&#xff01; v-slot"scopeRows" 是vue3的写法&#xff1b; vue2是 slot-scope"scope" <template><!-- 简单表格、多层表头、页码、没有合并列行…

2023年度盘点:网络电视盒子哪个好?目前性能最好的电视盒子

挑选电视盒子时配置性能是最重要的&#xff0c;芯片、运存、存储全部会影响到我们的观影体验&#xff0c;想播放流畅、响应速度快&#xff0c;要在预算范围内选择配置最高的产品&#xff0c;那么哪些电视盒子称得上是目前性能最好的电视盒子呢&#xff1f;不懂网络电视盒子哪个…

自定义view - 玩转字体变色

自定义View步骤&#xff1a; 1>&#xff1a;values__attrs.xml&#xff0c;定义自定义属性&#xff1b; 2>&#xff1a;在第三个构造方法中获取自定义属性&#xff1b; 3>&#xff1a;onMeasure【不是必须的】&#xff1b; 4>&#xff1a;onDraw&#xff1a;绘制代…

【Java中的Thread线程的简单方法介绍和使用详细分析】

文章目录 前言一、run() 和 start() 方法二、sleep() 方法三、join() 方法总结 前言 提示&#xff1a;若对Thread没有基本的了解&#xff0c;可以先阅读以下文章&#xff0c;同时部分的方法已经在如下两篇文章中介绍过了&#xff0c;本文不再重复介绍&#xff01;&#xff01;…

海量遥感数据处理与云计算技术教程

详情点击链接&#xff1a;海量遥感数据处理与GEE云计算技术 一&#xff0c;GEE及开发平台 1.GEE平台及典型应用&#xff1b; 2.GEE JavaScript开发环境及常用数据资源&#xff1b; 3.JavaScript基础&#xff0c;包括变量&#xff0c;运算符&#xff0c;数组&#xff0c;判断及…

【数据结构和算法15】二叉树的实现

二叉树是这么一种树状结构&#xff1a;每个节点最多有两个孩子&#xff0c;左孩子和右孩子 重要的二叉树结构 完全二叉树&#xff08;complete binary tree&#xff09;是一种二叉树结构&#xff0c;除最后一层以外&#xff0c;每一层都必须填满&#xff0c;填充时要遵从先左后…

程序员千万别碰这3种副业!!!

最近&#xff0c;“消费降级”这个词频频被大家提及&#xff0c;某瓣上&#xff0c;“今天消费降级了吗”小组的常驻人口有36万&#xff0c;某书上&#xff0c;跟消费降级有关的笔记近7万条...... 不少网友晒出了自己消费降级后的生活&#xff1a;由从前每天一杯的星巴克变成了…

ROS noetic,ROS melodic 安装 MoveIt 并运行

ROS noetic&#xff0c;ROS melodic 安装 MoveIt 并运行 前言更新功能包版本下载依赖文件创建工作区和软件源下载源代码安装编译器缓存&#xff08;可选环节&#xff09;编译Moveit&#xff01;安装Moveit&#xff01;检测是否安装成功 前言 在安装过程中我也碰壁过很多次&…

[算法通关村] 1.3 链表的删除

上一节我们谈到了链表的头插、尾插、中间插入的方法&#xff0c;忘记的小伙伴可以复习一下&#xff1a; [算法通关村] 1.2 链表的插入 接下来&#xff0c;完成链表的删除工作&#xff0c;我们在上一节的学习中&#xff0c;分别在链表的开头、中间和结尾插入了节点&#xff0c;…

【USACO OPEN12铜组】岛屿

【USACO OPEN12铜组】岛屿 文章目录 【USACO OPEN12铜组】岛屿题目描述输入格式输出格式数据范围输入样例&#xff1a;输出样例&#xff1a; 思路code 2014. 岛 - AcWing题库 题目描述 每当下雨时&#xff0c;农夫约翰的田地总是被洪水淹没。 由于田地不是完全水平的&#xf…

Cesium态势标绘专题-扇形(标绘+编辑)

标绘专题介绍:态势标绘专题介绍_总要学点什么的博客-CSDN博客 入口文件:Cesium态势标绘专题-入口_总要学点什么的博客-CSDN博客 辅助文件:Cesium态势标绘专题-辅助文件_总要学点什么的博客-CSDN博客 本专题没有废话,只有代码,代码中涉及到的引入文件方法,从上面三个链…

(五)FLUX中的数据类型

以下内容来自 尚硅谷&#xff0c;写这一系列的文章&#xff0c;主要是为了方便后续自己的查看&#xff0c;不用带着个PDF找来找去的&#xff0c;太麻烦&#xff01; 第 5 章 FLUX中的数据类型 5.1 10 个基本数据类型 5.1.1 Boolean &#xff08;布尔型&#xff09; 5.1.1.1 …

Pytorch学习笔记 | 利用线性回归实现最简单的梯度下降 | 含代码和数据

代码 import torch import numpy as np import matplotlib.pyplot as plt import pandas as pddef make_linear_regression_data():x = np.linspace(start=10

IIC通讯故障纠错一例

1.问题描述 IIC在既有的工作中&#xff0c;用来协调两个外围芯片。一个芯片扩展IO&#xff0c;一个处理显示。仔细核对了IIC的种种配置&#xff0c;并且最终测量了输出的时钟和数据波形&#xff0c;最终的时钟线波形是这样的&#xff1a; 单片机master模式&#xff0c;输出前面…

ES6基础知识八:你是怎么理解ES6中Proxy的?使用场景?

一、介绍 定义&#xff1a; 用于定义基本操作的自定义行为 本质&#xff1a; 修改的是程序默认形为&#xff0c;就形同于在编程语言层面上做修改&#xff0c;属于元编程(meta programming) 元编程&#xff08;Metaprogramming&#xff0c;又译超编程&#xff0c;是指某类计算…

【Java练习题汇总】《第一行代码JAVA》异常处理篇,汇总Java练习题——异常的概念及处理标准格式(try、catch、finally、throws、throw)、断言机制 Assertion ~

Java练习题 异常处理篇 1️⃣ 异常处理篇 1️⃣ 异常处理篇 一、填空题 Throwable 下的两个子类是______________ 、______________ 。ArthmeticException 类表示______________ 异常&#xff0c; ArraysIndexOutOfBoundsException 表示______________ 异常。一个 try 代码后…

Linux内核提权漏洞(Dirty-pipe)复现

前言&#xff1a;CVE-2022-0847 是存在于 Linux内核 5.8 及之后版本中的本地提权漏洞。攻击者通过利用此漏洞&#xff0c;可覆盖重写任意可读文件中的数据&#xff0c;从而可将普通权限的用户提升到特权 root。 CVE-2022-0847 的漏洞原理类似于 CVE-2016-5195 脏牛漏洞&#xf…

Vue入门项目——WebApi

Vue入门——WebApi vue3项目搭建组合式API响应式APIreactive()ref() 生命周期钩子computed计算属性函数watch监听函数父子通信模板引用组合选项 vue3项目搭建 简单看下Vue3的优势吧 下载安装npm及node.js16.0以上版本&#xff08;确保安装成功可用如下代码检查版本&#xff0…

YOLOX-PAI 论文学习

1. 解决了什么问题&#xff1f; 对 YOLOX 做加速&#xff0c;在单张 Tesla V100 上取得了 42.8 42.8 42.8mAP&#xff0c;推理速度为 1 毫秒。 2. 提出了什么方法&#xff1f; 2.1 主干网络 YOLOv6 和 PP-YOLOE 都将主干网络从 CSPNet 切换到了 RepVGG。RepVGG 在推理时&a…