C++轻量级Web服务器TinyWebServer源码分析之http篇

这篇具有很好参考价值的文章主要介绍了C++轻量级Web服务器TinyWebServer源码分析之http篇。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

http篇简介

http类这篇个人觉得是最难同时也是最繁琐的一篇,本篇在基础知识方面,包括epoll、HTTP报文格式、状态码和有限状态机,不做赘述,所有源码分析的篇章基础知识会做单开一篇或者读者可以看小白一文看懂社长服务器来补基础知识。真的佩服那个叫社长的男人,我读代码且困难,他是自己手敲的啊!

http报文处理流程
1、浏览器端发出http连接请求,主线程创建http对象接收请求并将所有数据读入对应buffer,将该对象插入任务队列,工作线程从任务队列中取出一个任务进行处理。

2、工作线程取出任务后,调用process_read函数,通过主、从状态机对请求报文进行解析。

3、解析完之后,跳转do_request函数生成响应报文,通过process_write写入buffer,返回给浏览器端。

http类头文件源码如下:

class http_conn
{
public:
    static const int FILENAME_LEN = 200;//设置读取文件的名称m_real_file大小
    static const int READ_BUFFER_SIZE = 2048;//设置读缓冲区m_read_buf大小
    static const int WRITE_BUFFER_SIZE = 1024;//设置写缓冲区m_write_buf大小
    //报文的请求方法,本项目只用到GET和POST
    enum METHOD{GET=0,POST,HEAD,PUT,DELETE,TRACE,OPTIONS,CONNECT,PATH};
    //主状态机的状态
    enum CHECK_STATE{CHECK_STATE_REQUESTLINE=0,CHECK_STATE_HEADER,CHECK_STATE_CONTENT};
    //报文解析的结果
    enum HTTP_CODE{NO_REQUEST,GET_REQUEST,BAD_REQUEST,NO_RESOURCE,FORBIDDEN_REQUEST,FILE_REQUEST,INTERNAL_ERROR,CLOSED_CONNECTION};
    //从状态机的状态  
    enum LINE_STATUS{LINE_OK=0,LINE_BAD,LINE_OPEN};
public:
    http_conn() {}
    ~http_conn() {}

public:
	//初始化套接字地址,函数内部会调用私有方法init
    void init(int sockfd, const sockaddr_in &addr, char *, int, int, string user, string passwd, string sqlname);
    void close_conn(bool real_close = true);    //关闭http连接
    void process();
    bool read_once();//读取浏览器端发来的全部数据
    bool write();//响应报文写入函数
    sockaddr_in *get_address(){return &m_address;}//获取套接字地址
    void initmysql_result(connection_pool *connPool);//同步线程初始化数据库读取表
    int timer_flag;
    int improv;
//原文中使用线程池初始化数据库表的函数initresultFileCGI()头文件中没找到,但是具体实现文件中找到了;

private:
    void init();
    HTTP_CODE process_read();//从m_read_buf读取,并处理请求报文
    bool process_write(HTTP_CODE ret);//向m_write_buf写入响应报文数据
    HTTP_CODE parse_request_line(char *text);//主状态机解析报文中的请求行数据
    HTTP_CODE parse_headers(char *text);//主状态机解析报文中的请求头数据
    HTTP_CODE parse_content(char *text); //主状态机解析报文中的请求内容
    
