使用 C++实现的 Web 服务器,代码规范参考的是Google 命名约定

功能

主体框架参照了WebServer项目,同时增加了以下功能:

  • 基于注册的静态请求跳转(如”/“跳转到”/index.html”)
  • 基于回调函数的动态请求处理(登录/数据库增删改查)
  • POST 请求解析(原项目做了”urlencoded”格式请求体的解析,改成了”json”格式的解析)
  • 基于”MySQL Connector/C++”的数据库系统(原项目使用的是 C 的数据库函数)
  • 利用”gtest”为每一个模块都写了模块测试

代码仓库 | Github
前端代码仓库 | Github
相关笔记 | 遗世の私语

环境安装与测试

  • 配置 mysql

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    # 安装 mysql 数据库(服务器+客户端)
    # 并配置用户账号密码和代码中对应
    # 参考链接: http://yishiyu.world/2023/02/08/TimelineServer-总结-数据库/
    # 默认为 root explosion

    # 创建数据库
    CREATE DATABASE timelineserver;
    CREATE TABLE IF NOT EXISTS users(
    user_id BIGINT(20) UNSIGNED AUTO_INCREMENT,
    user_name VARCHAR(40) NOT NULL,
    user_passwd VARCHAR(40) NOT NULL,
    PRIMARY KEY (user_id)
    );
    CREATE TABLE IF NOT EXISTS tasks(
    task_id BIGINT(20) UNSIGNED AUTO_INCREMENT,
    user_id BIGINT(20) UNSIGNED NOT NULL,
    time DATE NOT NULL,
    task VARCHAR(300) NOT NULL,
    priority INT DEFAULT 0 NOT NULL,
    PRIMARY KEY (task_id)
    );
  • 安装依赖库

    1
    2
    3
    4
    5
    6
    7
    sudo apt update

    # 安装 mysql connector
    sudo apt install libmysqlcppconn-dev

    # 安装 json11
    sudo apt install libjson11-1-dev
  • 编译

    1
    2
    git clone https://github.com/yishiyu/TimelineServer.git
    bash ./build.sh
  • 启动

    1
    2
    # cd 到 build 目录
    ./TimelineServer

    项目结构

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
.
├── build // 构建目录
├── build.sh // 构建脚本
├── CMakeLists.txt
├── code // 源代码
│ ├── buffer
│ ├── http
│ ├── log
│ ├── mux // IO 多路复用
│ ├── pool
│ ├── server
│ ├── timer
│ ├── main.cc // 代码入口
│ ├── routers.cc
│ └── routers.h // 路由函数
├── data
│ ├── log // 运行日志
│ ├── resources // 静态资源文件
│ └── test
│ ├── buffer
│ │ └── buffer_read.txt // Buffer 读文件测试文件
│ ├── http
│ │ ├── conn_request_fail.txt // HttpConn 访问不存在文件的报文
│ │ ├── conn_request_success.txt // HttpConn 访问存在文件的报文
│ │ ├── request_get.txt // HttpRequest 解析 Get 报文
│ │ ├── request_post.txt // HttpRequest 解析的 Post 报文
│ │ └── response_resource.txt // 测试用的资源文件
│ └── log
├── test // 对应模块的单元测试
│ ├── buffer
│ ├── http
│ ├── log
│ ├── mux
│ ├── pool
│ ├── server
│ └── timer
└── TimeLineFrontend // 前端资源项目

