基于boost的https服务器端实现
- 需求分析
客户需要管理一批数据(数据采集来的),内网环境下让授权用户在任何计算机都可以访问数据,处理数据,但不让下载(保护数据资产安全)
设计是数据存储到mysql服务器,使用redis加速数据中关键参数的访问,由于对数据的访问速度有要求(需要对数据可视化然后进行处理,不能让用户点一下按钮等半天)
什么样的数据呢?类似这样,每100ms采集一次的
- 本期解决的问题
1.建立用户管理服务器,响应客户端的登录请求,使用https返回管理员为其分配的mysql账号和密码,redis账号和密码
2.以控制台交互式菜单的形式,实现管理员创建用户,在mysql和redis为用户创建资源的逻辑
- 技术栈 梳理
boost.asio 网络库
openssl创建自签名证书,实现传输数据的https加密
jsoncpp json数据解析
hiredis redis数据库访问接口
MySQL Connector/C++ mysql数据库访问接口
asio多线程模型 AsioIOServicePool asio并发支持
因为暂时用不到后端服务间过程调用,暂不使用grpc(无状态设计,登录状态写到数据库并和客户端心跳验证,解决重登录问题)
- 好了,废话结束,我们开始抄代码
你问我从哪里超,https://gitbookcpp.llfc.club/大佬的博客写的很清楚,我是超人强,越超我越强,开超
程序入口的,main()
int main(int argc, char* argv[])
{
try
{
创建连接池
MysqlMgr::GetInstance();
RedisMgr::GetInstance();
初始化并读取ini文件
auto& gCfgMgr = ConfigMgr::Inst();
std::string gate_port_str = gCfgMgr["GateServer"]["Port"];
unsigned short gate_port = atoi(gate_port_str.c_str());
创建io_context上下文并启动
net::io_context ioc{ 1 };
boost::asio::signal_set signals(ioc, SIGINT, SIGTERM);
signals.async_wait([&ioc](const boost::system::error_code& error, int signal_number) {
if (error) {
return;
}
ioc.stop();
});
std::make_shared<CServer>(ioc)->Start();
std::cout << "Gate Server listen on port: " << gate_port << std::endl;
ioc.run();
RedisMgr::GetInstance()->Close();
}
catch (std::exception const& e)
{
std::cerr << "Error: " << e.what() << std::endl;
return EXIT_FAILURE;
}
}
然后是cserver的代码
#pragma once
#include <string>
#include "const.h"
class CServer :public std::enable_shared_from_this<CServer>
{
public:
CServer(boost::asio::io_context& ioc);
void Start();
private:
tcp::acceptor _acceptor;
ssl::context _ctx; // 单个全局的 SSL 上下文
};
#include "CServer.h"
#include <iostream>
#include "HttpsConnection.h"
#include "AsioIOServicePool.h"
#include <openssl/ssl.h>
CServer::CServer(boost::asio::io_context& ioc) : _ctx(ssl::context::tlsv12),
_acceptor(ioc, tcp::endpoint(tcp::v4(), 443))
{
// 加载证书和私钥
_ctx.use_certificate_chain_file("cert.pem");
_ctx.use_private_key_file("key.pem", ssl::context::pem);
// 启用会话缓存和其他选项
_ctx.set_options(ssl::context::default_workarounds |
ssl::context::no_sslv2 |
ssl::context::single_dh_use);
// 使用 OpenSSL API 设置会话缓存模式为同时启用客户端和服务器端缓存
SSL_CTX* ctx = _ctx.native_handle();
SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_BOTH);
// 设置密码套件以优先使用 ECDHE
SSL_CTX_set_cipher_list(ctx, "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384");
// 设置验证模式为单向 TLS(仅验证客户端提供的证书,但不要求提供)
_ctx.set_verify_mode(ssl::verify_none);
}
void CServer::Start()
{
auto self = shared_from_this();
auto& io_context = AsioIOServicePool::GetInstance()->GetIOService();
std::shared_ptr<HttpsConnection> new_con = std::make_shared<HttpsConnection>(io_context, _ctx);
_acceptor.async_accept(new_con->GetSocket().lowest_layer(), [self, new_con](beast::error_code ec)
{
try {
//出错则放弃这个连接,继续监听新链接
if (ec)
{
self->Start();
return;
}
// 完成 SSL 握手
new_con->GetSocket().async_handshake(ssl::stream_base::server, [new_con, self](beast::error_code ec) {
if (ec) {
std::cerr << "Handshake failed: " << ec.message() << "\n";
self->Start();
return;
}
// 处理新连接,启动 HttpsConnection 类管理新连接
new_con->Start();
//继续监听
self->Start();
});
}
catch (std::exception& exp) {
std::cout << "exception is " << exp.what() << std::endl;
self->Start();
} });
}
其他的大同小异,目前https跑通了,逻辑层还待完善,
补充一下跑https过程遇到的问题:
1.证书问题
https需要公钥和私钥,生成自签名证书时,确保你的证书包含正确的Common Name (CN),在SAN字段中明确列出该IP地址。
就是说,如果你服务器ip地址是192.168.124.7,你使用key.pem创建cert.pem时,一定要指定ip地址
然后,在windows中把证书cert.pem注册进去
感兴趣的我会在后面放我的github链接,再次声明,我是超人强,代码全靠超,尊重原作者,也欢迎大家超我!