    HTTP_CODE do_request();//生成响应报文
    char *get_line() { return m_read_buf + m_start_line; };//m_start_line是已经解析的字符,get_line用于将指针向后偏移,指向未处理的字符
    LINE_STATUS parse_line();//从状态机读取一行,分析是请求报文的哪一部分
    void unmap();//释放共享内存空间资源

//根据响应报文格式,生成对应8个部分,以下函数均由do_request调用
    bool add_response(const char *format, ...);
    bool add_content(const char *content);
    bool add_status_line(int status, const char *title);
    bool add_headers(int content_length);
    bool add_content_type();
    bool add_content_length(int content_length);
    bool add_linger();
    bool add_blank_line();

public:
    static int m_epollfd;
    static int m_user_count;
    MYSQL *mysql;
    int m_state;  //读为0, 写为1

private:
    int m_sockfd;
    sockaddr_in m_address;
    char m_read_buf[READ_BUFFER_SIZE];//存储读取的请求报文数据
    long m_read_idx;//缓冲区中m_read_buf中数据的最后一个字节的下一个位置
    long m_checked_idx;//m_read_buf读取的位置m_checked_idx
    int m_start_line;//m_read_buf中已经解析的字符个数
    char m_write_buf[WRITE_BUFFER_SIZE];//存储发出的响应报文数据
    int m_write_idx;//指示buffer中的长度
    
    CHECK_STATE m_check_state;//主状态机的状态
    METHOD m_method;//请求方法

//以下为解析请求报文中对应的6个变量//存储读取文件的名称
    char m_real_file[FILENAME_LEN];
    char *m_url;
    char *m_version;
    char *m_host;
    long m_content_length;
    bool m_linger;


    char *m_file_address;//读取服务器上的文件地址
    struct stat m_file_stat;
    struct iovec m_iv[2];//io向量机制iovec
    int m_iv_count;
    int cgi;        //是否启用的POST
    char *m_string; //存储请求头数据
    int bytes_to_send;//剩余发送字节数
    int bytes_have_send;//已发送字节数
    char *doc_root;//网站根目录,文件夹内存放请求的资源和跳转的html文件

    map<string, string> m_users;
    int m_TRIGMode;
    int m_close_log;

    char sql_user[100];
    char sql_passwd[100];
    char sql_name[100];
};

一、服务器接收http请求和http报文解析

浏览器端发出http连接请求,主线程创建http对象接收请求并将所有数据读入对应buffer,将该对象插入任务队列,工作线程从任务队列中取出一个任务进行处理。

各子线程通过process函数对任务进行处理,调用process_read函数和process_write函数分别完成报文解析与报文响应两个任务。

以下主从状态机解析报文流程图和解释来自社长原文:
tinyweb,项目开发与实战,Linux操作系统,服务器,前端,http
主状态机
三种状态,标识解析位置。

  • CHECK_STATE_REQUESTLINE,解析请求行

  • CHECK_STATE_HEADER,解析请求头

  • CHECK_STATE_CONTENT,解析消息体,仅用于解析POST请求

从状态机
三种状态,标识解析一行的读取状态。

  • LINE_OK,完整读取一行

  • LINE_BAD,报文语法有误

  • LINE_OPEN,读取的行不完整

对于主从状态机解析的结果定义为HTTP_CODE,分别为

  • NO_REQUEST//请求不完整,需要继续读取请求报文数据

  • GET_REQUEST//获得了完整的HTTP请求

  • BAD_REQUEST //HTTP请求报文有语法错误

  • INTERNAL_ERROR//服务器内部错误,该结果在主状态机逻辑switch的default下,一般不会触发

解析报文整体流程

process_read通过while循环,将主从状态机进行封装,对报文的每一行进行循环处理。
在HTTP报文中,每一行的数据由\r\n作为结束字符,空行则是仅仅是字符\r\n。因此,可以通过查找\r\n将报文拆解成单独的行进行解析。
判断条件

  • 主状态机转移到CHECK_STATE_CONTENT,该条件涉及解析消息体

  • 从状态机转移到LINE_OK,该条件涉及解析请求行和请求头部

  • 两者为或关系,当条件为真则继续循环,否则退出

循环体

  • 从状态机读取数据

  • 调用get_line函数,通过m_start_line将从状态机读取数据间接赋给text

  • 主状态机解析text

从状态机逻辑

从状态机负责读取buffer中的数据,将每行数据末尾的\r\n置为\0\0,并更新从状态机在buffer中读取的位置m_checked_idx,以此来驱动主状态机解析。
tinyweb,项目开发与实战,Linux操作系统,服务器,前端,http