前后端交互逻辑

  • 事件描述:

    1
    2
    3
    4
    5
    6
    7
    {
    "task_id": "xxx",
    "user_id": "xxx",
    "time": "时间戳",
    "task": "xxx",
    "priority": "0-10(越大越重要)"
    }
  • 服务器交互

    • 登陆之后,服务器回复浏览器一个 action_token 作为浏览器的身份凭证
    • 浏览器断开链接/主动退出登录后 action_token 销毁
    • (action_token 其实就是 cookie)

    • 浏览器发送内容

      • 登录

        • Post 信息

          1
          2
          3
          4
          5
          6
          7
          {
          "action": "login",
          "action_info": {
          "user": "xxx",
          "passwd": "xxx"
          }
          }
        • 服务器返回内容

          1
          2
          3
          4
          5
          6
          7
          8
          9
          {
          "action_result": "true/false",
          "result_info": {
          "action_token": "xxx",

          // 发生错误时
          "error_message": "xxx"
          }
          }
      • 退出登录

        • Post 信息

          1
          2
          3
          4
          5
          6
          {
          "action": "logout",
          "action_info": {
          "action_token": "xxx"
          }
          }
        • 服务器返回内容

          1
          2
          3
          4
          5
          6
          7
          {
          "action_result": "true/false",
          "result_info": {
          // 发生错误时
          "error_message": "xxx"
          }
          }
      • 查询数据

        • Post 信息

          1
          2
          3
          4
          5
          6
          7
          {
          "action": "query",
          "action_info": {
          // query:
          "action_token": "xxx"
          }
          }
        • 服务器返回内容

          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          {
          "action_result": "true/false",
          "result_info": {
          "tasks": [
          {
          "task_id": "xxx",
          "time": "时间戳",
          "task": "xxx",
          "priority": "0-10(越大越重要)"
          }
          ],
          // 发生错误时
          "error_message": "xxx"
          }
          }
      • 添加数据

        • Post 信息

          1
          2
          3
          4
          5
          6
          7
          8
          9
          {
          "action": "add",
          "action_info": {
          "action_token": "xxx",
          "time": "时间戳",
          "task": "xxx",
          "priority": "0-10(越大越重要)"
          }
          }
        • 服务器返回内容

          1
          2
          3
          4
          5
          6
          7
          8
          9
          {
          "action_result": "true/false",
          "result_info": {
          "task_id": "xxx",

          // 发生错误时
          "error_message": "xxx"
          }
          }
      • 更新数据

        • Post 信息

          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          {
          "action": "update",
          "action_info": {
          "action_token": "xxx",
          "task_id": "xxx",
          "time": "时间戳",
          "task": "xxx",
          "priority": "0-10(越大越重要)"
          }
          }
        • 服务器返回内容

          1
          2
          3
          4
          5
          6
          7
          {
          "action_result": "true/false",
          "result_info": {
          // 发生错误时
          "error_message": "xxx"
          }
          }
      • 删除数据

        • Post 信息

          1
          2
          3
          4
          5
          6
          7
          {
          "action": "delete",
          "action_info": {
          "action_token": "xxx",
          "task_id": "xxx"
          }
          }
        • 服务器返回内容

          1
          2
          3
          4
          5
          6
          7
          {
          "action_result": "true/false",
          "result_info": {
          // 发生错误时
          "error_message": "xxx"
          }
          }

          测试方法

  • login 动态路由

    1
    2
    3
    4
    5
    6
    7
    8
    9
    fetch(
    new Request("action/login", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: '{"action":"login", "action_info": {"user":"user", "passwd": "passwd"}}',
    })
    ).then((resp) => {
    console.log(resp);
    });
  • logout 动态路由

    1
    2
    3
    4
    5
    6
    7
    8
    9
    fetch(
    new Request("action/logout", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: '{"action":"logout", "action_info": {"action_token":"PKdhtXMmr29n3L0K99eM"}}',
    })
    ).then((resp) => {
    console.log(resp);
    });
  • query 动态路由

    1
    2
    3
    4
    5
    6
    7
    8
    9
    fetch(
    new Request("action/query", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: '{"action":"query", "action_info": {"action_token":"PKdhtXMmr29n3L0K99eM"}}',
    })
    ).then((resp) => {
    console.log(resp);
    });
  • add 动态路由

    1
    2
    3
    4
    5
    6
    7
    8
    9
    fetch(
    new Request("action/add", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: '{"action":"add", "action_info": {"action_token":"PKdhtXMmr29n3L0K99eM","time":"2024-01-01","task":"test task","priority": 3}}',
    })
    ).then((resp) => {
    console.log(resp);
    });
  • update 动态路由

    1
    2
    3
    4
    5
    6
    7
    8
    9
    fetch(
    new Request("action/update", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: '{"action":"update", "action_info": {"action_token":"PKdhtXMmr29n3L0K99eM","task_id": 4, "time":"2099-01-01","task":"test task","priority": 3}}',
    })
    ).then((resp) => {
    console.log(resp);
    });
  • delete 动态路由

    1
    2
    3
    4
    5
    6
    7
    8
    9
    fetch(
    new Request("action/delete", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: '{"action":"delete", "action_info": {"action_token":"PKdhtXMmr29n3L0K99eM","task_id": 9}}',
    })
    ).then((resp) => {
    console.log(resp);
    });