Consolexin's blog Consolexin's blog
首页
  • 算法基础

    • 图论
    • 字符串
    • 动态规划
    • 二分
    • 滑动窗口
    • 排序
  • Project

    • CppServer
  • 相关书籍

    • 现代C++编程
  • 书籍

    • SQL必知必会
    • MySQL必知必会
分类
标签
归档
GitHub (opens new window)

Consolexinhun

小学生
首页
  • 算法基础

    • 图论
    • 字符串
    • 动态规划
    • 二分
    • 滑动窗口
    • 排序
  • Project

    • CppServer
  • 相关书籍

    • 现代C++编程
  • 书籍

    • SQL必知必会
    • MySQL必知必会
分类
标签
归档
GitHub (opens new window)
  • README
  • day01-从一个最简单的socket开始
  • day02-不要放过任何一个错误
  • day03-高并发还得用epoll
  • day04-来看看我们的第一个类
  • day05-epoll高级用法-Channel登场
  • day06-服务器与事件驱动核心类登场
  • day07-为我们的服务器添加一个Acceptor
  • day08-一切皆是类,连TCP连接也不例外
  • day09-缓冲区-大作用
  • day10-加入线程池到服务器
  • day11-完善线程池,加入一个简单的测试程序
  • day12-将服务器改写为主从Reactor多线程模式
  • day13-支持业务逻辑自定义、完善Connection类
  • day14-重构核心库、使用智能指针
  • day15-重构Connection、修改生命周期
  • day16-使用CMake工程化
  • day17-使用EventLoopThreadPool、移交EventLoop
  • day18-HTTP有限状态转换机
  • day19-创建HTTP响应,实现HTTP服务器
  • day20-定时器的创建使用
  • day21-服务器主动关闭连接
  • day22-初步涉及日志库,定义自己的输出流LogStream
  • day23-定义前端日志库,实现同步输出
  • day24-异步日志库
  • day25-更有效的缓冲区
  • day26-监听写事件
  • day27-处理静态文件,实现POST请求
  • day28-文件服务器的简单实现,文件的展示和下载
  • day29-文件的上传
  • day30-WebBench的测试
  • CppServer
consolexinhun
2025-04-20

day18-HTTP有限状态转换机

# day18-HTTP有限状态转换机

为了实现Http服务器,首先需要对Http进行解析。对于Http请求报文。首先由四个部分组成,分别是请求行,请求头,空行和请求体组成。

其格式为

请求方法 URL HTTP/版本号
请求头字段
空行
body
1
2
3
4

例如

GET /HEELO HTTP/1.1\r\n
Host: 127.0.0.1:1234\r\n
Connection: Keep-alive\r\n
Content-Length: 12\r\n
\r\n
hello world;
1
2
3
4
5
6

可以看出,其格式是非常适合使用有限状态转换机。

为了实现这个功能,首先需要创建一个HttpContext解析器。这个解析器需要一个HttpRequest类来保存解析结果。 对于HttpRequest,他主要保存请求中的各种信息,如METHOD,URL等

class HttpRequest{
private:
    Method method_; // 请求方法
    Version version_; // 版本

    std::map<std::string, std::string> request_params_; // 请求参数

    std::string url_; // 请求路径

    std::string protocol_; // 协议

    std::map<std::string, std::string> headers_; // 请求头

    std::string body_; // 请求体
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

解析时,我们逐个字符遍历客户端发送的信息,首先设定HttpContext的初始状态为START。当我们遇到大写字母时,必然会是请求方法,因此此时,我们将此时的状态转成METHOD。当继续遇到大写字母时,说明METHOD的解析还在进行,而一旦遇到空格,说明我们的请求方法解析就结束了,我们使用start和end两个指针指向METHOD的起始位置和结束位置,获取相应的结果送入到HttpRequest中,之后更新start和end的位置,并更新当前的解析状态,进行下一个位置的解析。

class HttpContext
{
public:
    enum HttpRequestParaseState
    {
        kINVALID,         // 无效
        kINVALID_METHOD,  // 无效请求方法
        kINVALID_URL,     // 无效请求路径
        kINVALID_VERSION, // 无效的协议版本号
        kINVALID_HEADER,  // 无效请求头

        START,  // 解析开始
        METHOD, // 请求方法

        BEFORE_URL, // 请求连接前的状态,需要'/'开头
        IN_URL,     // url处理
        ...
        COMPLETE, // 完成
    };
    // 状态转换机,保存解析的状态
    bool ParaseRequest(const char *begin, int size);

private:
    std::unique_ptr<HttpRequest> request_;
    HttpRequestParaseState state_;
};

// HttpContext.cpp
bool HttpContext::ParamRequest(const char *begin, int size){
    //
    char *start = const_cast<char *>(begin);
    char *end = start;
    while(state_ != HttpRequestParaseState::kINVALID 
        && state_ != HttpRequestParaseState::COMPLETE
        && end - begin <= size){
        
        char ch = *end; // 当前字符
        switch(state_){
            case HttpRequestParaseState::START:{
                if(isupper(ch)){
                    state_ = HttpRequestParaseState::METHOD;
                }
                break;
            }
            case HttpRequestParaseState::METHOD:{
                if(isblank(ch)){
                    // 遇到空格表明,METHOD方法解析结束,当前处于即将解析URL,start进入下一个位置
                    request_->SetMethod(std::string(start, end));
                    state_ = HttpRequestParaseState::BEFORE_URL;
                    start = end + 1; // 更新下一个指标的位置
                    }
                break;
            }
            case HttpRequestParaseState::BEFORE_URL:{
                // 对请求连接前的处理,请求连接以'/'开头
                if(ch == '/'){
                    // 遇到/ 说明遇到了URL,开始解析
                    state_ = HttpRequestParaseState::IN_URL;
                }
                break;
            }
            ...
        }
        end ++;
    }
}
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

实际上这个版本并不鲁棒,而且性能并不是十分优异,但是作为一个玩具而言,并且去熟悉HTTP协议而言,还是有些价值的。

在本日额外实现了一个test_httpcontext.cpp文件用于测试HttpContext的效果。在CMakeLists.txt加入http的路径,并创建build文件,在build中运行cmake ..,之后make test_context并运行./test/test_contxt即可。

特别的,感谢若_思CSDN C++使用有限状态自动机变成解析HTTP协议 (opens new window)本文主要参考了该博客进行了修改和实现

编辑 (opens new window)
上次更新: 2025/05/21, 06:42:57
day17-使用EventLoopThreadPool、移交EventLoop
day19-创建HTTP响应,实现HTTP服务器

← day17-使用EventLoopThreadPool、移交EventLoop day19-创建HTTP响应,实现HTTP服务器→

最近更新
01
6-其他操作
05-20
02
4-联结
05-20
03
7-管理
05-20
更多文章>
Theme by Vdoing | Copyright © 2019-2025 Consolexinhun | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
×