C++后端服务器实现笔记

仓库

项目结构

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
├── .github/             # GitHub配置目录
│ └── workflows/ # GitHub Actions工作流
├── .gitignore # Git忽略配置
├── 3rdparty/ # 第三方库目录
│ ├── mysql/ # MySQL客户端库
│ │ ├── include/ # MySQL头文件
│ │ └── lib/ # MySQL库文件
│ └── redis/ # Redis客户端库
│ ├── include/ # Redis头文件
│ └── lib/ # Redis库文件
├── CMakeLists.txt # CMake构建配置
├── LICENSE # 许可证文件
├── README.md # 项目说明文档
├── include/ # 头文件目录
│ ├── DBConnector.h # 数据库连接器头文件
│ ├── Handler.h # 请求处理器头文件
│ ├── JsonValue.h # JSON处理头文件
│ ├── Log.h # 日志系统头文件
│ ├── RDConnector.h # Redis连接器头文件
│ ├── Server.h # 服务器核心头文件
│ ├── Threadpool.h # 线程池头文件
│ ├── jwt.h # JWT认证头文件
│ └── route.h # 路由系统头文件
├── main.cpp # 主程序入口
├── model/ # 数据模型目录
│ ├── Model.cpp # 数据模型实现
│ └── Model.h # 数据模型头文件
└── src/ # 源代码目录
├── DBConnector.cpp # 数据库连接器实现
├── JsonValue.cpp # JSON处理实现
├── Log.cpp # 日志系统实现
├── RDConnector.cpp # Redis连接器实现
├── Server.cpp # 服务器核心实现
├── Threadpool.cpp # 线程池实现
├── jwt.cpp # JWT认证实现
└── route.cpp # 路由系统实现

原理解析

1. JWT认证原理

JWT(JSON Web Token)是一种用于在网络应用间安全传输信息的开放标准(RFC 7519)。它通过紧凑且自包含的方式在各方之间以JSON对象形式安全地传输信息。这些信息可以被验证和信任,因为它们是数字签名的。

JWT的核心组成部分包括:

  1. Header(头部):包含令牌类型和使用的签名算法
  2. Payload(负载):包含声明(claims),如用户信息和过期时间
  3. Signature(签名):用于验证消息是否被篡改

在我们的实现中,JWT主要用于身份认证和会话管理。服务器生成JWT后,客户端在后续请求中携带此令牌,服务器通过验证令牌的有效性来确认用户身份。
}

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

#### 1,Base64 编码

Base64 是一种基于64个可打印字符来表示二进制数据的表示方法。它可以将二进制数据转换为文本字符串,以便在文本环境中传输和存储。

Base64 编码的步骤如下:

1. 将二进制数据按照每3个字节一组进行分组。
2. 将每个字节转换为4个6位的二进制数。
3. 将这4个6位的二进制数编码拼接成一个由4个字符组成的字符串。
4. 如果二进制数据的长度不是3的倍数,则在最后补0,直到长度为3的倍数。然后按照上述步骤进行编码。

项目内代码逻辑