主状态机逻辑

主状态机初始状态是CHECK_STATE_REQUESTLINE,通过调用从状态机来驱动主状态机,在主状态机进行解析前,从状态机已经将每一行的末尾\r\n符号改为\0\0,以便于主状态机直接取出对应字符串进行处理。

CHECK_STATE_REQUESTLINE

  • 主状态机的初始状态,调用parse_request_line函数解析请求行

  • 解析函数从m_read_buf中解析HTTP请求行,获得请求方法、目标URL及HTTP版本号

  • 解析完成后主状态机的状态变为CHECK_STATE_HEADER

解析完请求行后,主状态机继续分析请求头。在报文中,请求头和空行的处理使用的同一个函数,这里通过判断当前的text首位是不是\0字符,若是,则表示当前处理的是空行,若不是,则表示当前处理的是请求头。

CHECK_STATE_HEADER

  • 调用parse_headers函数解析请求头部信息

  • 判断是空行还是请求头,若是空行,进而判断content-length是否为0,如果不是0,表明是POST请求,则状态转移到CHECK_STATE_CONTENT,否则说明是GET请求,则报文解析结束。

  • 若解析的是请求头部字段,则主要分析connection字段,content-length字段,其他字段可以直接跳过,各位也可以根据需求继续分析。

  • connection字段判断是keep-alive还是close,决定是长连接还是短连接

  • content-length字段,这里用于读取post请求的消息体长度

解析完消息体后,报文的完整解析就完成了,但此时主状态机的状态还是CHECK_STATE_CONTENT,也就是说,符合循环入口条件,还会再次进入循环,这并不是我们所希望的。

为此,增加了&& line_status==LINE_OK,并在完成消息体解析后,将line_status变量更改为LINE_OPEN,此时可以跳出循环,完成报文解析任务。

CHECK_STATE_CONTENT

  • 仅用于解析POST请求,调用parse_content函数解析消息体

  • 用于保存post请求消息体,为后面的登录和注册做准备

下面是源码函数截图(代码太多了…):
initmysql_result函数是对数据库的操作封装
而 addfd 、removefd 、modfd 、setnonblocking
这几个函数都是epoll常用操作的封装,具体作用如社长源码中注释所示:
tinyweb,项目开发与实战,Linux操作系统,服务器,前端,http

read_once读取浏览器端发送来的请求报文,直到无数据可读或对方关闭连接,读取到m_read_buffer中,并更新m_read_idx。
tinyweb,项目开发与实战,Linux操作系统,服务器,前端,http

二、服务器响应请求报文

浏览器端发出HTTP请求报文,服务器端接收该报文并调用process_read对其进行解析,根据解析结果HTTP_CODE,进入相应的逻辑和模块。

其中,服务器子线程完成报文的解析与响应;主线程监测读写事件,调用read_once和http_conn::write完成数据的读取与发送。

tinyweb,项目开发与实战,Linux操作系统,服务器,前端,http

tinyweb,项目开发与实战,Linux操作系统,服务器,前端,http文章来源地址https://www.toymoban.com/news/detail-686740.html

