TinywebServer代码详解– 配置文件(13)

原项目地址(点击跳转)

博主添加注释后项目地址(点击跳转)


一、基础知识

1.解析JSON文件

在本文中,使用JSON文件存储,数据库服务器相关信息,有数据库服务器端口、数据库服务器用户名、密码、以及使用的数据库名

在程序运行时,调用函数对JSON文件进行解析,获取数据。在本文中使用JSONCPP库对JSON文件进行解析

按照JSONCPP库之后,出了源文件中包含JSONCPP的头文件外#include <jsoncpp/json/json.h>,在编译时,CMakeLists.txt中也需要添加相关信息,使得程序可以正常编译,如下是一个使用JSONCPP库的CMakeLists.txt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
cmake_minimum_required(VERSION 3.10)
project(MyProject)

# 查找 JSONCPP
find_package(jsoncpp REQUIRED)
# 使用 find_package 成功找到 JSONCPP 后,它会设置一些变量,包括包含目录
# 可以使用这些变量来确保你的项目能找到 JSONCPP 的头文件
include_directories(${JSONCPP_INCLUDE_DIRS})

# 添加可执行文件
add_executable(app main.cpp)

# 链接 JSONCPP
target_link_libraries(app jsoncpp_lib)

使用JSON库解析JSON文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
bool Config::parseJsonFile()
{
// 文件刘读取JSON文件
std::ifstream ifs("/home/zxz/Proj/C_C++/WebServer/TinyWebServerBymyself/dbconf.json");
// Json::Reader 对象用于解析JSON数据
Json::Reader rd;
// Json::Value 对象用于存储解析后的JSON数据结构
Json::Value root;
// parse方法将输入文件流 ifs 中的内容解析到 root 对象中
rd.parse(ifs,root);
if(root.isObject())
{
user = root["userName"].asString();
password = root["password"].asString();
databasename = root["dbName"].asString();
db_Port = root["db_port"].asInt();
return true;
}
return false;
}


2.getopt函数

getopt() 函数是一个用于解析命令行选项的 C 语言标准库函数,它帮助程序处理命令行参数,使得用户可以输入格式化的选项和参数

1
2
3
#include <unistd.h>

int getopt(int argc, char * const argv[], const char *optstring);

参数:

  • argc: 命令行参数的数量
  • argv: 命令行参数的数组
  • optstring:一个字符串,包含程序可识别的选项字符(程序只接受短选项名,如-h),如果选项后跟冒号:,则表示该选项后必须跟一个参数值

返回:

  • 返回识别到的选项字符
  • 如果所有命令行选项都已解析完毕,则返回 -1
  • 如果遇到一个选项字符不在optstring中,则返回字符?
  • 如果一个需要参数的选项未给出参数,则返回字符:(如果optstring以冒号开始)

全局变量

  • optarg:指向当前选项参数(如果有)的指针。
  • optind:下一个将被处理的元素的索引。
  • opterr:如果不为零,getopt()会在遇到错误时打印错误信息
  • optopt:它在某些特定情况下存储关于命令行选项的额外信息。当 getopt() 调用返回 ‘?’ 来表示一个错误——通常是因为遇到了一个未知选项或者一个需要参数的选项没有提供参数,optopt 变量会被设置为引起错误的选项字符

实例程序

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
#include <stdio.h>
#include <unistd.h>

int main(int argc, char *argv[]) {
int opt;

// optstring中的冒号表示-c选项后必须有一个关联的参数
while ((opt = getopt(argc, argv, "abc:")) != -1) {
switch (opt) {
case 'a':
printf("Option a is specified\n");
break;
case 'b':
printf("Option b is specified\n");
break;
case 'c':
printf("Option c is specified with value %s\n", optarg);
break;
case '?': // 未知选项
printf("Unknown option: %c\n", optopt);
break;
default:
printf("Other error\n");
}
}

// 处理额外的参数
for (; optind < argc; optind++) {
printf("Additional argument: %s\n", argv[optind]);
}

return 0;
}



二、代码解析

1.JSON文件

dbconf.json