```cpp
/**
* @brief JWT类的base64UrlEncode方法
* @param data 需要进行base64Url编码的原始数据
* @return 返回编码后的字符串
*
* 该方法实现了base64Url编码,它是一种base64编码的变体,主要用于URL安全。
* 与标准base64编码相比,它将'+'替换为'-','/'替换为'_',并移除了末尾的'='填充字符。
*/
std::string JWT::base64UrlEncode(const std::string& data) {
// 定义base64编码表
static const char* base64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
std::string encoded;
// 预分配足够的内存空间,提高性能
encoded.reserve(((data.size() + 2) / 3) * 4);

std::size_t i = 0;
// 处理完整的三字节块
while (i + 3 <= data.size()) {
auto b1 = static_cast<unsigned char>(data[i]);
auto b2 = static_cast<unsigned char>(data[i + 1]);
auto b3 = static_cast<unsigned char>(data[i + 2]);

// 将三个字节转换为四个base64字符
encoded.push_back(base64Chars[b1 >> 2]);
encoded.push_back(base64Chars[((b1 & 0x03) << 4) | (b2 >> 4)]);
encoded.push_back(base64Chars[((b2 & 0x0F) << 2) | (b3 >> 6)]);
encoded.push_back(base64Chars[b3 & 0x3F]);
i += 3;
}

// 处理剩余的不完整字节块
if (i < data.size()) {
auto b1 = static_cast<unsigned char>(data[i]);
encoded.push_back(base64Chars[b1 >> 2]);
if (i + 1 < data.size()) {
auto b2 = static_cast<unsigned char>(data[i + 1]);
encoded.push_back(base64Chars[((b1 & 0x03) << 4) | (b2 >> 4)]);
encoded.push_back(base64Chars[(b2 & 0x0F) << 2]);
encoded.push_back('=');
} else {
encoded.push_back(base64Chars[(b1 & 0x03) << 4]);
encoded.push_back('=');
encoded.push_back('=');
}
}

// 将base64编码转换为URL安全的格式
for (char& ch : encoded) {
if (ch == '+') {
ch = '-';
} else if (ch == '/') {
ch = '_';
}
}

// 移除末尾的填充字符'='
while (!encoded.empty() && encoded.back() == '=') {
encoded.pop_back();
}

return encoded;
}

同时,需要有 Base64 解码函数

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
/**
* @brief Base64URL解码函数
* @param data 需要解码的Base64URL字符串
* @return 解码后的原始字符串
*
* 该函数将Base64URL编码的字符串转换为原始字符串。Base64URL是Base64的变种,
* 使用'-'和'_'替代了标准的'+'和'/',同时不使用填充字符'='。
*/
std::string JWT::base64UrlDecode(const std::string& data) {
std::string base64 = data;
// 将Base64URL的特殊字符转换为标准Base64字符
for (char& ch : base64) {
if (ch == '-') {
ch = '+';
} else if (ch == '_') {
ch = '/';
}
}
// 添加填充字符'=',使字符串长度为4的倍数
while (base64.size() % 4 != 0) {
base64.push_back('=');
}

// Lambda函数:将Base64字符转换为对应的6位整数值
auto decodeChar = [](char ch) -> int {
if ('A' <= ch && ch <= 'Z') return ch - 'A';
if ('a' <= ch && ch <= 'z') return ch - 'a' + 26;
if ('0' <= ch && ch <= '9') return ch - '0' + 52;
if (ch == '+') return 62;
if (ch == '/') return 63;
return -1;
};

std::string decoded;
// 预分配内存,提高性能
decoded.reserve((base64.size() / 4) * 3);

// 每次处理4个Base64字符
for (std::size_t i = 0; i < base64.size(); i += 4) {
int val1 = decodeChar(base64[i]);
int val2 = decodeChar(base64[i + 1]);
int val3 = base64[i + 2] == '=' ? -1 : decodeChar(base64[i + 2]);
int val4 = base64[i + 3] == '=' ? -1 : decodeChar(base64[i + 3]);

// 将4个6位值转换为3个8位字节
decoded.push_back(static_cast<char>((val1 << 2) | (val2 >> 4)));
if (val3 >= 0) {
decoded.push_back(static_cast<char>(((val2 & 0x0F) << 4) | (val3 >> 2)));
if (val4 >= 0) {
decoded.push_back(static_cast<char>(((val3 & 0x03) << 6) | val4));
}
}
}

return decoded;
}

2,sha256 签名

SHA-256 是一种安全哈希算法,它可以生成一个256位的哈希值。哈希值是一个固定长度的字符串,用于唯一标识输入数据。SHA-256 算法具有以下特点:

  1. 输入数据的微小变化会导致哈希值的巨大变化,这使得哈希值具有抗冲突性。也就是说,两个不同的输入数据几乎不可能产生相同的哈希值。
  2. SHA-256 算法是不可逆的,即无法从哈希值反推出原始数据。
  3. SHA-256 算法是公开的,任何人都可以使用它来生成哈希值。
  4. 我们可以将哈希值转成一个固定的长度(例如32个字符)的十六进制字符串,以便于存储和传输。

实现原理:

  1. 将需要签名的数据转换为字节数组。
  2. 使用 SHA-256 算法对字节数组进行哈希运算,生成一个256位的哈希值。
  3. 将哈希值转换为十六进制字符串。

具体算法超级复杂,直接看源码,一大堆异或(其实只要不可逆,怎么算都行),翻转等等

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
/**
* @brief 计算输入字符串的SHA256哈希值
* @param data 输入字符串
* @return 返回计算得到的SHA256哈希值字符串
*/
std::string JWT::sha256(const std::string& data) {
// 初始化SHA256哈希值的初始常量
std::array<std::uint32_t, 8> hash{
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19
};

// 将输入字符串转换为字节数组
std::vector<std::uint8_t> message(data.begin(), data.end());
// 记录原始数据的比特长度
const std::uint64_t originalBitLength = static_cast<std::uint64_t>(message.size()) * 8;

// 添加填充位1
message.push_back(0x80);
// 添加填充位0,直到消息长度满足模64等于56
while ((message.size() % 64) != 56) {
message.push_back(0x00);
}

// 添加原始消息长度信息(64位大端序)
for (int i = 7; i >= 0; --i) {
message.push_back(static_cast<std::uint8_t>((originalBitLength >> (i * 8)) & 0xFF));
}

// 处理消息的每个512位(64字节)的块
for (std::size_t offset = 0; offset < message.size(); offset += 64) {
std::uint32_t w[64];

// 将当前64字节的块分解为16个32位字
for (int i = 0; i < 16; ++i) {
w[i] = (static_cast<std::uint32_t>(message[offset + 4 * i]) << 24) |
(static_cast<std::uint32_t>(message[offset + 4 * i + 1]) << 16) |
(static_cast<std::uint32_t>(message[offset + 4 * i + 2]) << 8) |
(static_cast<std::uint32_t>(message[offset + 4 * i + 3]));
}

// 扩展16个字为64个字
for (int i = 16; i < 64; ++i) {
std::uint32_t s0 = rotor(w[i - 15], 7) ^ rotor(w[i - 15], 18) ^ (w[i - 15] >> 3);
std::uint32_t s1 = rotor(w[i - 2], 17) ^ rotor(w[i - 2], 19) ^ (w[i - 2] >> 10);
w[i] = w[i - 16] + s0 + w[i - 7] + s1;
}

// 初始化哈希值的工作变量
std::uint32_t a = hash[0];
std::uint32_t b = hash[1];
std::uint32_t c = hash[2];
std::uint32_t d = hash[3];
std::uint32_t e = hash[4];
std::uint32_t f = hash[5];
std::uint32_t g = hash[6];
std::uint32_t h = hash[7];

// 主压缩函数循环
for (int i = 0; i < 64; ++i) {
std::uint32_t s1 = rotor(e, 6) ^ rotor(e, 11) ^ rotor(e, 25);
std::uint32_t ch = (e & f) ^ ((~e) & g);
std::uint32_t temp1 = h + s1 + ch + kSha256Constants[i] + w[i];
std::uint32_t s0 = rotor(a, 2) ^ rotor(a, 13) ^ rotor(a, 22);
std::uint32_t maj = (a & b) ^ (a & c) ^ (b & c);
std::uint32_t temp2 = s0 + maj;

// 更新工作变量
h = g;
g = f;
f = e;
e = d + temp1;
d = c;
c = b;
b = a;
a = temp1 + temp2;
}

// 更新哈希值
hash[0] += a;
hash[1] += b;
hash[2] += c;
hash[3] += d;
hash[4] += e;
hash[5] += f;
hash[6] += g;
hash[7] += h;
}

// 将最终的哈希值转换为32字节的字符串
std::string result;
result.reserve(32);
for (std::uint32_t part : hash) {
for (int shift = 24; shift >= 0; shift -= 8) {
result.push_back(static_cast<char>((part >> shift) & 0xFF));
}
}
return result;
}

利用哈希函数实现签名:

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
/**
* HMAC-SHA256 签名算法实现
* HMAC (Hash-based Message Authentication Code) 是一种基于哈希的消息认证码算法
* 此函数使用SHA-256作为哈希函数实现HMAC签名
*
* @param data 要签名的原始数据
* @param key 用于签名的密钥
* @return 返回HMAC-SHA256签名结果
*/
std::string JWT::hmacSha256(const std::string& data, const std::string& key) {
constexpr std::size_t blockSize = 64;
std::string actualKey = key;

// 处理密钥
if (actualKey.size() > blockSize) {
actualKey = sha256(actualKey);
}
if (actualKey.size() < blockSize) {
actualKey.append(blockSize - actualKey.size(), '\0');
}

// 创建填充密钥
std::string oKeyPad(blockSize, '\0');
std::string iKeyPad(blockSize, '\0');
for (std::size_t i = 0; i < blockSize; ++i) {
oKeyPad[i] = static_cast<char>(actualKey[i] ^ 0x5c);
iKeyPad[i] = static_cast<char>(actualKey[i] ^ 0x36);
}

// 计算内部哈希:iKeyPad + data
std::string innerData = iKeyPad + data;
std::string innerHash = sha256(innerData);

// 计算最终哈希:oKeyPad + innerHash
std::string outerData = oKeyPad + innerHash;
return sha256(outerData);
}

需要给JWT设置标准头表明其类型和哈希算法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* 构建JWT头部信息
* @return 返回包含算法和类型的标准JWT头部字符串
*
* 该函数返回一个固定的JWT头部字符串,使用HS256算法作为签名算法,
* 并指定令牌类型为JWT(JSON Web Token)
*/
std::string JWT::buildHeader() {
// 使用静态常量存储标准JWT头部字符串
// 内容为JSON格式,包含算法(alg)和类型(typ)字段
static const std::string header = R"({"alg":"HS256","typ":"JWT"})";
// 返回预定义的JWT头部
return header;
}

3,payload部分

payload部分是JWT的主体,用于存放用户信息,一般包括以下字段:

  • iss (issuer):签发人
  • sub (subject):主题
  • aud (audience):受众
  • exp (expiration time):过期时间
  • nbf (not before):生效时间
  • iat (issued at):签发时间
  • jti (JWT ID):编号

通俗来讲,就是把字典(json)改成字符串。

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
/**
* @brief 构建JWT的payload部分(使用JsonValue优化)
* 先构造JsonValue对象,再序列化为JSON字符串,避免手动拼接错误
*/
std::string JWT::buildPayload(const std::map<std::string, std::string>& customClaims,
long long issuedAt,
std::optional<long long> expiresAt) {
// 第一步:构造JsonValue对象(JSON对象类型)
std::map<std::string, JsonValue> payloadMap;

// 添加标准声明:iat(签发时间)
payloadMap.emplace("iat", JsonValue(static_cast<double>(issuedAt)));

// 添加可选标准声明:exp(过期时间)
if (expiresAt.has_value()) {
payloadMap.emplace("exp", JsonValue(static_cast<double>(expiresAt.value())));
}

// 添加自定义声明(自动处理字符串转义)
for (const auto& [key, value] : customClaims) {
// 直接使用JsonValue的字符串构造,转义逻辑由JsonValue::escapeJsonString自动处理
payloadMap.emplace(key, JsonValue(value));
}

// 第二步:使用JsonValue序列化为标准JSON字符串
JsonValue payloadValue(payloadMap);
return payloadValue.toJson();
}

注:JsonValue是自定义的json类,用于序列化和反序列化json字符串,后面会讲到。

4,总体流程

这样,生成token的整体流程如下

获取头部
获取过期时间
获取payload
获取签名
拼接token

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
/**
* 生成JWT令牌
* @param customClaims 自定义声明,一个键值对映射
* @param ttlSeconds 令牌有效期(秒),-1表示使用默认有效期
* @return 生成的JWT令牌字符串
* @throws std::runtime_error 当密钥未设置时抛出异常
*/
std::string JWT::generateToken(const std::map<std::string, std::string>& customClaims, long long ttlSeconds) const {
// 检查密钥是否已设置
if (secret_.empty()) {
// std::cout<<"empty"<<std::endl;
throw std::runtime_error("JWT secret is not set");
}

// 构建并编码JWT头部
const auto header = buildHeader();
const auto headerEncoded = base64UrlEncode(header);

// 获取当前时间戳(秒)
auto now = std::chrono::system_clock::now();
auto issuedAt = std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch()).count();

// 处理令牌有效期
long long effectiveTTL = ttlSeconds;
if (effectiveTTL < 0) {
effectiveTTL = defaultTTL_; // 使用默认有效期
}
std::optional<long long> expiresAt;
if (effectiveTTL > 0) {
expiresAt = issuedAt + effectiveTTL; // 计算过期时间戳
}

// 构建并编码JWT载荷
const auto payload = buildPayload(customClaims, issuedAt, expiresAt);
const auto payloadEncoded = base64UrlEncode(payload);

// 创建签名输入并生成签名
const std::string signingInput = headerEncoded + "." + payloadEncoded;
const auto signature = base64UrlEncode(hmacSha256(signingInput, secret_));

// 组合头部、载荷和签名,返回完整的JWT令牌
return signingInput + "." + signature;
}

检查token是否过期

先按照"."分割,获取payload部分,然后解析payload部分,获取过期时间,然后和当前时间比较,如果当前时间大于过期时间,则过期。

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
/**
* @brief 将JWT令牌字符串按照点号(.)分割成多个部分
* @param token 完整的JWT令牌字符串
* @return std::vector<std::string> 分割后的令牌部分数组
*/
std::vector<std::string> JWT::splitToken(const std::string& token) {
std::vector<std::string> parts; // 用于存储分割后的令牌部分
std::size_t start = 0; // 当前查找的起始位置
// 遍历令牌字符串,查找点号(.)作为分割点
while (start <= token.size()) {
auto pos = token.find('.', start); // 查找下一个点号的位置
// 如果找不到点号,将剩余部分添加到结果中并结束循环
if (pos == std::string::npos) {
parts.emplace_back(token.substr(start));
break;
}
// 添加当前部分到结果中
parts.emplace_back(token.substr(start, pos - start));
start = pos + 1; // 更新查找起始位置为点号之后的位置
}
return parts; // 返回分割后的令牌部分数组
}

/**
* 验证JWT令牌的有效性
* @param token 待验证的JWT令牌字符串
* @param payloadJson 用于存储载荷JSON字符串的输出参数,可为nullptr
* @return 验证成功返回true,失败返回false
*/
bool JWT::verifyToken(const std::string& token, std::string* payloadJson) const {
// 检查密钥是否为空
if (secret_.empty()) {
return false;
}

// 分割令牌为三部分(头部、载荷、签名)
// Split token into three parts (header, payload, signature)
const auto parts = splitToken(token);
// 检查令牌是否由三部分组成
if (parts.size() != 3) {
return false;
}

// 构造待签名的字符串(头部+载荷)
const std::string signingInput = parts[0] + "." + parts[1];
// 计算期望的签名
const std::string expectedSignature = base64UrlEncode(hmacSha256(signingInput, secret_));
// 验证签名是否匹配
if (expectedSignature != parts[2]) {
return false;
}

// 解码载荷部分
const std::string payload = base64UrlDecode(parts[1]);

// 检查过期时间(exp)声明
const auto expClaim = extractNumericClaim(payload, "exp");
if (expClaim.has_value()) {
// 获取当前时间戳
auto now = std::chrono::system_clock::now();
auto current = std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch()).count();
// 检查是否过期
if (current > expClaim.value()) {
return false;
}
}

// 如果提供了payloadJson参数,则将载荷JSON字符串存入
if (payloadJson != nullptr) {
*payloadJson = payload;
}
return true;
}

解析 Claim

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
/**
* @brief 解析JSON对象字符串为键值对映射(使用JsonValue优化)
* 利用JsonValue的完整解析能力,自动处理转义字符、嵌套检查、语法验证
*/
std::map<std::string, std::string> JWT::parseJsonObject(const std::string& json) {
std::map<std::string, std::string> result;
JsonValue jsonValue;

try {
jsonValue.fromJson(json);
} catch (const std::exception& e) {
throw std::runtime_error("JSON parse failed: " + std::string(e.what()));
}

// 第二步:检查解析结果是否为JSON对象类型
if (jsonValue.getType() != JsonValue::OBJECT) {
throw std::runtime_error("Invalid JSON: not an object");
}

// 第三步:提取对象中的所有键值对,转换为string-string映射
auto jsonObject = jsonValue.asObject();
for (const auto& [key, value] : jsonObject) {
// 提取值并确保为string类型(非string类型会自动转为对应字符串形式)
switch (value.getType()) {
case JsonValue::STRING:
result.emplace(key, value.asString());
break;
case JsonValue::NUMBER:
// 数字类型转为string(保持原始数值格式)
result.emplace(key, value.toJson());
break;
case JsonValue::BOOLEAN:
// 布尔类型转为"true"/"false"字符串
result.emplace(key, value.toJson());
break;
case JsonValue::NULL_TYPE:
// null类型转为"null"字符串
result.emplace(key, "null");
break;
default:
// 不支持对象/数组作为值(如需支持可扩展)
throw std::runtime_error("Unsupported value type for key: " + key);
}
}

return result;
}


/**
* @brief 解析JWT令牌中的声明(claims)部分
* @param token 要解析的JWT令牌字符串
* @return 返回一个包含字符串映射的std::optional对象,如果解析失败则返回std::nullopt
*/
std::optional<std::map<std::string, std::string>> JWT::parseClaims(const std::string& token) const {
std::string payload; // 用于存储令牌的payload部分
if (!verifyToken(token, &payload)) { // 验证令牌有效性,如果验证失败
return std::nullopt; // 返回空值表示解析失败
}
try {
return parseJsonObject(payload); // 尝试解析payload为JSON对象
} catch (const std::exception&) { // 捕获解析过程中可能出现的异常
return std::nullopt; // 如果解析失败,返回空值
}
}

同时我在其中顺便实现密码哈希

密码哈希流程

  • 生成盐值(salt)
  • 将盐值与密码进行哈希运算
  • 转成16进制字符串
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
/**
* @brief 生成加密安全的盐值
* 生成 16 字节(128 位)加密安全随机数,编码为 32 字符十六进制字符串
* 满足密码学安全要求,跨平台兼容,生成的盐值可直接存储/传输
*/
std::string JWT::generate_salt() {
const size_t salt_length = 16; // 16 字节 = 128 位,足够密码学安全
std::string salt_bytes(salt_length, 0x00);

try {
// 1. 初始化加密安全随机数生成器
std::random_device rd;
// 双重种子:random_device + 当前时间戳,增强随机性(应对 rd 伪随机的情况)
uint64_t seed = rd() ^ static_cast<uint64_t>(std::chrono::high_resolution_clock::now().time_since_epoch().count());
std::mt19937_64 gen(seed); // 64 位 Mersenne Twister 生成器,适合加密场景
std::uniform_int_distribution<uint8_t> dist(0x00, 0xFF); // 生成 0-255 的均匀分布随机字节

// 2. 生成 16 字节随机数据
for (auto& byte : salt_bytes) {
byte = dist(gen);
}

// 3. 转为十六进制字符串(可打印、易存储)
return bytes_to_hex(salt_bytes);
} catch (const std::exception& e) {
throw std::runtime_error("Failed to generate salt: " + std::string(e.what()));
}
}

// 补充:十六进制字符串转字节数组(解析盐值和哈希时需要)
std::string JWT::hex_to_bytes(const std::string& hex) {
std::string bytes;
if (hex.size() % 2 != 0) {
return bytes; // 无效的十六进制字符串(长度为奇数)
}
bytes.reserve(hex.size() / 2);
for (size_t i = 0; i < hex.size(); i += 2) {
// 解析每两个字符为一个字节(16进制)
std::string byte_str = hex.substr(i, 2);
auto byte = static_cast<char>(std::stoul(byte_str, nullptr, 16));
bytes.push_back(byte);
}
return bytes;
}

// 常量时间比较(防时序攻击):无论内容是否相同,比较时间一致
bool JWT::constant_time_compare(const std::string & a, const std::string & b) {
if (a.size() != b.size()) {
std::cout<<"长度不同"<<std::endl;
return false; // 长度不同直接返回false
}
int diff = 0;
for (size_t i = 0; i < a.size(); ++i) {
// 异或:若字节不同则结果非0,累积到diff中
diff |= (a[i] ^ b[i]);
}
return diff == 0; // 只有所有字节都相同,diff才为0
}

/**
* 安全的密码加密:使用PBKDF2-HMAC-SHA256
* @param password 明文密码
* @return 存储格式:salt(hex):iterations:hash(hex)
*/
std::string JWT::encrypt_password(const std::string& password) {
const size_t iterations = 100000; // 迭代次数(可根据性能调整)
std::string salt = generate_salt();

// PBKDF2核心:多次迭代HMAC-SHA256
std::string dk(32, 0x00); // 输出32字节哈希
std::string U, T(32, 0);
for (size_t i = 1; i <= 1; ++i) { // 只需要1个块(32字节)
// 盐值 + 4字节大端序号(i=1)
std::string salt_i = salt;
salt_i.push_back(static_cast<char>((i >> 24) & 0xFF));
salt_i.push_back(static_cast<char>((i >> 16) & 0xFF));
salt_i.push_back(static_cast<char>((i >> 8) & 0xFF));
salt_i.push_back(static_cast<char>(i & 0xFF));

U = hmacSha256(password, salt_i);
T = U;
for (size_t j = 1; j < iterations; ++j) {
U = hmacSha256(password, U);
for (size_t k = 0; k < 32; ++k) {
T[k] ^= U[k]; // 异或累积
}
}
dk = T;
}

return bytes_to_hex(salt) + ":" + std::to_string(iterations) + ":" + bytes_to_hex(dk);
}

验证密码

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
/**
* 密码验证
* @param password 明文密码
* @param stored_hash 加密后存储的字符串(格式:salt_hex:iterations:hash_hex)
* @return 是否验证通过
*/
bool JWT::verify_password(const std::string& password, const std::string& stored_hash) {
// 1. 解析存储的哈希字符串(格式:salt_hex:iterations:hash_hex)
size_t colon1 = stored_hash.find(':');
size_t colon2 = stored_hash.find(':', colon1 + 1);
if (colon1 == std::string::npos || colon2 == std::string::npos) {
std::cout<<"格式错误"<<std::endl;
return false; // 格式错误
}

// 提取盐值(十六进制)、迭代次数、存储的哈希(十六进制)
std::string salt_hex = stored_hash.substr(0, colon1);
std::string iterations_str = stored_hash.substr(colon1 + 1, colon2 - colon1 - 1);
std::string stored_hash_hex = stored_hash.substr(colon2 + 1);

// 2. 转换盐值和存储的哈希为字节数组
std::string salt = hex_to_bytes(salt_hex);
std::string expected_hash = hex_to_bytes(stored_hash_hex);
if (salt.empty() || expected_hash.empty()) {
std::cout<<"无效的十六进制"<<std::endl;
return false; // 解析失败(无效的十六进制)
}

// 3. 解析迭代次数(确保为正整数)
size_t iterations;
try {
iterations = std::stoul(iterations_str);
if (iterations == 0) {
std::cout<<"迭代次数不能为0"<<std::endl;
return false; // 迭代次数不能为0
}
} catch (...) {
std::cout<<"转换失败(非数字)"<<std::endl;
return false; // 转换失败(非数字)
}

// 4. 用相同的盐值和迭代次数重新计算哈希
std::string computed_hash(32, 0x00); // 输出32字节(SHA-256长度)

// 复用PBKDF2-HMAC-SHA256逻辑(与加密时一致)
std::string U, T(32, 0);
for (size_t i = 1; i <= 1; ++i) { // 1个块(32字节)
std::string salt_i = salt;
// 添加4字节大端序号(与加密时一致,i=1)
salt_i.push_back(static_cast<char>((i >> 24) & 0xFF));
salt_i.push_back(static_cast<char>((i >> 16) & 0xFF));
salt_i.push_back(static_cast<char>((i >> 8) & 0xFF));
salt_i.push_back(static_cast<char>(i & 0xFF));

U = hmacSha256(password, salt_i);
T = U;
for (size_t j = 1; j < iterations; ++j) {
U = hmacSha256(password, U);
for (size_t k = 0; k < 32; ++k) {
T[k] ^= U[k];
}
}
computed_hash = T;
}

// 5. 常量时间比较计算出的哈希与存储的哈希
return constant_time_compare(computed_hash, expected_hash);
}

2,JsonValue 类

在网络得到信息,大部分是json格式,需要解析json,使用JsonValue类

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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
//
// Created by HP on 2025/11/5.
//

#ifndef CBSF_JSONVALUE_H
#define CBSF_JSONVALUE_H

#include <string>
#include <map>
#include <vector>
#include <cctype>
#include <stdexcept>
#include <algorithm>
/**
* @brief JSON值类型类,支持嵌套结构的JSON数据
*
* 该类提供了完整的JSON值表示,支持以下类型:
* - NULL_TYPE: null值
* - STRING: 字符串值
* - NUMBER: 数字值(整数或浮点数)
* - BOOLEAN: 布尔值
* - OBJECT: 对象(键值对映射)
* - ARRAY: 数组
*
* @brief JSON value type class that supports nested structured JSON data
*
* This class provides complete JSON value representation, supporting the following types:
* - NULL_TYPE: null value
* - STRING: string value
* - NUMBER: numeric value (integer or floating point)
* - BOOLEAN: boolean value
* - OBJECT: object (key-value pair mapping)
* - ARRAY: array
*/
class JsonValue {
public:
/**
* @brief JSON值类型枚举
* @brief JSON value type enumeration
*/
enum Type {
NULL_TYPE, ///< null类型 (null type)
STRING, ///< 字符串类型 (string type)
NUMBER, ///< 数字类型 (numeric type)
BOOLEAN, ///< 布尔类型 (boolean type)
OBJECT, ///< 对象类型 (object type)
ARRAY ///< 数组类型 (array type)
};

/**
* @brief 默认构造函数,创建null值
* @brief Default constructor, creates a null value
*/
JsonValue();

/**
* @brief 从字符串构造JsonValue
* @param value 字符串值
* @brief Construct JsonValue from string
* @param value String value
*/
explicit JsonValue(std::string value);

/**
* @brief 从C风格字符串构造JsonValue
* @param value C风格字符串
* @brief Construct JsonValue from C-style string
* @param value C-style string
*/
explicit JsonValue(const char* value);

/**
* @brief 从整数构造JsonValue
* @param value 整数值
* @brief Construct JsonValue from integer
* @param value Integer value
*/
explicit JsonValue(int value);

/**
* @brief 从浮点数构造JsonValue
* @param value 浮点数值
* @brief Construct JsonValue from floating-point number
* @param value Floating-point value
*/
explicit JsonValue(double value);

/**
* @brief 从布尔值构造JsonValue
* @param value 布尔值
* @brief Construct JsonValue from boolean
* @param value Boolean value
*/
explicit JsonValue(bool value);

/**
* @brief 从对象映射构造JsonValue
* @param value 对象映射
* @brief Construct JsonValue from object map
* @param value Object map
*/
explicit JsonValue(const std::map<std::string, JsonValue>& value);

/**
* @brief 从JsonValue数组构造JsonValue
* @param value JsonValue数组
* @brief Construct JsonValue from JsonValue array
* @param value JsonValue array
*/
explicit JsonValue(const std::vector<JsonValue>& value);

/**
* @brief 从map数组构造JsonValue
* @param value map数组
* @brief Construct JsonValue from map array
* @param value Map array
*/
explicit JsonValue(const std::vector<std::map<std::string, std::string>>& value);

/**
* @brief 从字符串数组构造JsonValue
* @param value 字符串数组
* @brief Construct JsonValue from string array
* @param value String array
*/
explicit JsonValue(const std::vector<std::string>& value);

/**
* @brief 获取值的类型
* @return 值的类型
* @brief Get the type of the value
* @return The type of the value
*/
[[nodiscard]] Type getType() const { return type_; }

/**
* @brief 获取字符串值
* @return 字符串值,如果类型不是STRING则返回空字符串
* @brief Get string value
* @return String value, returns empty string if type is not STRING
*/
[[nodiscard]] std::string asString() const;

/**
* @brief 获取数字值
* @return 数字值
*/
[[nodiscard]] double asNumber() const;

/**
* @brief 获取布尔值
* @return 布尔值
*/
[[nodiscard]] bool asBoolean() const;

/**
* @brief 获取对象值
* @return 对象映射
*/
[[nodiscard]] std::map<std::string, JsonValue> asObject() const;

/**
* @brief 获取数组值
* @return JsonValue数组
*/
[[nodiscard]] std::vector<JsonValue> asArray() const;

/**
* @brief 转换为JSON字符串
* @return JSON格式的字符串
*/
[[nodiscard]] std::string toJson() const;

/**
* @brief 从JSON字符串解析并填充对象属性
* @param jsonStr 输入的JSON格式字符串,用于解析并填充对象的属性
*/
void fromJson(const std::string& jsonStr);


/**
* @brief 静态方法:创建对象
* @param obj 对象映射
* @return JsonValue对象
*/
static JsonValue object(const std::map<std::string, JsonValue>& obj);

/**
* @brief 静态方法:创建数组
* @param arr JsonValue数组
* @return JsonValue对象
*/
static JsonValue array(const std::vector<JsonValue>& arr);

// 辅助函数:解析字符串(处理转义字符)
static std::string parseString(const std::string& jsonStr, size_t& pos) {
pos++; // 跳过开头的 "
std::string result;
while (pos < jsonStr.size() && jsonStr[pos] != '"') {
if (jsonStr[pos] == '\\') { // 处理转义字符
pos++;
if (pos >= jsonStr.size()) {
throw std::invalid_argument("Invalid JSON string: unexpected end after escape");
}
switch (jsonStr[pos]) {
case '"': result += '"'; break;
case '\\': result += '\\'; break;
case '/': result += '/'; break;
case 'b': result += '\b'; break;
case 'f': result += '\f'; break;
case 'n': result += '\n'; break;
case 'r': result += '\r'; break;
case 't': result += '\t'; break;
default: throw std::invalid_argument("Invalid escape character in JSON string");
}
} else {
result += jsonStr[pos];
}
pos++;
}
if (pos >= jsonStr.size() || jsonStr[pos] != '"') {
throw std::invalid_argument("Invalid JSON string: missing closing quote");
}
pos++; // 跳过结尾的 "
return result;
}

private:
Type type_; ///< 值的类型
std::string stringValue_; ///< 字符串值
double numberValue_; ///< 数字值
bool boolValue_; ///< 布尔值
std::map<std::string, JsonValue> objectValue_; ///< 对象值
std::vector<JsonValue> arrayValue_; ///< 数组值

/**
* @brief 转义JSON字符串中的特殊字符
* @param str 原始字符串
* @return 转义后的字符串
*/
[[nodiscard]] static std::string escapeJsonString(const std::string& str) ;

static size_t skipWhitespace(const std::string& jsonStr, size_t pos) {
while (pos < jsonStr.size() && std::isspace(static_cast<unsigned char>(jsonStr[pos]))) {
pos++;
}
return pos;
}

// 辅助函数:解析数字
static double parseNumber(const std::string& jsonStr, size_t& pos) {
size_t start = pos;
// 处理负号
if (jsonStr[pos] == '-') {
pos++;
}
// 处理整数部分
while (pos < jsonStr.size() && std::isdigit(static_cast<unsigned char>(jsonStr[pos]))) {
pos++;
}
// 处理小数部分
if (pos < jsonStr.size() && jsonStr[pos] == '.') {
pos++;
while (pos < jsonStr.size() && std::isdigit(static_cast<unsigned char>(jsonStr[pos]))) {
pos++;
}
}
// 处理指数部分
if (pos < jsonStr.size() && (jsonStr[pos] == 'e' || jsonStr[pos] == 'E')) {
pos++;
if (pos < jsonStr.size() && (jsonStr[pos] == '+' || jsonStr[pos] == '-')) {
pos++;
}
while (pos < jsonStr.size() && std::isdigit(static_cast<unsigned char>(jsonStr[pos]))) {
pos++;
}
}
// 转换为double
std::string numStr = jsonStr.substr(start, pos - start);
try {
return std::stod(numStr);
} catch (...) {
throw std::invalid_argument("Invalid JSON number: " + numStr);
}
}

// 辅助函数:解析布尔值
static bool parseBoolean(const std::string& jsonStr, size_t& pos) {
if (jsonStr.substr(pos, 4) == "true") {
pos += 4;
return true;
} else if (jsonStr.substr(pos, 5) == "false") {
pos += 5;
return false;
} else {
throw std::invalid_argument("Invalid JSON boolean value");
}
}

// 辅助函数:解析null
static void parseNull(const std::string& jsonStr, size_t& pos) {
if (jsonStr.substr(pos, 4) != "null") {
throw std::invalid_argument("Invalid JSON null value");
}
pos += 4;
}

// 辅助函数:解析对象(键值对集合)
static std::map<std::string, JsonValue> parseObject(const std::string& jsonStr, size_t& pos) {
pos++; // 跳过开头的 {
std::map<std::string, JsonValue> obj;
pos = skipWhitespace(jsonStr, pos);

while (pos < jsonStr.size() && jsonStr[pos] != '}') {
// 解析键(必须是字符串)
if (jsonStr[pos] != '"') {
throw std::invalid_argument("Invalid JSON object: key must be a string");
}
std::string key = parseString(jsonStr, pos);
pos = skipWhitespace(jsonStr, pos);

// 解析冒号
if (pos >= jsonStr.size() || jsonStr[pos] != ':') {
throw std::invalid_argument("Invalid JSON object: missing colon after key");
}
pos++;
pos = skipWhitespace(jsonStr, pos);

// 解析值(递归)
JsonValue value = parseValue(jsonStr, pos);
obj[key] = value;
pos = skipWhitespace(jsonStr, pos);

// 处理逗号或结束符
if (pos < jsonStr.size() && jsonStr[pos] == ',') {
pos++;
pos = skipWhitespace(jsonStr, pos);
} else if (jsonStr[pos] != '}') {
throw std::invalid_argument("Invalid JSON object: unexpected character");
}
}

if (pos >= jsonStr.size() || jsonStr[pos] != '}') {
throw std::invalid_argument("Invalid JSON object: missing closing brace");
}
pos++; // 跳过结尾的 }
return obj;
}

// 辅助函数:解析数组(值列表)
static std::vector<JsonValue> parseArray(const std::string& jsonStr, size_t& pos) {
pos++; // 跳过开头的 [
std::vector<JsonValue> arr;
pos = skipWhitespace(jsonStr, pos);

while (pos < jsonStr.size() && jsonStr[pos] != ']') {
// 解析元素(递归)
JsonValue elem = parseValue(jsonStr, pos);
arr.push_back(elem);
pos = skipWhitespace(jsonStr, pos);

// 处理逗号或结束符
if (pos < jsonStr.size() && jsonStr[pos] == ',') {
pos++;
pos = skipWhitespace(jsonStr, pos);
} else if (jsonStr[pos] != ']') {
throw std::invalid_argument("Invalid JSON array: unexpected character");
}
}

if (pos >= jsonStr.size() || jsonStr[pos] != ']') {
throw std::invalid_argument("Invalid JSON array: missing closing bracket");
}
pos++; // 跳过结尾的 ]
return arr;
}

// 递归解析JSON值
static JsonValue parseValue(const std::string& jsonStr, size_t& pos) {
pos = skipWhitespace(jsonStr, pos);
if (pos >= jsonStr.size()) {
throw std::invalid_argument("Empty JSON string");
}

char c = jsonStr[pos];
switch (c) {
case '{': return JsonValue(parseObject(jsonStr, pos));
case '[': return JsonValue(parseArray(jsonStr, pos));
case '"': return JsonValue(parseString(jsonStr, pos));
case 't': case 'f': return JsonValue(parseBoolean(jsonStr, pos));
case 'n': parseNull(jsonStr, pos); return JsonValue(); // null类型
case '-': case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
return JsonValue(parseNumber(jsonStr, pos));
default: throw std::invalid_argument("Invalid JSON value: unexpected character '" + std::string(1, c) + "'");
}
}
};

/**
* @brief 便捷函数:将嵌套map转换为JSON字符串
* @param obj 包含JsonValue的map
* @return JSON格式的字符串
*/
std::string toJson(const std::map<std::string, JsonValue>& obj);

/**
* @brief 便捷函数:将vector<map>转换为JSON数组字符串
* @param vec map数组
* @return JSON数组格式的字符串
*/
std::string toJsonArray(const std::vector<std::map<std::string, std::string>>& vec);

#endif //CBSF_JSONVALUE_H

这个类的递归含量比较高,关键方法是 toJson和fromJson

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
std::string JsonValue::toJson() const {
switch (type_) {
case NULL_TYPE:
return "null";
case STRING:
return "\"" + escapeJsonString(stringValue_) + "\"";
case NUMBER:
// 如果是整数,不显示小数点
if (numberValue_ == static_cast<int>(numberValue_)) {
return std::to_string(static_cast<int>(numberValue_));
}
return std::to_string(numberValue_);
case BOOLEAN:
return boolValue_ ? "true" : "false";
case OBJECT: {
std::string json = "{";
bool first = true;
for (const auto& pair : objectValue_) {
if (!first) json += ",";
json += "\"" + escapeJsonString(pair.first) + "\":" + pair.second.toJson();
first = false;
}
json += "}";
return json;
}
case ARRAY: {
std::string json = "[";
bool first = true;
for (const auto& item : arrayValue_) {
if (!first) json += ",";
json += item.toJson();
first = false;
}
json += "]";
return json;
}
default:
return "null";
}
}

// 实现fromJson方法
void JsonValue::fromJson(const std::string& jsonStr) {
size_t pos = 0;
try {
// 递归解析整个JSON字符串,得到一个完整的JsonValue
JsonValue parsed = parseValue(jsonStr, pos);

// 检查解析是否完全(避免JSON字符串后有多余字符)
pos = skipWhitespace(jsonStr, pos);
if (pos < jsonStr.size()) {
throw std::invalid_argument("Unexpected characters after JSON value");
}

// 将解析结果赋值给当前对象
this->type_ = parsed.type_;
this->stringValue_ = std::move(parsed.stringValue_);
this->numberValue_ = parsed.numberValue_;
this->boolValue_ = parsed.boolValue_;
this->objectValue_ = std::move(parsed.objectValue_);
this->arrayValue_ = std::move(parsed.arrayValue_);
} catch (const std::exception& e) {
// 解析失败时,重置为null类型
this->type_ = NULL_TYPE;
throw std::runtime_error("JSON parse error: " + std::string(e.what()));
}
}

3,高并发线程池

对于可能出现的高并发场景,需要使用线程池来管理线程,避免频繁创建和销毁线程带来的性能开销。线程池的实现需要考虑线程的创建、销毁、任务分配、任务执行、任务队列等细节。

在 C++17 以上,可以更加灵活地处理线程池,这也为实现基础服务器实现可能

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
//
// Created by HP on 2025/11/5.
//

#ifndef CBSF_THREADPOOL_H
#define CBSF_THREADPOOL_H

#include <thread>
#include <vector>
#include <queue>
#include <mutex>
#include <condition_variable>
#include <functional>
#include <atomic>

// 线程池类,用于管理多线程执行任务
// Thread pool class, used to manage multi-thread task execution
class ThreadPool {
private:
// 最大任务数
// Maximum number of tasks
const int maxTasks_ = 10000;
// 线程列表
// Thread list
std::vector<std::thread> threads_;
// 任务队列
// Task queue
std::queue<std::function<void()>> tasks_;
// 互斥锁
// Mutex lock
std::mutex mtx_;
// 条件变量
// Condition variable
std::condition_variable cv_;
// 运行标志
// Running flag
std::atomic<bool> running_;
public:
// 构造函数,初始化线程池
// Constructor, initialize thread pool
explicit ThreadPool(size_t numThreads) ;

// 析构函数,释放线程池资源
// Destructor, release thread pool resources
~ThreadPool() ;

// 添加任务到线程池
// Add task to thread pool
/**
* @brief 添加任务到任务队列中
* @tparam F 函数类型
* @tparam Args 可变参数类型
* @param f 要添加的函数
* @param args 函数参数
* @return 添加成功返回true,队列已满返回false
*/
template<typename F, typename... Args>
bool addTask(F&& f, Args&&... args) {
std::unique_lock<std::mutex> lock(mtx_);
if (!running_) {
return false;
}
if (tasks_.size() >= static_cast<size_t>(maxTasks_)) {
return false;
}
tasks_.emplace(std::bind(
std::forward<F>(f),
std::forward<Args>(args)...
));
lock.unlock();
cv_.notify_one();
return true;
}
};


#endif //CBSF_THREADPOOL_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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#include <stdexcept>
#include <iostream>
#include "../include/Threadpool.h"

/**
* 线程池构造函数,初始化指定数量的工作线程
* Thread pool constructor, initializes specified number of worker threads
* @param numThreads 线程池中工作线程的数量
* @param numThreads Number of worker threads in the thread pool
*/
ThreadPool::ThreadPool(size_t numThreads): running_(true) {
// 创建指定数量的工作线程
for(size_t i = 0; i < numThreads; ++i) {
// 使用emplace_back直接在threads_容器中构造线程
threads_.emplace_back([this] {
// 线程主循环,持续运行直到线程池关闭且没有待处理任务
while(true) {
// 用于存储从任务队列中获取的任务
std::function<void()> task;
{
// 使用互斥锁保护共享资源访问
std::unique_lock<std::mutex> lock(this->mtx_);
// 等待条件变量,直到线程池关闭或任务队列不为空
this->cv_.wait(lock, [this] { return !this->running_ || !this->tasks_.empty(); });
if(!this->running_ && this->tasks_.empty())
// 如果线程池关闭且任务队列为空,则退出线程
return;
task = std::move(this->tasks_.front());
// 从任务队列中获取一个任务
this->tasks_.pop();
}
try {
task();
// 执行任务
} catch (const std::exception&) {
// 捕获并处理标准异常
std::cerr << "Exception in thread pool thread" << std::endl;
} catch (...) {
// 捕获并处理标准异常
std::cerr << "Exception in thread pool thread" << std::endl;
}
}
}
);
}
}


/**
* ThreadPool析构函数
* ThreadPool destructor
* 用于清理线程池资源
* Used to clean up thread pool resources
*/
ThreadPool::~ThreadPool() {
// 使用互斥锁保护共享变量running_
std::unique_lock<std::mutex> lock(mtx_);
// 设置线程池运行状态为false,通知所有线程退出
running_ = false;
// 唤醒所有等待的线程
cv_.notify_all();
// 解锁互斥锁
lock.unlock();

// 遍历所有线程
for(std::thread &thread : threads_)
// 检查线程是否可被join,如果是则等待线程结束
if(thread.joinable())
thread.join();
}

4,服务器实现

服务器实现,就是将客户端的请求,交给线程池处理,然后返回结果。通过上面的几个组件,服务器实现起来就很简单了。

服务器结构

  • Request : 接受请求结构体
  • Response : 返回结果结构体
  • Server : 服务器类
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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
// 请求结构
// Request structure
struct Request {
std::string method;
std::string path;
std::map<std::string, std::string> headers;
std::string body;
std::map<std::string, std::string> queryParams; // 查询参数
std::map<std::string, JsonValue> bodyParams; // 表单参数或JSON参数
std::unique_ptr<JsonValue> jsonBody; // 解析后的JSON对象(如果适用)

[[nodiscard]] std::string query_param(const std::string& key) const {
auto it = queryParams.find(key);
return it != queryParams.end() ? it->second : "";
}

void show() const {
std::cout << "Method: " + method + "\n";
std::cout << "Path: " + path + "\n";
std::cout << "Query Parameters:\n";
for (const auto& [key, value] : queryParams) {
std::cout << " " << key << " = " << value << "\n";
}
std::cout << "Headers:\n";
for (const auto& [key, value] : headers) {
std::cout << " " << key << ": " << value << "\n";
}
std::cout << "Body: " << body << "\n";

if (jsonBody) {
std::cout << "Parsed JSON Body: " << jsonBody->toJson() << "\n";
} else {
std::cout << "Body Parameters (key-value):\n";
for (const auto& [key, value] : bodyParams) {
std::cout << " " << key << " = " << value.toJson() << "\n";
}
}
}

/**
* 解析请求体,根据Content-Type处理不同格式
*/
void parseBody() {
if (body.empty()) {
return;
}

// 获取Content-Type,不区分大小写
std::string contentType;
for (const auto& [key, value] : headers) {
if (strcasecmp(key.c_str(), "content-type") == 0) {
contentType = value;
break;
}
}

// 转换为小写以便比较
std::transform(contentType.begin(), contentType.end(),
contentType.begin(), ::tolower);

// 移除可能的字符集信息,如"application/x-www-form-urlencoded; charset=UTF-8"
size_t semicolonPos = contentType.find(';');
if (semicolonPos != std::string::npos) {
contentType = contentType.substr(0, semicolonPos);
}

// 去除空格
contentType.erase(std::remove_if(contentType.begin(), contentType.end(), ::isspace),
contentType.end());

// 根据Content-Type调用相应的解析函数
if (contentType == "application/x-www-form-urlencoded") {
parseUrlEncodedBody();
} else if (contentType == "application/json") {
parseJsonBody();
} else if (contentType == "multipart/form-data") {
// 处理multipart/form-data(较复杂,需要边界)
std::cerr << "Warning: multipart/form-data parsing not fully implemented\n";
parseMultipartFormData();
} else if (contentType == "text/plain") {
// 纯文本,直接存储
bodyParams["_raw_text"] = JsonValue(body);
} else if (contentType.empty()) {
// 没有Content-Type,尝试自动检测
autoDetectContentType();
} else {
// 未知类型,作为原始文本处理
std::cerr << "Warning: Unknown Content-Type: " << contentType << "\n";
bodyParams["_raw_data"] = JsonValue(body);
}
}

/**
* 获取JSON请求体中的值
* @param key JSON键
* @return 如果键存在且是字符串类型,返回字符串值;否则返回空字符串
*/
[[nodiscard]] std::string json_param(const std::string& key) const {
if (!jsonBody) {
return "";
}

try {
if (jsonBody->getType() == JsonValue::OBJECT) {
auto obj = jsonBody->asObject();
auto it = obj.find(key);
if (it != obj.end()) {
if (it->second.getType() == JsonValue::STRING) {
return it->second.asString();
} else if (it->second.getType() == JsonValue::NUMBER) {
return std::to_string(it->second.asNumber());
} else if (it->second.getType() == JsonValue::BOOLEAN) {
return it->second.asBoolean() ? "true" : "false";
}
}
}
} catch (const std::exception& e) {
std::cerr << "Error getting JSON parameter: " << e.what() << "\n";
}

return "";
}

/**
* 检查请求体是否是JSON
*/
[[nodiscard]] bool isJson() const {
return jsonBody != nullptr;
}

/**
* 获取解析后的JSON对象
*/
[[nodiscard]] const JsonValue* getJsonBody() const {
return jsonBody.get();
}

private:
/**
* 解析application/x-www-form-urlencoded格式的请求体
* 格式: key1=value1&key2=value2
*/
void parseUrlEncodedBody() {
std::stringstream ss(body);
std::string pair;

while (std::getline(ss, pair, '&')) {
if (pair.empty()) continue;

size_t equalsPos = pair.find('=');
if (equalsPos != std::string::npos) {
std::string key = pair.substr(0, equalsPos);
std::string value = pair.substr(equalsPos + 1);

// URL解码
key = urlDecode(key);
value = urlDecode(value);

bodyParams[key] = JsonValue(value);
} else {
// 只有key没有value的情况
bodyParams[urlDecode(pair)] = JsonValue("");
}
}
}

/**
* 解析JSON格式的请求体(使用JsonValue类)
*/
void parseJsonBody() {
try {
// 创建JsonValue对象并解析JSON
jsonBody = std::make_unique<JsonValue>();
jsonBody->fromJson(body);

// 如果是JSON对象,将其扁平化到bodyParams以便向后兼容
if (jsonBody->getType() == JsonValue::OBJECT) {
flattenJsonToParams(*jsonBody, "");
} else if (jsonBody->getType() == JsonValue::ARRAY) {
// 数组类型的JSON,存储到特殊键
bodyParams["_json_array"] = *jsonBody;
} else {
// 其他基本类型
bodyParams["_json_value"] = *jsonBody;
}
} catch (const std::exception& e) {
std::cerr << "Failed to parse JSON body: " << e.what() << "\n";
// JSON解析失败,尝试作为普通文本处理
bodyParams["_invalid_json"] = JsonValue(body);
}
}

/**
* 将JSON对象扁平化为键值对(用于向后兼容)
* @param json JsonValue对象
* @param prefix 键的前缀(用于嵌套对象)
*/
void flattenJsonToParams(const JsonValue& json, const std::string& prefix) {
if (json.getType() == JsonValue::OBJECT) {
auto obj = json.asObject();
for (const auto& [key, value] : obj) {
std::string newKey = prefix.empty() ? key : prefix + "." + key;

if (value.getType() == JsonValue::OBJECT) {
// 递归处理嵌套对象
flattenJsonToParams(value, newKey);
} else if (value.getType() == JsonValue::ARRAY) {
// 数组存储为JSON字符串
bodyParams[newKey] = value;
} else {
// 基本类型转换为字符串
bodyParams[newKey] = value;
}
}
}
}

/**
* 将JsonValue基本类型转换为字符串
*/
static std::string jsonToString(const JsonValue& json) {
switch (json.getType()) {
case JsonValue::STRING:
return json.asString();
case JsonValue::NUMBER:
return std::to_string(json.asNumber());
case JsonValue::BOOLEAN:
return json.asBoolean() ? "true" : "false";
case JsonValue::NULL_TYPE:
return "";
default:
return json.toJson();
}
}

/**
* 解析multipart/form-data(简化实现)
*/
void parseMultipartFormData() {
// 提取boundary
std::string contentTypeHeader;
for (const auto& [key, value] : headers) {
if (strcasecmp(key.c_str(), "content-type") == 0) {
contentTypeHeader = value;
break;
}
}

std::string boundary;
size_t boundaryPos = contentTypeHeader.find("boundary=");
if (boundaryPos != std::string::npos) {
boundary = contentTypeHeader.substr(boundaryPos + 9);
// 去除可能的引号
if (!boundary.empty() && boundary[0] == '"') {
boundary = boundary.substr(1, boundary.size() - 2);
}
}

if (boundary.empty()) {
std::cerr << "Error: No boundary found in multipart/form-data\n";
return;
}

// 完整的multipart解析较复杂,这里只做简单示例
std::string delimiter = "--" + boundary;
std::string endDelimiter = delimiter + "--";

size_t startPos = body.find(delimiter);
if (startPos == std::string::npos) {
return;
}

startPos += delimiter.length();
if (body[startPos] == '\r') startPos++;
if (body[startPos] == '\n') startPos++;

while (true) {
size_t nextDelimiter = body.find(delimiter, startPos);
if (nextDelimiter == std::string::npos) {
break;
}

std::string part = body.substr(startPos, nextDelimiter - startPos);
parseMultipartPart(part);

startPos = nextDelimiter + delimiter.length();
if (body[startPos] == '\r') startPos++;
if (body[startPos] == '\n') startPos++;

// 检查是否是结束边界
if (body.substr(startPos, 2) == "--") {
break;
}
}
}

/**
* 解析multipart的单个part
*/
void parseMultipartPart(const std::string& part) {
// 查找头部结束位置(空行)
size_t headerEnd = part.find("\r\n\r\n");
if (headerEnd == std::string::npos) {
headerEnd = part.find("\n\n");
if (headerEnd == std::string::npos) {
return;
}
}

std::string headersStr = part.substr(0, headerEnd);
std::string content = part.substr(headerEnd + 4); // 跳过空行

// 解析Content-Disposition获取name
std::string name;
size_t namePos = headersStr.find("name=\"");
if (namePos != std::string::npos) {
size_t nameEnd = headersStr.find('\"', namePos + 6);
if (nameEnd != std::string::npos) {
name = headersStr.substr(namePos + 6, nameEnd - namePos - 6);
}
}

if (!name.empty()) {
bodyParams[name] = JsonValue(content);
}
}

/**
* 自动检测Content-Type并解析
*/
void autoDetectContentType() {
if (body.empty()) {
return;
}

// 尝试解析为JSON
try {
// 先检查是否是JSON格式
std::string trimmed = body;
trimmed.erase(std::remove_if(trimmed.begin(), trimmed.end(), ::isspace),
trimmed.end());

if (trimmed.front() == '{' && trimmed.back() == '}') {
// 尝试解析为JSON
JsonValue testJson;
testJson.fromJson(body);
parseJsonBody();
return;
} else if (trimmed.front() == '[' && trimmed.back() == ']') {
// 数组形式的JSON
jsonBody = std::make_unique<JsonValue>();
jsonBody->fromJson(body);
bodyParams["_json_array"] = JsonValue(body);
return;
}
} catch (const std::exception&) {
// 不是有效的JSON,继续尝试其他格式
}

// 检查是否是表单数据
if (body.find('=') != std::string::npos &&
(body.find('&') != std::string::npos || body.find('\n') != std::string::npos)) {
parseUrlEncodedBody();
return;
}

// 默认作为纯文本
bodyParams["_raw_text"] = JsonValue(body);
}

/**
* URL解码(将%XX转换为字符)
*/
static std::string urlDecode(const std::string& str) {
std::string result;
result.reserve(str.length());

for (size_t i = 0; i < str.length(); ++i) {
if (str[i] == '%' && i + 2 < str.length()) {
// 尝试解码%XX格式
std::string hex = str.substr(i + 1, 2);
char ch = static_cast<char>(std::strtoul(hex.c_str(), nullptr, 16));
result.push_back(ch);
i += 2;
} else if (str[i] == '+') {
// +号表示空格
result.push_back(' ');
} else {
result.push_back(str[i]);
}
}

return result;
}

/**
* 不区分大小写的字符串比较辅助函数
*/
static int strcasecmp(const char* s1, const char* s2) {
while (*s1 && *s2 && ::tolower(*s1) == ::tolower(*s2)) {
++s1;
++s2;
}
return ::tolower(*s1) - ::tolower(*s2);
}
};

较多代码的原因是添加了body解析,实际上就是按照相应的格式进行解析,部分未实现。

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
// 响应结构
// Response structure
struct Response {
int statusCode = 200;
std::map<std::string, std::string> headers;
std::string body;

Response() {
headers["Content-Type"] = "application/json; charset=utf-8";
}

// 便捷方法:设置JSON响应
void json(const std::string& jsonStr) {
body = jsonStr;
headers["Content-Type"] = "application/json; charset=utf-8";
}

// 便捷方法:设置文本响应
void text(const std::string& textStr) {
body = textStr;
headers["Content-Type"] = "text/plain; charset=utf-8";
}

// 便捷方法:设置HTML响应
void status(int code) {
statusCode = code;
}

void success(const std::map<std::string, std::string>& resMap) {
std::map<std::string, std::string> result=resMap;
result["status"] = "ok";
result["message"] = "Success";
json(mpToJson(result));
}

// 新版:支持嵌套JSON结构的success方法
void success(const std::map<std::string, JsonValue>& resMap) {
std::map<std::string, JsonValue> result;
result["status"] = JsonValue("ok");
result["message"] = JsonValue("Success");
// 合并传入的数据
for (const auto& pair : resMap) {
result[pair.first] = pair.second;
}
json(toJson(result));
}

void success() {
json(R"({"status":"ok", "message":"Success"})");
}

void error(int code, const std::string& message) {
Log::getInstance()->write("Time "+getFormattedDate()+" Code "+std::to_string(code)+" Error: " + message + "\n");
statusCode = code;
json(R"({"status":"fail", "message":")" + message + "\"}");
}
};

Respond 结构体,用于封装响应信息,包括状态码、头部信息和响应体。其中,jsontextstatussuccesserror 方法用于设置不同的响应类型和内容。预设不同响应,对比 Request 结构体比较简单。

通过这两个结构体,定义出路由处理器的格式

1
2
// 路由处理器类型
typedef std::function<void(const Request&, Response&)> Handler;

然后就可以设计 Server 了。

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
// 简单的HTTP服务器类
// Simple HTTP server class
class Server {
public:
// 构造函数,初始化服务器端口
// Constructor, initialize server port
explicit Server(int port = 8080, bool printParams = false);
// 析构函数
// Destructor
~Server();

// 注册路由 - GET方法
// Register route - GET method
void get(const std::string& path, Handler handler);
// 注册路由 - POST方法
// Register route - POST method
void post(const std::string& path, Handler handler);
// 注册路由 - PUT方法
// Register route - PUT method
void put(const std::string& path, Handler handler);
// 注册路由 - DELETE方法
// Register route - DELETE method
void del(const std::string& path, Handler handler);

// 启动服务器
// Start server
void run();

// 停止服务器
// Stop server
void stop();

// 获取服务器实例
// Get server instance (singleton pattern)
static Server* getInstance();
private:
int port_;
int serverSocket_;
bool running_;
bool LogParams;
std::mutex routesMutex_;
std::mutex logMutex_;
ThreadPool threadpool_;

// 路由表:method -> (path -> handler)
std::map<std::string, std::map<std::string, Handler>> routes_;

// 静态成员用于信号处理
static Server* instance_;

// 静态信号处理函数
static void signalHandler(int sig);

// 注册信号处理器
static void registerSignalHandlers();

#ifdef _WIN32
// Windows控制台关闭事件处理函数(友元函数)
friend BOOL WINAPI ConsoleCtrlHandler(DWORD dwCtrlType);
#endif

// 内部方法
void handleClient(int clientSocket, const sockaddr_in *clientAddress);
static Request parseRequest(const std::string& requestStr);
static std::string buildResponse(const Response& response);
Handler findHandler(const std::string& method, const std::string& path);
static std::map<std::string, std::string> parseQueryParams(const std::string& query);

// 工具函数
void printRegisteredRoutes() const;
static std::string getClientIP(const sockaddr_in *clientAddress) ;

static std::string urlDecode(const std::string &value);

static std::string getLanIpv4();

void listenIPv6(int serverSocketIPv6);

static std::string getLanIpv6();
};

这样设计,整个服务器的框架就出来了。
通过 get, post, put, del 方法注册路由,通过 run 方法启动服务器,通过 stop 方法停止服务器。

我们主要看这几个方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void Server::get(const std::string& path, Handler handler) {
std::lock_guard<std::mutex> lock(routesMutex_);
routes_["GET"][path] = std::move(handler);
}

void Server::post(const std::string& path, Handler handler) {
std::lock_guard<std::mutex> lock(routesMutex_);
routes_["POST"][path] = std::move(handler);
}

void Server::put(const std::string& path, Handler handler) {
std::lock_guard<std::mutex> lock(routesMutex_);
routes_["PUT"][path] = std::move(handler);
}

void Server::del(const std::string& path, Handler handler) {
std::lock_guard<std::mutex> lock(routesMutex_);
routes_["DELETE"][path] = std::move(handler);
}

通过锁保护,将路由注册到路由表中。

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
// start 和 stop

Server::Server(int port, bool printParams) : port_(port),serverSocket_(-1), running_(false), LogParams(printParams), threadpool_(std::thread::hardware_concurrency() ? std::thread::hardware_concurrency() : 1) {
instance_ = this;
registerSignalHandlers(); // 注册信号处理函数
}

/**
* 注册信号处理函数,用于处理程序终止信号
* 根据不同操作系统平台设置相应的信号处理机制
* Register signal handlers for program termination signals
* Configure appropriate signal handling mechanisms based on different operating systems
*/
void Server::registerSignalHandlers() {
#ifdef _WIN32
// Windows上使用SetConsoleCtrlHandler处理控制台关闭事件
SetConsoleCtrlHandler(ConsoleCtrlHandler, TRUE);
signal(SIGINT, signalHandler);
signal(SIGTERM, signalHandler);
#else
// Linux/Unix上信号处理正常工作
signal(SIGINT, signalHandler);
signal(SIGTERM, signalHandler);
#endif
}

/**
* 停止服务器函数
* 该函数用于安全地关闭服务器,释放相关资源
* Stop server function
* This function safely shuts down the server and releases related resources
*/
void Server::stop() {
// 检查服务器是否已经停止,如果已停止则直接返回
if (!running_) {
return; // 已经停止,避免重复输出
}

// 设置服务器运行状态为停止
running_ = false;
// 输出服务器停止信息
std::cout << "Server stopped." << std::endl;

// 检查服务器套接字是否有效
if (serverSocket_ >= 0) {
// 关闭服务器套接字
close(serverSocket_);
// 将套接字描述符重置为无效值
serverSocket_ = -1;
}
#ifdef _WIN32
// 如果是Windows平台,进行Windows套接字清理
WSACleanup();
#endif
}

这个是服务器的启动和停止,通过信号处理函数来处理程序终止信号,从而安全地关闭服务器。

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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
/**
* 服务器运行函数
* 初始化网络环境,创建socket,绑定地址,监听连接并处理客户端请求
*/
void Server::run() {
#ifdef _WIN32
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
std::cerr << "Error: Failed to initialize Winsock" << std::endl;
return;
}
#endif

// 1. 创建并绑定IPv4 socket
serverSocket_ = socket(AF_INET, SOCK_STREAM, 0);
if (serverSocket_ < 0) {
std::cerr << "Error: Failed to create IPv4 socket" << std::endl;
#ifdef _WIN32
WSACleanup();
#endif
return;
}

// 设置socket选项
int opt = 1;
#ifdef _WIN32
setsockopt(serverSocket_, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt, sizeof(opt));
#else
setsockopt(serverSocket_, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
#endif

// 绑定IPv4地址
struct sockaddr_in address{};
memset(&address, 0, sizeof(address));
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(port_); // 使用port_成员变量

if (bind(serverSocket_, (struct sockaddr*)&address, sizeof(address)) < 0) {
std::cerr << "Error: Failed to bind IPv4 to port " << port_
<< ": " << strerror(errno) << std::endl;
#ifdef _WIN32
closesocket(serverSocket_);
WSACleanup();
#else
close(serverSocket_);
#endif
return;
}

// 2. 创建并绑定IPv6 socket
int serverSocketIPv6 = -1;
serverSocketIPv6 = socket(AF_INET6, SOCK_STREAM, 0);
if (serverSocketIPv6 < 0) {
std::cerr << "Warning: Failed to create IPv6 socket: "
<< strerror(errno) << std::endl;
// 不退出,继续使用IPv4
} else {
// 设置socket选项
#ifdef _WIN32
setsockopt(serverSocketIPv6, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt, sizeof(opt));
#else
setsockopt(serverSocketIPv6, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
#endif

// 在Windows上需要设置IPV6_V6ONLY
#ifdef _WIN32
int ipv6Only = 1;
setsockopt(serverSocketIPv6, IPPROTO_IPV6, IPV6_V6ONLY,
(const char*)&ipv6Only, sizeof(ipv6Only));
#endif

// 绑定IPv6地址 - 使用相同的端口号
struct sockaddr_in6 addr6{};
memset(&addr6, 0, sizeof(addr6));
addr6.sin6_family = AF_INET6;
addr6.sin6_addr = in6addr_any;
addr6.sin6_port = htons(port_); // 使用port_成员变量,确保端口一致

if (bind(serverSocketIPv6, (struct sockaddr*)&addr6, sizeof(addr6)) < 0) {
std::cerr << "Warning: Failed to bind IPv6 to port " << port_
<< ": " << strerror(errno) << std::endl;
#ifdef _WIN32
closesocket(serverSocketIPv6);
#else
close(serverSocketIPv6);
#endif
serverSocketIPv6 = -1; // 标记为无效
}
}

// 3. 开始监听
if (listen(serverSocket_, 10) < 0) {
std::cerr << "Error: Failed to listen on IPv4 socket: "
<< strerror(errno) << std::endl;
#ifdef _WIN32
closesocket(serverSocket_);
if (serverSocketIPv6 >= 0) closesocket(serverSocketIPv6);
WSACleanup();
#else
close(serverSocket_);
if (serverSocketIPv6 >= 0) close(serverSocketIPv6);
#endif
return;
}

// 如果IPv6 socket创建并绑定成功,开始监听
if (serverSocketIPv6 >= 0) {
if (listen(serverSocketIPv6, 10) < 0) {
std::cerr << "Warning: Failed to listen on IPv6 socket: "
<< strerror(errno) << std::endl;
#ifdef _WIN32
closesocket(serverSocketIPv6);
#else
close(serverSocketIPv6);
#endif
serverSocketIPv6 = -1;
} else {
std::cout << "IPv6 socket listening on [::]:" << port_ << std::endl;
// 启动独立线程监听IPv6连接
std::thread([this, serverSocketIPv6]() {
this->listenIPv6(serverSocketIPv6);
}).detach();
}
}

running_ = true;

// 打印服务器信息
printRegisteredRoutes();
std::cout << "Server running on:" << std::endl;
std::cout << " Localhost: http://localhost:" << port_ << std::endl;
std::cout << " LAN IPv4: http://" << getLanIpv4() << ":" << port_ << std::endl;
std::cout << " Localhost IPv6: http://[::1]:" << port_ << std::endl;
if (serverSocketIPv6 >= 0) {
std::cout << " LAN IPv6: http://["<< getLanIpv6() << "]:" << port_ << std::endl;
}
std::cout << "==========================================="<< std::endl;

// 主循环接受IPv4连接
while (running_) {
struct sockaddr_in clientAddress{};
socklen_t clientAddrLen = sizeof(clientAddress);

int clientSocket = accept(serverSocket_,
(struct sockaddr*)&clientAddress,
&clientAddrLen);
if (clientSocket < 0) {
if (running_) {
std::cerr << "Error: Failed to accept IPv4 connection: "
<< strerror(errno) << std::endl;
}
continue;
}

// 添加到线程池
threadpool_.addTask(&Server::handleClient, this,
clientSocket, &clientAddress);
}
}

void Server::listenIPv6(int serverSocketIPv6) {
while (running_) {
struct sockaddr_storage clientAddr{}; // 使用sockaddr_storage通用结构
socklen_t clientAddrLen = sizeof(clientAddr);

int clientSocket = accept(serverSocketIPv6,
(struct sockaddr*)&clientAddr,
&clientAddrLen);
if (clientSocket < 0) {
if (running_) {
std::cerr << "Error: Failed to accept IPv6 connection: "
<< strerror(errno) << std::endl;
}
continue;
}

// 根据地址族传递正确的参数
if (clientAddr.ss_family == AF_INET6) {
threadpool_.addTask(&Server::handleClient, this,
clientSocket,
(struct sockaddr_in*)&clientAddr);
}
}

#ifdef _WIN32
closesocket(serverSocketIPv6);
#else
close(serverSocketIPv6);
#endif
}

std::string Server::getLanIpv4() {
std::string lanIp = "127.0.0.1"; // 默认本地回环(获取失败时返回)
int sockfd = -1;

#ifdef _WIN32
// Windows 初始化 Winsock(仅当前函数内临时使用,避免影响全局)
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
return lanIp;
}
#endif

// 1. 创建 UDP socket(SOCK_DGRAM),仅用于获取本地 IP,不实际发送数据
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0) {
#ifdef _WIN32
WSACleanup();
#endif
return lanIp;
}

// 2. 配置公网服务器地址(Google DNS 8.8.8.8,端口 53,仅用于触发本地路由)
struct sockaddr_in serverAddr{};
memset(&serverAddr, 0, sizeof(serverAddr));
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(53); // DNS 端口(任意端口均可,53 是常用默认值)

// 解析 IP 地址(兼容 IPv4 字符串直接转换)
if (inet_pton(AF_INET, "8.8.8.8", &serverAddr.sin_addr) <= 0) {
// 解析失败时,尝试通过域名解析(可选,增强兼容性)
struct hostent* host = gethostbyname("8.8.8.8");
if (host == nullptr || host->h_addr_list[0] == nullptr) {
#ifdef _WIN32
closesocket(sockfd);
WSACleanup();
#else
close(sockfd);
#endif
return lanIp;
}
memcpy(&serverAddr.sin_addr, host->h_addr_list[0], host->h_length);
}

// 3. 连接 UDP socket(UDP 是无连接协议,但 connect 仅用于绑定目标地址,不建立实际连接)
// 此操作会让系统自动选择本地合适的网卡(局域网网卡)和端口,从而获取局域网 IP
if (connect(sockfd, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) == 0) {
// 4. 获取本地绑定的地址(即局域网 IP)
struct sockaddr_in localAddr{};
socklen_t localAddrLen = sizeof(localAddr);
if (getsockname(sockfd, (struct sockaddr*)&localAddr, &localAddrLen) == 0) {
// 转换网络字节序 IP 到字符串
char ipBuf[INET_ADDRSTRLEN] = {0};
if (inet_ntop(AF_INET, &localAddr.sin_addr, ipBuf, sizeof(ipBuf)) != nullptr) {
lanIp = ipBuf;
}
}
}

// 5. 清理资源
#ifdef _WIN32
closesocket(sockfd);
WSACleanup();
#else
close(sockfd);
#endif

return lanIp;
}


std::string Server::getLanIpv6() {
std::string lanIpv6 = "::1";
int sockfd = -1;

#ifdef _WIN32
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
return lanIpv6;
}
#endif

sockfd = socket(AF_INET6, SOCK_DGRAM, 0);
if (sockfd < 0) {
#ifdef _WIN32
WSACleanup();
#endif
return lanIpv6;
}

struct sockaddr_in6 serverAddr{};
memset(&serverAddr, 0, sizeof(serverAddr));
serverAddr.sin6_family = AF_INET6;
serverAddr.sin6_port = htons(53);

// 解析 IPv6 地址(跨平台兼容版本)
if (inet_pton(AF_INET6, "2001:4860:4860::8888", &serverAddr.sin6_addr) <= 0) {
struct addrinfo hints{}, *res = nullptr;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET6;
hints.ai_socktype = SOCK_DGRAM;

int ret = getaddrinfo("ipv6.google.com", nullptr, &hints, &res);
if (ret != 0 || res == nullptr) {
#ifdef _WIN32
closesocket(sockfd);
WSACleanup();
#else
close(sockfd);
#endif
return lanIpv6;
}

memcpy(&serverAddr.sin6_addr,
&((struct sockaddr_in6*)res->ai_addr)->sin6_addr,
sizeof(in6_addr));
freeaddrinfo(res);
}

if (connect(sockfd, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) == 0) {
struct sockaddr_in6 localAddr{};
socklen_t localAddrLen = sizeof(localAddr);
if (getsockname(sockfd, (struct sockaddr*)&localAddr, &localAddrLen) == 0) {
uint8_t* addrBytes = localAddr.sin6_addr.s6_addr;
bool isLinkLocal = (addrBytes[0] == 0xfe && (addrBytes[1] & 0xc0) == 0x80);
bool isLoopback = (memcmp(addrBytes, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", 16) == 0);

if (!isLinkLocal && !isLoopback) {
char ipBuf[INET6_ADDRSTRLEN] = {0};
if (inet_ntop(AF_INET6, &localAddr.sin6_addr, ipBuf, sizeof(ipBuf)) != nullptr) {
lanIpv6 = ipBuf;
}
}
}
}

#ifdef _WIN32
closesocket(sockfd);
WSACleanup();
#else
close(sockfd);
#endif

return lanIpv6;
}

先通过 socket 函数创建 IPv4 和 IPv6 的 socket,然后分别调用 bind 和 listen 函数进行绑定和监听。如果 IPv6 的 socket 创建和绑定成功,则启动一个独立的线程来监听 IPv6 连接。在主循环中,使用 accept 函数接受 IPv4 连接,并添加到线程池中处理。

后面每次接到连接,先拿到 clientSocket,是请求在网络上的标识,然后通过 clientSocket 获取到 clientAddress,这个是客户端的地址信息,包括 IP 地址和端口号。然后通过 clientSocket 和 clientAddress 创建一个 ClientSession 对象,这个对象负责处理这个连接的请求。

然后就是 handleClient,用于查找并处理请求。

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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
/**
* 处理客户端请求的函数
* @param clientSocket 客户端套接字描述符
* @param clientAddress 客户端地址结构指针
* Handle client request function
* @param clientSocket Client socket descriptor
* @param clientAddress Client address structure pointer
*/
void Server::handleClient(int clientSocket, const sockaddr_in *clientAddress) {
std::time_t now = std::time(nullptr);
long long timestamp = static_cast<long long>(now) * 1000;

// 创建缓冲区并初始化为0,用于接收客户端数据
char buffer[8192] = {0};
// 从客户端读取数据,读取的字节数存储在bytesRead中
int bytesRead = recv(clientSocket, buffer, sizeof(buffer) - 1, 0);

// 如果读取字节数小于等于0,表示连接已关闭或出错
if (bytesRead <= 0) {
// 关闭客户端套接字并返回
close(clientSocket);
return;
}

// 将接收到的数据转换为字符串
std::string requestStr(buffer, bytesRead);

Log::getInstance()->write(getFormattedDate()+" "+getClientIP(clientAddress)+" "+requestStr);

// 解析HTTP请求
Request request = parseRequest(requestStr);

// 创建响应对象
Response response;

// 处理 OPTIONS 预检请求(CORS)
if (request.method == "OPTIONS") {
response.statusCode = 200;
response.body = "";
// CORS 头将在 buildResponse 中添加
} else {
// 查找路由处理器
Handler handler = findHandler(request.method, request.path);
if (handler) {
// 如果找到处理器,执行处理函数
try {
handler(request, response);
} catch (const std::exception& e) {
// 捕获异常并返回500错误
response.statusCode = 500;
response.error(500,"error: " + std::string(e.what()));
}
} else {
// 如果未找到处理器,返回404错误
response.statusCode = 404;
response.error(404, "Resource not found");
}
}

// 构建响应字符串并发送给客户端
std::string responseStr = buildResponse(response);
send(clientSocket, responseStr.c_str(), responseStr.length(), 0);

// 打印访问日志(类似Apache/Nginx格式)
std::string clientIP = getClientIP(clientAddress);
std::lock_guard<std::mutex> lock(logMutex_);
std::cout << clientIP << " - - [" << getFormattedDate() << "] \""
<< request.method << " " << request.path;
if (!request.queryParams.empty()&&LogParams) {
std::cout << "?";
bool first = true;
for (const auto& param : request.queryParams) {
if (!first) std::cout << "&";
std::cout << param.first << "=" << param.second;
first = false;
}
}
std::cout << " HTTP/1.1\" " << response.statusCode << " "
<< response.body.length();

std::time_t end_time = std::time(nullptr);
long long end_timestamp = static_cast<long long>(end_time) * 1000;
std::cout << end_timestamp - timestamp << "ms" << std::endl;

close(clientSocket);
}

/**
* 解析HTTP请求字符串,将其解析为Request结构体
* @param requestStr HTTP请求字符串
* @return 解析后的Request对象
*/
Request Server::parseRequest(const std::string& requestStr) {
// 创建Request对象和字符串流
Request request;
std::istringstream iss(requestStr);
std::string line;

// 解析请求行(第一行)
if (std::getline(iss, line)) {
std::istringstream lineStream(line);
// 提取方法和路径
lineStream >> request.method >> request.path;

// 分离路径和查询参数
size_t queryPos = request.path.find('?');
if (queryPos != std::string::npos) {
std::string query = request.path.substr(queryPos + 1);
request.queryParams = parseQueryParams(query);
// 解析查询参数
request.path = request.path.substr(0, queryPos);
}
// 更新路径为不包含查询参数的部分
}

// 解析头部和body
bool inBody = false;
std::string body;
while (std::getline(iss, line)) { // 标记是否进入body部分
if (line.empty() || line == "\r") {
inBody = true;
// 空行或\r表示头部结束,body开始
continue;
}

if (!inBody) {
size_t colonPos = line.find(':');
if (colonPos != std::string::npos) {
// 解析头部字段
std::string key = line.substr(0, colonPos);
std::string value = line.substr(colonPos + 1);
// 提取键值对

// 去除空格和\r
key.erase(0, key.find_first_not_of(" \t\r"));
key.erase(key.find_last_not_of(" \t\r") + 1);
value.erase(0, value.find_first_not_of(" \t\r"));
value.erase(value.find_last_not_of(" \t\r") + 1);

request.headers[key] = value;
// std::cout<<"key: "<<key<<" value: "<<value<<std::endl;
}
} else {
body += line;
if (!iss.eof()) body += "\n";
}
}

request.body = body;
request.parseBody();
return request;
}

/**
* 构建HTTP响应字符串
* @param response 包含状态码、头部和响应体的Response对象
* @return 构建好的HTTP响应字符串
*/
std::string Server::buildResponse(const Response& response) {
// std::cout<<"Build Response"<<std::endl;
// std::cout<<response.statusCode<<std::endl;
std::ostringstream oss; // 使用字符串流构建响应

// 添加状态行,包括HTTP版本、状态码和状态描述
oss << "HTTP/1.1 " << response.statusCode << " ";
switch (response.statusCode) {
case 200: oss << "OK"; break; // 200 OK - 请求成功
// 200 OK - Request successful
case 201: oss << "Created"; break; // 201 Created - 资源创建成功
// 201 Created - Resource created successfully
case 400: oss << "Bad Request"; break; // 400 Bad Request - 客户端请求错误
// 400 Bad Request - Client request error
case 404: oss << "Not Found"; break; // 404 Not Found - 资源未找到
// 404 Not Found - Resource not found
case 500: oss << "Internal Server Error"; break; // 500 Internal Server Error - 服务器内部错误
// 500 Internal Server Error - Server internal error
default: oss << "Unknown"; break; // 未知状态码
}
oss << "\r\n"; // HTTP协议使用\r\n作为换行符

// 添加 CORS 头(跨域支持)
oss << "Access-Control-Allow-Origin: *\r\n";
oss << "Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS\r\n";
oss << "Access-Control-Allow-Headers: Content-Type, Authorization, X-Requested-With\r\n";
oss << "Access-Control-Max-Age: 86400\r\n"; // 预检请求缓存24小时

// 添加其他头部
for (const auto& header : response.headers) {
oss << header.first << ": " << header.second << "\r\n";
}

oss << "Content-Length: " << response.body.length() << "\r\n";
oss << "\r\n";
oss << response.body;

// std::cout<<oss.str()<<std::endl;

return oss.str();
}

/**
* 查找并返回与给定方法和路径匹配的处理程序
* @param method HTTP方法(如"GET"、"POST"等)
* @param path 请求的URL路径
* @return 匹配的Handler对象,如果未找到则返回nullptr
*/
Handler Server::findHandler(const std::string& method, const std::string& path) {
// 检查是否存在对应方法的路由映射
if (routes_.find(method) != routes_.end()) {
const auto& methodRoutes = routes_[method]; // 获取该方法的所有路由

// 精确匹配检查
if (methodRoutes.find(path) != methodRoutes.end()) {
return methodRoutes.at(path); // 返回精确匹配的处理程序
}
}

return nullptr; // 未找到匹配的处理程序,返回空指针
}

流程:

  • 1,解析请求,获取请求行、头部、body
  • 2,根据请求行中的方法和路径,查找对应的处理程序
  • 3,调用处理程序,获取响应
  • 4,构建响应字符串
  • 5,发送响应

至此,一个简单的C++后端服务器实现完成。其余部分欢迎小伙伴们继续完善。

5. Redis缓存(RDConnector)实现原理

5.1 单例模式设计

RDConnector类采用了单例模式设计,确保整个应用中只有一个Redis连接实例。核心实现包括:

1
2
3
4
5
6
7
8
9
10
11
12
// 静态单例实例指针
RdConnector* RdConnector::instance_ = nullptr;

// 获取单例实例
RdConnector *RdConnector::getInstance() {
return instance_; // 返回静态单例实例指针
}

// 在connect方法中设置单例实例
if (instance_ == nullptr) {
instance_ = this;
}

单例模式的设计确保了应用程序中只维护一个Redis连接,避免了连接资源的浪费和潜在的连接管理问题。

5.2 Redis连接管理

Redis连接的建立和管理是通过hiredis客户端库实现的。连接过程包括以下几个关键步骤:

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
bool RdConnector::connect() {
// 检查是否已经存在其他RdConnector实例
if (instance_ != nullptr && instance_ != this) {
std::cerr << "Only one instance of RdConnector is allowed." << std::endl;
return false;
}

// 如果已存在连接,则先断开
if (context != nullptr) {
redisFree(context);
context = nullptr;
}

// 将端口号字符串转换为整数
int port = -1;
try {
port = std::stoi(port_);
} catch (...) {
std::cerr << "端口格式错误:" << port << std::endl;
return false;
}

// 尝试连接到Redis服务器
context = redisConnect(host_.c_str(), port);
if (context == nullptr || context->err) {
std::cerr << "连接失败:" << (context ? context->errstr : "内存分配失败") << std::endl;
return false;
}

// 如果配置了密码,进行身份验证
if (!password_.empty()) {
auto* reply = (redisReply*)redisCommand(context, "AUTH %s", password_.c_str());
if (reply == nullptr || reply->type == REDIS_REPLY_ERROR) {
std::cerr << "密码验证失败:" << (reply ? reply->str : context->errstr) << std::endl;
redisFree(context);
context = nullptr;
freeReplyObject(reply);
return false;
}
freeReplyObject(reply);
}

// 如果指定了非0数据库,切换到指定数据库
if(db!=0){
auto* reply = (redisReply*)redisCommand(context, "SELECT %d", db);
if (reply == nullptr || reply->type == REDIS_REPLY_ERROR) {
std::cerr << "切换数据库失败:" << (reply ? reply->str : context->errstr) << std::endl;
redisFree(context);
context = nullptr;
freeReplyObject(reply);
return false;
}
freeReplyObject(reply);
}

// 如果是第一个实例,则设置instance_指针
if (instance_ == nullptr) {
instance_ = this;
}

return true;
}

连接管理的特点:

  • 连接前检查单例唯一性
  • 断开已存在的连接,避免资源泄露
  • 支持密码验证,增强安全性
  • 支持数据库切换,实现数据隔离
  • 详细的错误处理和日志输出

5.3 基本数据操作功能

RDConnector提供了一系列基本的数据操作方法,封装了Redis的常用命令:

5.3.1 获取数据(GET)
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
std::string RdConnector::get(const std::string& key) {
if (context == nullptr){
std::cerr<<"未连接"<<std::endl;
return "";
}

auto* reply = (redisReply*)redisCommand(context, "GET %s", key.c_str());
if (reply == nullptr || reply->type == REDIS_REPLY_ERROR) {
std::cerr << "获取失败:" << (reply ? reply->str : context->errstr) << std::endl;
freeReplyObject(reply);
return "";
}

std::string result;
if (reply->type == REDIS_REPLY_STRING) {
result = reply->str; // 键存在,返回值
} else if (reply->type == REDIS_REPLY_NIL) {
result = ""; // 键不存在,返回空
} else {
std::cerr << "GET 命令返回异常(类型:" << reply->type << ")" << std::endl;
result = "";
}

freeReplyObject(reply);
return result;
}
2.3.2 设置数据(SET)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
bool RdConnector::set(const std::string& key, const std::string& value) {
if (context == nullptr){
std::cerr<<"未连接"<<std::endl;
return false;
}

auto* reply = (redisReply*)redisCommand(context, "SET %s %s", key.c_str(), value.c_str());
if (reply == nullptr || reply->type == REDIS_REPLY_ERROR) {
std::cerr << "命令执行失败:" << (reply ? reply->str : context->errstr) << std::endl;
freeReplyObject(reply);
return false;
}

bool success = (reply->type == REDIS_REPLY_STATUS && std::string(reply->str) == "OK");
if (!success) {
std::cerr << "SET 命令失败:" << (reply->str ? reply->str : "未知错误") << std::endl;
}

freeReplyObject(reply);
return success;
}
2.3.3 设置数据并指定过期时间(SETEX)
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
bool RdConnector::set(const std::string& key, const std::string& value, int expireSeconds) {
if (context == nullptr){
std::cerr<<"未连接"<<std::endl;
return false;
}

if (expireSeconds <= 0) {
std::cerr << "过期时间必须大于0" << std::endl;
return false;
}

auto* reply = (redisReply*)redisCommand(context, "SETEX %s %d %s", key.c_str(), expireSeconds, value.c_str());
if (reply == nullptr || reply->type == REDIS_REPLY_ERROR) {
std::cerr << "命令执行失败:" << (reply ? reply->str : context->errstr) << std::endl;
freeReplyObject(reply);
return false;
}

bool success = (reply->type == REDIS_REPLY_STATUS && std::string(reply->str) == "OK");
if (!success) {
std::cerr << "SETEX 命令失败:" << (reply->str ? reply->str : "未知错误") << std::endl;
}

freeReplyObject(reply);
return success;
}
5.3.4 检查键是否存在(EXISTS)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
bool RdConnector::exists(const std::string& key) {
if (context == nullptr){
std::cerr<<"未连接"<<std::endl;
return false;
}

auto* reply = (redisReply*)redisCommand(context, "EXISTS %s", key.c_str());
if (reply == nullptr || reply->type == REDIS_REPLY_ERROR) {
std::cerr << "命令执行失败:" << (reply ? reply->str : context->errstr) << std::endl;
freeReplyObject(reply);
return false;
}

bool keyExists = false;
if (reply->type == REDIS_REPLY_INTEGER) {
keyExists = (reply->integer == 1);
} else {
std::cerr << "EXISTS 命令返回异常(类型:" << reply->type << ")" << std::endl;
}

freeReplyObject(reply);
return keyExists;
}
5.3.5 删除数据(DEL)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
bool RdConnector::del(const std::string& key) {
if (context == nullptr) {
std::cerr << "未连接Redis服务器" << std::endl;
return false;
}

redisReply* reply = (redisReply*)redisCommand(context, "DEL %s", key.c_str());
if (reply == nullptr) {
std::cerr << "DEL命令执行失败:" << context->errstr << std::endl;
return false;
}

bool success = true;
if (reply->type == REDIS_REPLY_ERROR) {
std::cerr << "DEL命令失败:" << reply->str << std::endl;
success = false;
} else if (reply->type != REDIS_REPLY_INTEGER) {
std::cerr << "DEL命令返回异常(类型:" << reply->type << ")" << std::endl;
success = false;
}

freeReplyObject(reply);
return success;
}

5.4 错误处理机制

RDConnector实现了完善的错误处理机制:

  1. 连接错误处理:检查连接是否成功建立
  2. 命令执行错误处理:捕获并记录命令执行失败的情况
  3. 类型检查:验证Redis返回数据的类型是否符合预期
  4. 内存管理:确保每次操作后正确释放Redis回复对象
  5. 错误信息获取:通过getError()方法获取最近的错误信息
1
2
3
4
5
6
std::string RdConnector::getError() {
if (context == nullptr) {
return "未初始化连接"; // 连接未初始化时的错误信息
}
return context->errstr; // 返回Redis上下文的错误信息
}

5.5 使用示例

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
// 初始化Redis连接
RdConnector* redis = new RdConnector();
redis->setHost("localhost");
redis->setPort("6379");
redis->setPassword("redis_password"); // 可选,如果Redis配置了密码
redis->setDb(0); // 可选,默认是0

if (redis->connect()) {
std::cout << "Redis连接成功" << std::endl;

// 设置键值对
if (redis->set("user:1:name", "张三")) {
std::cout << "设置数据成功" << std::endl;
}

// 设置键值对并指定过期时间(5分钟)
if (redis->set("session:abc123", "user_data", 300)) {
std::cout << "设置带过期时间的数据成功" << std::endl;
}

// 检查键是否存在
if (redis->exists("user:1:name")) {
// 获取键值
std::string value = redis->get("user:1:name");
std::cout << "获取的值: " << value << std::endl;
}

// 删除键
if (redis->del("user:1:name")) {
std::cout << "删除键成功" << std::endl;
}

delete redis; // 释放连接对象
}

// 使用单例模式
RdConnector* redisInstance = new RdConnector();
redisInstance->setHost("localhost");
redisInstance->setPort("6379");
if (redisInstance->connect()) { // 这会设置全局单例
// 通过单例访问
RdConnector* sameInstance = RdConnector::getInstance();
sameInstance->set("app:version", "1.0.0");

delete redisInstance; // 注意:释放对象后,单例指针仍然存在,但指向的对象已被销毁
// 实际应用中应避免这种情况,确保单例的生命周期管理正确
}

6. 数据库连接(DBConnector)实现原理

6.1 单例模式设计

DBConnector类采用了单例模式设计,确保整个应用中只有一个数据库连接实例。这种设计模式能够有效管理数据库连接资源,避免频繁创建和销毁连接带来的性能开销。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 静态单例实例指针
DBConnector* DBConnector::instance_ = nullptr;

// 初始化单例
void DBConnector::initInstance(const std::string& host, const std::string& user, const std::string& password, const std::string& database, const std::string& port) {
// 如果实例不存在,则创建新实例
if (instance_ == nullptr) {
instance_ = new DBConnector(host, user, password, database, port);
}
}

// 获取单例实例
DBConnector* DBConnector::getInstance() {
return instance_; // 返回静态单例实例指针
}

// 销毁单例实例
void DBConnector::destroyInstance() {
if (instance_ != nullptr) {
delete instance_; // 删除实例,调用析构函数关闭连接
instance_ = nullptr; // 重置指针
}
}

单例模式的实现确保了应用程序在任何时候都只能有一个数据库连接对象,从而避免了多线程环境下的连接资源争用问题。

6.2 连接管理

DBConnector通过MySQL C API管理数据库连接,主要包括连接的建立、维护和关闭:

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
// 构造函数初始化连接参数
DBConnector::DBConnector(const std::string& host, const std::string& user, const std::string& password, const std::string& database, const std::string& port)
: host_(host), user_(user), password_(password), database_(database), port_(port), db_(nullptr) {
// 初始化MySQL句柄
db_ = mysql_init(nullptr);
if (db_ == nullptr) {
std::cerr << "MySQL初始化失败" << std::endl;
}
}

// 析构函数关闭连接
DBConnector::~DBConnector() {
// 关闭数据库连接
if (db_ != nullptr) {
mysql_close(db_);
db_ = nullptr;
}
}

// 建立数据库连接
bool DBConnector::connect() {
if (db_ == nullptr) {
std::cerr << "连接失败:MySQL未初始化" << std::endl;
return false;
}

// 尝试连接到MySQL服务器
if (!mysql_real_connect(db_, host_.c_str(), user_.c_str(), password_.c_str(), database_.c_str(), std::stoi(port_), nullptr, 0)) {
std::cerr << "连接失败:" << mysql_error(db_) << std::endl;
return false;
}

// 设置字符集为UTF-8,确保中文正常显示
if (mysql_set_character_set(db_, "utf8mb4") != 0) {
std::cerr << "设置字符集失败:" << mysql_error(db_) << std::endl;
// 字符集设置失败不会影响连接,继续执行
}

return true;
}

连接管理的关键特点:

  • 构造函数初始化MySQL句柄
  • 析构函数自动关闭连接,避免资源泄漏
  • connect方法处理连接建立和字符集设置
  • 完善的错误处理和日志输出

6.3 查询与执行功能

DBConnector提供了两个核心方法来操作数据库:

6.3.1 query方法(用于SELECT查询)
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
MYSQL_RES* DBConnector::query(const std::string& sql) {
// 检查连接是否有效
if (db_ == nullptr) {
std::cerr << "查询失败:未连接到数据库" << std::endl;
return nullptr;
}

// 执行SQL查询
if (mysql_query(db_, sql.c_str()) != 0) {
std::cerr << "查询失败:" << mysql_error(db_) << std::endl;
std::cerr << "SQL: " << sql << std::endl;
return nullptr;
}

// 存储结果集
MYSQL_RES* result = mysql_store_result(db_);
if (result == nullptr) {
// 检查是否是因为没有结果集(例如非SELECT语句)
if (mysql_field_count(db_) == 0) {
// 这是预期的,不是错误
return nullptr;
} else {
std::cerr << "获取结果集失败:" << mysql_error(db_) << std::endl;
return nullptr;
}
}

return result;
}
6.3.2 execute方法(用于INSERT、UPDATE、DELETE)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
bool DBConnector::execute(const std::string& sql) {
// 检查连接是否有效
if (db_ == nullptr) {
std::cerr << "执行失败:未连接到数据库" << std::endl;
return false;
}

// 执行SQL语句
if (mysql_query(db_, sql.c_str()) != 0) {
std::cerr << "执行失败:" << mysql_error(db_) << std::endl;
std::cerr << "SQL: " << sql << std::endl;
return false;
}

// 检查是否有结果集(非SELECT语句不应有结果集)
MYSQL_RES* result = mysql_store_result(db_);
if (result != nullptr) {
// 如果返回了结果集,说明SQL可能不是预期的非查询语句
std::cerr << "警告:非查询语句返回了结果集" << std::endl;
mysql_free_result(result);
}

return true;
}

6.4 SQL注入防护

DBConnector实现了SQL注入防护机制,通过escapeSqlLiteral方法对SQL字符串进行转义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
std::string DBConnector::escapeSqlLiteral(const std::string& value) {
// 如果连接不存在,返回空字符串
if (db_ == nullptr) {
return "";
}

// 分配足够大的缓冲区(MySQL要求至少是原值的2倍+1)
size_t bufferSize = value.length() * 2 + 1;
char* buffer = new char[bufferSize];

// 使用MySQL的转义函数处理字符串
mysql_real_escape_string(db_, buffer, value.c_str(), value.length());

// 创建结果字符串
std::string escaped(buffer);

// 释放缓冲区
delete[] buffer;

return escaped;
}

6.5 使用示例

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
// 初始化数据库连接
DBConnector* db = new DBConnector();
db->setHost("localhost");
db->setUser("admin");
db->setPassword("password");
db->setDatabase("mydb");
db->setPort("3306");

if (db->connect()) {
std::cout << "数据库连接成功" << std::endl;

// 执行SQL查询
auto result = db->query("SELECT * FROM users LIMIT 10");
if (result != nullptr) {
// 处理查询结果
int num_fields = mysql_num_fields(result);
while (auto row = mysql_fetch_row(result)) {
for (int i = 0; i < num_fields; i++) {
std::cout << (row[i] ? row[i] : "NULL") << " ";
}
std::cout << std::endl;
}
db->freeResult(result); // 释放结果集
}

// 执行SQL更新
if (db->execute("UPDATE users SET status = 1 WHERE id = 1")) {
std::cout << "更新成功,影响行数: " << db->affectedRows() << std::endl;
}

delete db; // 释放连接对象
}

// 使用单例模式
DBConnector::initInstance("localhost", "admin", "password", "mydb", "3306");
DBConnector* dbInstance = DBConnector::getInstance();
if (dbInstance->connect()) {
// 使用单例进行数据库操作
// ...
DBConnector::destroyInstance(); // 释放单例
}

通过数据库连接和缓存功能的添加,服务器的功能更加完善,能够有效管理数据持久化和缓存,提升应用的性能和响应速度。