到了这里,关于C++轻量级Web服务器TinyWebServer源码分析之http篇的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • PHP实现轻量级WEB服务器接收HTTP提交的RFID刷卡信息并回应驱动读卡器显示播报语音

     本示例使用的读卡器:RFID网络WIFI无线TCP/UDP/HTTP可编程二次开发读卡器POE供电语音-淘宝网 (taobao.com)

    2024年02月12日
    浏览(31)
  • cpp-httplib: 轻量级、高性能的C++ HTTP/HTTPS客户端和服务器库

    cpp-httplib 是一个轻量级且高效的 C++ HTTP/HTTPS 客户端和服务器库。它由 Hideaki Sone(yhirose)开发,并在 MIT 许可下发布。该项目的主要目标是提供一种简单易用的方式,在 C++ 应用程序中实现 HTTP 和 HTTPS 功能。 项目仓库地址:https://gitcode.com/yhirose/cpp-httplib cpp-httplib 可用于以下场

    2024年04月09日
    浏览(59)
  • git轻量级服务器gogs、gitea,非轻量级gitbucket

    本文来源:git轻量级服务器gogs、gitea,非轻量级gitbucket, 或 gitcode/gogs,gitea.md 结论: gogs、gitea很相似 确实轻, gitbucket基于java 不轻, 这三者都不支持组织树(嵌套组织 nested group) 只能一层组织。 个人用,基于gogs、gitea,两层结构树 简易办法: 把用户当成第一层节点、该用户的

    2024年02月07日
    浏览(52)
  • Tomcat轻量级服务器

    目录 1.常见系统架构  C-S架构 B-S架构 2.B-S架构系统的通信步骤 3.常见WEB服服务器软件 4.Tomcat服务器的配置 下载安装 环境变量配置 测试环境变量是否配置成功 测试Tomcat服务器是否配置成功  Tomcat窗口一闪而过的解决步骤 Tomcat解决乱码 介绍: C-S架构即Client/Server(客户端/服务

    2023年04月14日
    浏览(115)
  • 轻量级音乐服务器LMS

    本文软件是网友 tommyvinny 推荐的,他在 UNRAID 上安装遇到了权限问题,存在无法建立目录的情况,但似乎在群晖上没遇到。如果你也遇到相似的问题,可以试试命令行模式。 官方体验站点:https://lms-demo.poupon.dev/ 什么是 LMS ? LMS ( Lightweight Music Server )是一款自托管的轻量级

    2024年02月08日
    浏览(36)
  • 三步配置轻量级服务器nginx

    一款轻量级的 Web服务器,反向代理服务器,以及电子邮件代理服务器 主要有三个优点: 占用内存少,并发能力强 Nginx为性能优化开发,能支持五千个左右的并发响应 (Tomcat只有三百到五百) Nginx支持热部署,可以在不间断服务情况下对软件进行升级(不要用关闭服务器)

    2023年04月24日
    浏览(38)
  • 阿里云轻量级服务器安装docker

    前置知识:需要有一台阿里云服务器(或者自己电脑装虚拟机使用centos) docker理念:\\\"一次封装,到处运行\\\",只需要一次配置好环境,换到别的机子上就可以一键部署好,大大简化了操作。 docker:解决了运行环境和配置问题的软件容器。方便做持续集成并有助于整体发布的容器

    2023年04月22日
    浏览(44)
  • 腾讯云轻量级服务器部署(新手图文教程)

    相信不少同学都想将自己的项目部署到云服务器,让别人通过自己的域名就可以访问到自己的项目,而腾讯云轻量级服务器作为一个小型的应用,支持域名解析和可视化运维等。比较适合新手入门,本文服务器操作系统为Centos7。 1.购买方式 本文主要面向新手,毕竟一般只有

    2024年02月10日
    浏览(40)
  • 腾讯云轻量级服务器哪个镜像比较好?

    腾讯云轻量应用服务器镜像是什么?镜像就是操作系统,轻量服务器镜像系统怎么选择?如果是用来搭建网站腾讯云百科txybk.com建议选择选择宝塔Linux面板腾讯云专享版,镜像系统根据实际使用来选择,腾讯云百科来详细说下腾讯云轻量应用服务器镜像的选择方法: 轻量应用

    2024年02月06日
    浏览(38)
  • 轻量级服务器nginx:反向代理的具体配置

    例如:第一章 Python 机器学习入门之pandas的使用 我们在国内,如果要访问谷歌,那就无法访问,需要借助一个正向代理服务器,先将信息传给代理服务器,代理服务器所在的位置可以访问谷歌,这样就可以做到代理去谷歌取到并返回数据,并把信息发送到自己的终端上。 如果

    2023年04月25日
    浏览(55)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包