1
2
3
4
5
6
{
"db_port": 3306,
"userName": "zxz",
"password": "123456",
"dbName": "serverdb"
}


2.config

config.h

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
#pragma once
#include "webserver.h"


class Config
{
public:
Config();
~Config(){};

// 解析终端命令传入的参数
void parse_arg(int argc, char*argv[]);

// 解析JSON数据库配置文件
bool parseJsonFile();

// 端口号
int PORT;

// 日志写入方式(异步/同步)
int LOGWrite;

// 触发组合模式
int TRIGMode;

// listenfd事件触发模式(监听套接字)
int LISTENTrigmode;

// connfd事件触发模式(通信套接字)
int CONNTrigmode;

// 是否优雅关闭链接(套接字关闭时,是否等待接收/发送的数据完成)
int OPT_LINGER;

// 数据库连接池中数据库连接的数量
int sql_num;

// 线程池内的线程数量
int thread_num;

// 是否关闭日志
int close_log;

// 并发模型选择(事件处理模式)
int actor_model;

// 数据库登陆用户名
std::string user;
// 数据库登陆密码
std::string password;
// 数据库名字
std::string databasename;
// 数据库服务器端口
int db_Port;
};

config.cpp

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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#include "config.h"
#include <fstream>
#include <jsoncpp/json/json.h> // JsonCpp库

Config::Config()
{
// 端口号,默认9006
PORT = 9006;

// 日志写入方式,默认同步
LOGWrite = 0;

// 触发组合模式,默认listenfd LT + connfd LT
TRIGMode = 0;

// listenfd(监听套接字事件)触发模式,默认LT
LISTENTrigmode = 0;

// connfd(通信套接字事件)触发模式,默认LT
CONNTrigmode = 0;

// 优雅关闭http链接,默认不使用
OPT_LINGER = 0;

// 数据库连接池中数据库连接的数量,默认8
sql_num = 8;

// 线程池内的线程数量,默认8
thread_num = 8;

// 关闭日志,默认不关闭
close_log = 0;

// 并发模型(事件处理模式),默认是proactor
actor_model = 0;

// 数据库的服务器端口,默认为3306
db_Port = 3306;
}


/*
* @func: 解析JSON配置文件
*/
bool Config::parseJsonFile()
{
// 文件刘读取JSON文件
std::ifstream ifs("/home/zxz/Proj/C_C++/WebServer/TinyWebServerBymyself/dbconf.json");
// Json::Reader 对象用于解析JSON数据
Json::Reader rd;
// Json::Value 对象用于存储解析后的JSON数据结构
Json::Value root;
// parse方法将输入文件流 ifs 中的内容解析到 root 对象中
rd.parse(ifs,root);
if(root.isObject())
{
user = root["userName"].asString();
password = root["password"].asString();
databasename = root["dbName"].asString();
db_Port = root["db_port"].asInt();
return true;
}
return false;
}

/*
* @func: 解析命令行终端的选项输入
*/
void Config::parse_arg(int argc, char*argv[]){
int opt;
const char *str = "p:l:m:o:s:t:c:a:";
// getopt函数用于解析命令行选项(短选项)
while ((opt = getopt(argc, argv, str)) != -1)
{
switch (opt)
{
case 'p':
{
// 服务器程序监听端口
PORT = atoi(optarg);
break;
}
case 'l':
{
// 日志写入方式(异步/同步)
LOGWrite = atoi(optarg);
break;
}
case 'm':
{
// 触发组合模式
TRIGMode = atoi(optarg);
break;
}
case 'o':
{
// 优雅关闭http链接
OPT_LINGER = atoi(optarg);
break;
}
case 's':
{
// 数据库连接池中数据库连接数量
sql_num = atoi(optarg);
break;
}
case 't':
{
// 线程池中工作线程数量
thread_num = atoi(optarg);
break;
}
case 'c':
{
// 是否关闭日志
close_log = atoi(optarg);
break;
}
case 'a':
{
// 并发模型(事件处理模式)
actor_model = atoi(optarg);
break;
}
default:
break;
}
}
}