add old lci module

This commit is contained in:
qing committed 2026-08-02 20:15:07 +08:00
1 parent 38a6e815f0
commit 8499631091
171 files changed
+25562

No files matched your search

+417
View File
@@ -0,0 +1,417 @@
#include "net/HttpServer.hpp"
#include <boost/algorithm/string.hpp>
#include <fstream>
#include <sstream>
namespace pmc {
namespace net {
// Session类 - 处理单个HTTP连接
class HttpServer::Session : public std::enable_shared_from_this<Session> {
public:
Session(tcp::socket&& socket, HttpServer* server)
: socket_(std::move(socket)), server_(server) {
}
void run() {
doRead();
}
private:
void doRead() {
auto self = shared_from_this();
http::async_read(socket_, buffer_, request_,
[this, self](beast::error_code ec, std::size_t) {
if (!ec) {
processRequest();
} else if (ec != http::error::end_of_stream) {
// 错误处理
}
});
}
void processRequest() {
try {
// 创建响应对象
auto response = std::make_shared<http::response<http::string_body>>(
server_->handleRequest(request_)
);
doWrite(response);
} catch (const std::exception& e) {
std::cerr << "Error processing request: " << e.what() << std::endl;
// 发送错误响应
auto error_response = std::make_shared<http::response<http::string_body>>();
error_response->version(request_.version());
error_response->result(http::status::internal_server_error);
error_response->set(http::field::content_type, "text/plain");
error_response->body() = "500 Internal Server Error\n";
error_response->prepare_payload();
doWrite(error_response);
}
}
void doWrite(std::shared_ptr<http::response<http::string_body>> response) {
auto self = shared_from_this();
http::async_write(socket_, *response,
[this, self, response](beast::error_code ec, std::size_t) {
if (!ec) {
// 检查是否需要保持连接
if (!response->keep_alive()) {
socket_.shutdown(tcp::socket::shutdown_send, ec);
}
}
// 继续读取下一个请求
doRead();
});
}
tcp::socket socket_;
beast::flat_buffer buffer_;
http::request<http::string_body> request_;
HttpServer* server_;
};
// Listener类 - 接受新连接
class HttpServer::Listener : public std::enable_shared_from_this<Listener> {
public:
Listener(asio::io_context& ioc, tcp::endpoint endpoint, HttpServer* server)
: ioc_(ioc), acceptor_(ioc), server_(server) {
beast::error_code ec;
acceptor_.open(endpoint.protocol(), ec);
if (!ec) {
acceptor_.set_option(asio::socket_base::reuse_address(true), ec);
acceptor_.bind(endpoint, ec);
if (!ec) {
acceptor_.listen(asio::socket_base::max_listen_connections, ec);
}
}
if (ec) {
std::cerr << "Listener error: " << ec.message() << std::endl;
}
}
void run() {
if (acceptor_.is_open()) {
doAccept();
}
}
private:
void doAccept() {
acceptor_.async_accept(
[this, self = shared_from_this()](beast::error_code ec, tcp::socket socket) {
if (!ec) {
std::make_shared<Session>(std::move(socket), server_)->run();
}
doAccept();
});
}
asio::io_context& ioc_;
tcp::acceptor acceptor_;
HttpServer* server_;
};
// HttpServer实现 - 新构造函数(监听所有接口)
HttpServer::HttpServer(unsigned short port, unsigned int threads)
: address_("0.0.0.0"), port_(port), threads_(threads),
ioc_(std::make_unique<asio::io_context>(threads)) {
if (!ioc_) {
throw std::runtime_error("Failed to create io_context");
}
}
// HttpServer实现 - 新构造函数(指定监听地址)
HttpServer::HttpServer(const std::string& address, unsigned short port, unsigned int threads)
: address_(address), port_(port), threads_(threads),
ioc_(std::make_unique<asio::io_context>(threads)) {
if (!ioc_) {
throw std::runtime_error("Failed to create io_context");
}
}
HttpServer::~HttpServer() {
stop();
}
void HttpServer::start() {
if (running_) {
return;
}
try {
// 创建端点(使用指定的地址和端口)
asio::ip::address ip_address = asio::ip::make_address(address_);
tcp::endpoint endpoint(ip_address, port_);
// 创建监听器
listener_ = std::make_shared<Listener>(*ioc_, endpoint, this);
if (!listener_) {
throw std::runtime_error("Failed to create listener");
}
listener_->run();
running_ = true;
// 启动工作线程
for (unsigned int i = 0; i < threads_; ++i) {
worker_threads_.emplace_back([this]() {
try {
ioc_->run();
} catch (const std::exception& e) {
std::cerr << "Thread exception: " << e.what() << std::endl;
}
});
}
std::cout << "HTTP Server started on " << address_ << ":" << port_
<< " with " << threads_ << " threads" << std::endl;
} catch (const std::exception& e) {
std::cerr << "Failed to start server: " << e.what() << std::endl;
running_ = false;
}
}
void HttpServer::stop() {
if (!running_) {
return;
}
running_ = false;
if (ioc_) {
ioc_->stop();
}
for (auto& thread : worker_threads_) {
if (thread.joinable()) {
thread.join();
}
}
worker_threads_.clear();
std::cout << "HTTP Server stopped" << std::endl;
}
void HttpServer::get(const std::string& path, HttpRequestHandler handler) {
get_handlers_[path] = handler;
}
void HttpServer::post(const std::string& path, HttpRequestHandler handler) {
post_handlers_[path] = handler;
}
void HttpServer::put(const std::string& path, HttpRequestHandler handler) {
put_handlers_[path] = handler;
}
void HttpServer::del(const std::string& path, HttpRequestHandler handler) {
delete_handlers_[path] = handler;
}
void HttpServer::use(MiddlewareHandler middleware) {
middlewares_.push_back(middleware);
}
void HttpServer::setStaticDirectory(const std::string& path) {
static_directory_ = path;
}
bool HttpServer::isRunning() const {
return running_;
}
unsigned short HttpServer::getPort() const {
return port_;
}
std::string HttpServer::getAddress() const {
return address_;
}
void HttpServer::run() {
start();
if (ioc_) {
ioc_->run();
}
}
void HttpServer::showStatus() const {
std::cout << "=== HTTP Server Status ===" << std::endl;
std::cout << "Address: " << address_ << std::endl;
std::cout << "Port: " << port_ << std::endl;
std::cout << "Threads: " << threads_ << std::endl;
std::cout << "Running: " << (running_ ? "Yes" : "No") << std::endl;
std::cout << "Static Directory: " << (static_directory_.empty() ? "Not set" : static_directory_) << std::endl;
std::cout << "Registered Routes:" << std::endl;
std::cout << " GET: " << get_handlers_.size() << " handler(s)" << std::endl;
for (const auto& [path, _] : get_handlers_) {
std::cout << " GET " << path << std::endl;
}
std::cout << " POST: " << post_handlers_.size() << " handler(s)" << std::endl;
for (const auto& [path, _] : post_handlers_) {
std::cout << " POST " << path << std::endl;
}
std::cout << " PUT: " << put_handlers_.size() << " handler(s)" << std::endl;
for (const auto& [path, _] : put_handlers_) {
std::cout << " PUT " << path << std::endl;
}
std::cout << " DELETE: " << delete_handlers_.size() << " handler(s)" << std::endl;
for (const auto& [path, _] : delete_handlers_) {
std::cout << " DELETE " << path << std::endl;
}
std::cout << "Middlewares: " << middlewares_.size() << std::endl;
std::cout << "==========================" << std::endl;
}
std::unordered_map<std::string, std::string> HttpServer::parseQueryParams(const std::string& query) {
std::unordered_map<std::string, std::string> params;
if (query.empty()) {
return params;
}
std::vector<std::string> pairs;
boost::split(pairs, query, boost::is_any_of("&"));
for (const auto& pair : pairs) {
std::vector<std::string> key_value;
boost::split(key_value, pair, boost::is_any_of("="));
if (key_value.size() == 2) {
params[key_value[0]] = key_value[1];
} else if (key_value.size() == 1) {
params[key_value[0]] = "";
}
}
return params;
}
http::response<http::string_body> HttpServer::handleRequest(const http::request<http::string_body>& req) {
// 将 target() 转换为字符串
std::string target_path = std::string(req.target().data(), req.target().size());
std::string query_string;
// 分离路径和查询参数
auto query_pos = target_path.find('?');
if (query_pos != std::string::npos) {
query_string = target_path.substr(query_pos + 1);
target_path = target_path.substr(0, query_pos);
}
// 解析查询参数
auto query_params = parseQueryParams(query_string);
// 准备响应对象
http::response<http::string_body> response;
response.version(req.version());
response.set(http::field::server, "Boost.Beast HttpServer");
response.set(http::field::content_type, "text/plain");
response.keep_alive(req.keep_alive());
// 执行中间件链
bool middleware_handled = false;
for (const auto& middleware : middlewares_) {
if (!middleware(req, response, query_params)) {
middleware_handled = true;
break;
}
}
// 如果中间件处理了请求,直接返回
if (middleware_handled) {
response.prepare_payload();
return response;
}
// 查找路由处理器
HttpRequestHandler handler = nullptr;
switch (req.method()) {
case http::verb::get: {
auto it = get_handlers_.find(target_path);
if (it != get_handlers_.end()) {
handler = it->second;
}
break;
}
case http::verb::post: {
auto it = post_handlers_.find(target_path);
if (it != post_handlers_.end()) {
handler = it->second;
}
break;
}
case http::verb::put: {
auto it = put_handlers_.find(target_path);
if (it != put_handlers_.end()) {
handler = it->second;
}
break;
}
case http::verb::delete_: {
auto it = delete_handlers_.find(target_path);
if (it != delete_handlers_.end()) {
handler = it->second;
}
break;
}
default:
break;
}
// 处理静态文件
if (!handler && !static_directory_.empty()) {
std::string file_path = static_directory_ + target_path;
std::ifstream file(file_path, std::ios::binary);
if (file.is_open()) {
std::stringstream buffer;
buffer << file.rdbuf();
response.result(http::status::ok);
response.body() = buffer.str();
// 根据文件扩展名设置Content-Type
if (boost::algorithm::ends_with(file_path, ".html")) {
response.set(http::field::content_type, "text/html");
} else if (boost::algorithm::ends_with(file_path, ".css")) {
response.set(http::field::content_type, "text/css");
} else if (boost::algorithm::ends_with(file_path, ".js")) {
response.set(http::field::content_type, "application/javascript");
} else if (boost::algorithm::ends_with(file_path, ".json")) {
response.set(http::field::content_type, "application/json");
} else if (boost::algorithm::ends_with(file_path, ".png")) {
response.set(http::field::content_type, "image/png");
} else if (boost::algorithm::ends_with(file_path, ".jpg") ||
boost::algorithm::ends_with(file_path, ".jpeg")) {
response.set(http::field::content_type, "image/jpeg");
}
response.prepare_payload();
return response;
}
}
// 如果找到处理器,调用它
if (handler) {
try {
return handler(req, query_params);
} catch (const std::exception& e) {
std::cerr << "Handler exception: " << e.what() << std::endl;
response.result(http::status::internal_server_error);
response.body() = "500 Internal Server Error\n";
response.prepare_payload();
return response;
}
}
// 404 Not Found
response.result(http::status::not_found);
response.body() = "404 Not Found\n";
response.prepare_payload();
return response;
}
} // namespace net
} // namespace pmc
+386
View File
@@ -0,0 +1,386 @@
#include "net/WebSocketClient.hpp"
#include <boost/beast/core.hpp>
#include <boost/beast/websocket.hpp>
#include <boost/asio/strand.hpp>
#include <boost/asio/dispatch.hpp>
#include <boost/asio/post.hpp>
#include <boost/asio/connect.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <iostream>
#include <thread>
#include <chrono>
#include <mutex>
#include <queue>
#include <condition_variable>
#include <atomic>
namespace pmc {
namespace net {
namespace beast = boost::beast;
namespace asio = boost::asio;
using tcp = asio::ip::tcp;
// 内部实现结构体
struct WebSocketClient::Impl {
// 配置参数
std::string host;
std::string port;
std::string path;
// Boost 对象
asio::io_context ioc;
tcp::resolver resolver;
beast::websocket::stream<beast::tcp_stream> ws;
// 线程管理
std::thread io_thread;
std::atomic<bool> running{false};
// 回调函数
WebSocketClientBase::MessageCallback message_callback;
WebSocketClientBase::ErrorCallback error_callback;
WebSocketClientBase::ConnectCallback connect_callback;
WebSocketClientBase::CloseCallback close_callback;
// 消息队列
std::queue<std::string> send_queue;
std::mutex queue_mutex;
std::condition_variable queue_cv;
std::atomic<bool> sending{false};
// 状态标志
std::atomic<bool> connected{false};
std::atomic<bool> connecting{false};
std::atomic<bool> stopping{false};
// 配置选项
bool auto_reconnect{false};
int reconnect_interval_ms{3000};
int heartbeat_interval_ms{30000};
// 构造函数
Impl(const std::string& host, const std::string& port, const std::string& path)
: host(host)
, port(port)
, path(path)
, resolver(asio::make_strand(ioc))
, ws(asio::make_strand(ioc))
{
}
// 析构函数
~Impl() {
stop();
}
// 启动IO线程
void start() {
if (!running) {
running = true;
io_thread = std::thread([this]() {
while (running) {
try {
ioc.run();
break;
} catch (const std::exception& e) {
std::cerr << "IO上下文异常: " << e.what() << std::endl;
}
}
});
}
}
// 停止IO线程
void stop() {
running = false;
stopping = true;
if (io_thread.joinable()) {
ioc.stop();
io_thread.join();
}
}
// 执行连接
void doConnect() {
if (connecting || connected) {
return;
}
connecting = true;
connected = false;
// 解析主机名
resolver.async_resolve(host, port,
[this](beast::error_code ec, tcp::resolver::results_type results) {
onResolve(ec, results);
});
}
// 解析完成回调
void onResolve(beast::error_code ec, tcp::resolver::results_type results) {
if (ec) {
onError("解析失败: " + ec.message());
return;
}
// 连接到解析出的端点
beast::get_lowest_layer(ws).async_connect(results,
[this](beast::error_code ec, tcp::resolver::results_type::endpoint_type) {
onConnect(ec);
});
}
// 连接完成回调
void onConnect(beast::error_code ec) {
if (ec) {
onError("连接失败: " + ec.message());
return;
}
// 设置WebSocket选项
ws.set_option(beast::websocket::stream_base::timeout::suggested(
beast::role_type::client));
// 执行WebSocket握手
ws.async_handshake(host, path,
[this](beast::error_code ec) {
onHandshake(ec);
});
}
// 握手完成回调
void onHandshake(beast::error_code ec) {
connecting = false;
if (ec) {
onError("握手失败: " + ec.message());
return;
}
connected = true;
// 调用连接成功回调
if (connect_callback) {
connect_callback(true);
}
// 开始读取消息
doRead();
// 开始发送队列中的消息
doSend();
}
// 读取消息
void doRead() {
if (!connected) {
return;
}
ws.async_read(buffer,
[this](beast::error_code ec, std::size_t bytes_transferred) {
onRead(ec, bytes_transferred);
});
}
// 读取完成回调
void onRead(beast::error_code ec, std::size_t bytes_transferred) {
if (ec) {
if (ec == beast::websocket::error::closed) {
// 正常关闭
onClose();
} else {
onError("读取失败: " + ec.message());
}
return;
}
// 处理接收到的消息
std::string message = beast::buffers_to_string(buffer.data());
buffer.consume(bytes_transferred);
if (message_callback) {
message_callback(message);
}
// 继续读取
doRead();
}
// 发送消息
void doSend() {
if (!connected || sending) {
return;
}
std::unique_lock<std::mutex> lock(queue_mutex);
if (send_queue.empty()) {
return;
}
sending = true;
std::string message = std::move(send_queue.front());
send_queue.pop();
lock.unlock();
ws.async_write(asio::buffer(message),
[this](beast::error_code ec, std::size_t bytes_transferred) {
onWrite(ec, bytes_transferred);
});
}
// 写入完成回调
void onWrite(beast::error_code ec, std::size_t bytes_transferred) {
sending = false;
if (ec) {
onError("写入失败: " + ec.message());
return;
}
// 继续发送队列中的消息
doSend();
}
// 错误处理
void onError(const std::string& error) {
connecting = false;
connected = false;
std::cerr << "WebSocket错误: " << error << std::endl;
if (error_callback) {
error_callback(error);
}
// 自动重连
if (auto_reconnect && !stopping) {
std::this_thread::sleep_for(std::chrono::milliseconds(reconnect_interval_ms));
asio::post(ioc, [this]() { doConnect(); });
}
}
// 连接关闭
void onClose() {
connected = false;
if (close_callback) {
close_callback();
}
// 自动重连
if (auto_reconnect && !stopping) {
std::this_thread::sleep_for(std::chrono::milliseconds(reconnect_interval_ms));
asio::post(ioc, [this]() { doConnect(); });
}
}
private:
beast::flat_buffer buffer;
};
// WebSocketClient 实现
WebSocketClient::WebSocketClient(const std::string& host, const std::string& port, const std::string& path)
: impl_(std::make_unique<Impl>(host, port, path))
{
impl_->start();
}
WebSocketClient::~WebSocketClient() {
disconnect();
}
void WebSocketClient::setMessageCallback(MessageCallback callback) {
impl_->message_callback = callback;
}
void WebSocketClient::setErrorCallback(ErrorCallback callback) {
impl_->error_callback = callback;
}
void WebSocketClient::setConnectCallback(ConnectCallback callback) {
impl_->connect_callback = callback;
}
void WebSocketClient::setCloseCallback(CloseCallback callback) {
impl_->close_callback = callback;
}
bool WebSocketClient::connect(int timeout_ms) {
if (impl_->connecting || impl_->connected) {
return false;
}
impl_->doConnect();
return true;
}
void WebSocketClient::disconnect() {
if (!impl_->connected && !impl_->connecting) {
return;
}
impl_->stopping = true;
asio::post(impl_->ioc, [this]() {
beast::error_code ec;
impl_->ws.close(beast::websocket::close_code::normal, ec);
impl_->connected = false;
impl_->connecting = false;
});
}
bool WebSocketClient::isConnected() const {
return impl_->connected;
}
bool WebSocketClient::send(const std::string& message) {
if (!impl_->connected) {
return false;
}
{
std::lock_guard<std::mutex> lock(impl_->queue_mutex);
impl_->send_queue.push(message);
}
impl_->queue_cv.notify_one();
asio::post(impl_->ioc, [this]() { impl_->doSend(); });
return true;
}
bool WebSocketClient::sendBinary(const void* data, size_t size) {
if (!impl_->connected) {
return false;
}
std::string message(static_cast<const char*>(data), size);
return send(message);
}
void WebSocketClient::setAutoReconnect(bool enable, int interval_ms) {
impl_->auto_reconnect = enable;
impl_->reconnect_interval_ms = interval_ms;
}
void WebSocketClient::setHeartbeatInterval(int interval_ms) {
impl_->heartbeat_interval_ms = interval_ms;
}
std::string WebSocketClient::getHost() const {
return impl_->host;
}
std::string WebSocketClient::getPort() const {
return impl_->port;
}
std::string WebSocketClient::getPath() const {
return impl_->path;
}
} // namespace net
} // namespace pmc
+620
View File
@@ -0,0 +1,620 @@
#include "net/WebSocketClientSSL.hpp"
#include <boost/beast/websocket.hpp>
#include <boost/beast/websocket/ssl.hpp>
#include <boost/asio/ssl.hpp>
#include <boost/asio/strand.hpp>
#include <boost/asio/dispatch.hpp>
#include <boost/asio/post.hpp>
#include <iostream>
#include <thread>
#include <chrono>
#include <mutex>
#include <queue>
#include <condition_variable>
#include <atomic>
namespace pmc {
namespace net {
namespace beast = boost::beast;
namespace asio = boost::asio;
namespace ssl = asio::ssl;
using tcp = asio::ip::tcp;
using websocket = beast::websocket::stream<beast::ssl_stream<beast::tcp_stream>>;
// 内部实现结构体
struct WebSocketClientSSL::Impl {
// 配置参数
std::string host;
std::string port;
std::string path;
// SSL 配置
bool verify_certificate{true};
std::string cert_file;
std::string key_file;
std::string ca_file;
std::string ciphers;
std::string sni_hostname;
long ssl_options{ssl::context::default_workarounds |
ssl::context::no_sslv2 |
ssl::context::no_sslv3};
// Boost 对象
asio::io_context ioc;
ssl::context ssl_ctx{ssl::context::tlsv12_client};
tcp::resolver resolver;
websocket ws;
asio::steady_timer heartbeat_timer;
asio::steady_timer reconnect_timer;
// 回调函数
MessageCallback message_callback;
ErrorCallback error_callback;
ConnectCallback connect_callback;
CloseCallback close_callback;
// 状态管理
std::atomic<bool> connected{false};
std::atomic<bool> connecting{false};
std::atomic<bool> stopping{false};
std::atomic<bool> auto_reconnect{false};
std::atomic<int> reconnect_interval{3000};
std::atomic<int> heartbeat_interval{30000};
// 消息队列
struct QueuedMessage {
std::string data;
bool is_binary;
};
std::queue<QueuedMessage> send_queue;
std::mutex queue_mutex;
std::condition_variable queue_cv;
std::atomic<bool> writing{false};
// 接收缓冲区
beast::flat_buffer read_buffer;
// 线程
std::thread io_thread;
Impl(const std::string& h, const std::string& p, const std::string& pt)
: host(h)
, port(p)
, path(pt)
, resolver(asio::make_strand(ioc))
, ws(asio::make_strand(ioc), ssl_ctx)
, heartbeat_timer(asio::make_strand(ioc))
, reconnect_timer(asio::make_strand(ioc)) {
// 配置 SSL 上下文
configureSSLContext();
}
~Impl() {
stopping = true;
if (io_thread.joinable()) {
ioc.stop();
io_thread.join();
}
}
void configureSSLContext() {
try {
// 设置 SSL 选项
ssl_ctx.set_options(ssl_options);
// 设置密码列表
if (!ciphers.empty()) {
SSL_CTX_set_cipher_list(ssl_ctx.native_handle(), ciphers.c_str());
}
// 加载证书文件
if (!cert_file.empty()) {
ssl_ctx.use_certificate_file(cert_file, ssl::context::pem);
}
// 加载私钥文件
if (!key_file.empty()) {
ssl_ctx.use_private_key_file(key_file, ssl::context::pem);
}
// 加载 CA 证书
if (!ca_file.empty()) {
ssl_ctx.load_verify_file(ca_file);
} else {
// 使用系统默认的 CA 证书
ssl_ctx.set_default_verify_paths();
}
// 设置验证模式
if (verify_certificate) {
ssl_ctx.set_verify_mode(ssl::verify_peer | ssl::verify_fail_if_no_peer_cert);
} else {
ssl_ctx.set_verify_mode(ssl::verify_none);
}
} catch (const std::exception& e) {
std::cerr << "SSL 上下文配置错误: " << e.what() << std::endl;
}
}
void setSNI() {
// 设置 SNI 主机名
if (!sni_hostname.empty()) {
if (!SSL_set_tlsext_host_name(ws.next_layer().native_handle(), sni_hostname.c_str())) {
beast::error_code ec{static_cast<int>(::ERR_get_error()), asio::error::get_ssl_category()};
throw beast::system_error{ec};
}
} else if (!host.empty()) {
// 使用主机名作为 SNI
if (!SSL_set_tlsext_host_name(ws.next_layer().native_handle(), host.c_str())) {
beast::error_code ec{static_cast<int>(::ERR_get_error()), asio::error::get_ssl_category()};
throw beast::system_error{ec};
}
}
}
};
// 构造函数
WebSocketClientSSL::WebSocketClientSSL(const std::string& host, const std::string& port, const std::string& path)
: impl_(std::make_unique<Impl>(host, port, path)) {
}
// 析构函数
WebSocketClientSSL::~WebSocketClientSSL() {
disconnect();
}
// 回调函数设置
void WebSocketClientSSL::setMessageCallback(MessageCallback callback) {
impl_->message_callback = std::move(callback);
}
void WebSocketClientSSL::setErrorCallback(ErrorCallback callback) {
impl_->error_callback = std::move(callback);
}
void WebSocketClientSSL::setConnectCallback(ConnectCallback callback) {
impl_->connect_callback = std::move(callback);
}
void WebSocketClientSSL::setCloseCallback(CloseCallback callback) {
impl_->close_callback = std::move(callback);
}
// SSL/TLS 配置
void WebSocketClientSSL::setVerifyCertificate(bool verify) {
impl_->verify_certificate = verify;
impl_->configureSSLContext();
}
void WebSocketClientSSL::setCertificateFile(const std::string& cert_file) {
impl_->cert_file = cert_file;
impl_->configureSSLContext();
}
void WebSocketClientSSL::setPrivateKeyFile(const std::string& key_file) {
impl_->key_file = key_file;
impl_->configureSSLContext();
}
void WebSocketClientSSL::setCertificateAuthorityFile(const std::string& ca_file) {
impl_->ca_file = ca_file;
impl_->configureSSLContext();
}
void WebSocketClientSSL::setSSLContextOptions(long options) {
impl_->ssl_options = options;
impl_->configureSSLContext();
}
void WebSocketClientSSL::setCiphers(const std::string& ciphers) {
impl_->ciphers = ciphers;
impl_->configureSSLContext();
}
void WebSocketClientSSL::setSNIHostname(const std::string& sni_hostname) {
impl_->sni_hostname = sni_hostname;
}
// 连接函数
bool WebSocketClientSSL::connect(int timeout_ms) {
if (impl_->connecting || impl_->connected) {
return false;
}
impl_->connecting = true;
impl_->stopping = false;
// 启动 IO 线程
if (!impl_->io_thread.joinable()) {
impl_->io_thread = std::thread([this]() {
try {
impl_->ioc.run();
} catch (const std::exception& e) {
if (impl_->error_callback) {
impl_->error_callback(std::string("IO context error: ") + e.what());
}
}
});
}
// 异步连接
asio::post(impl_->ioc, [this]() {
try {
impl_->setSNI();
doConnect();
} catch (const std::exception& e) {
handleError("SSL setup failed", beast::error_code{}, e.what());
}
});
// 等待连接完成
auto start = std::chrono::steady_clock::now();
while (impl_->connecting && !impl_->stopping) {
if (timeout_ms > 0) {
auto now = std::chrono::steady_clock::now();
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start).count();
if (elapsed > timeout_ms) {
impl_->connecting = false;
if (impl_->error_callback) {
impl_->error_callback("Connection timeout");
}
return false;
}
}
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
return impl_->connected;
}
// 私有连接方法
void WebSocketClientSSL::doConnect() {
impl_->resolver.async_resolve(
impl_->host,
impl_->port,
[this](beast::error_code ec, tcp::resolver::results_type results) {
onResolve(ec, std::move(results));
});
}
void WebSocketClientSSL::onResolve(beast::error_code ec, tcp::resolver::results_type results) {
if (ec) {
handleError("Resolve failed", ec);
return;
}
// 设置超时
beast::get_lowest_layer(impl_->ws).expires_after(std::chrono::seconds(30));
// 异步连接
beast::get_lowest_layer(impl_->ws).async_connect(
results,
[this](beast::error_code ec, tcp::resolver::results_type::endpoint_type endpoint) {
onConnect(ec, endpoint);
});
}
void WebSocketClientSSL::onConnect(beast::error_code ec, tcp::resolver::results_type::endpoint_type endpoint) {
if (ec) {
handleError("Connect failed", ec);
return;
}
// SSL 握手
impl_->ws.next_layer().async_handshake(
ssl::stream_base::client,
[this](beast::error_code ec) {
onSSLHandshake(ec);
});
}
void WebSocketClientSSL::onSSLHandshake(beast::error_code ec) {
if (ec) {
handleError("SSL handshake failed", ec);
return;
}
// 关闭 TCP 超时设置
beast::get_lowest_layer(impl_->ws).expires_never();
// 设置 WebSocket 选项
impl_->ws.set_option(websocket::stream_base::timeout::suggested(beast::role_type::client));
impl_->ws.read_message_max(10 * 1024 * 1024); // 10MB 限制
// WebSocket 握手
impl_->ws.async_handshake(
impl_->host + ":" + impl_->port,
impl_->path,
[this](beast::error_code ec) {
onHandshake(ec);
});
}
void WebSocketClientSSL::onHandshake(beast::error_code ec) {
impl_->connecting = false;
if (ec) {
handleError("WebSocket handshake failed", ec);
return;
}
impl_->connected = true;
// 开始读取消息
doRead();
// 开始心跳
if (impl_->heartbeat_interval > 0) {
startHeartbeat();
}
// 通知连接成功
if (impl_->connect_callback) {
impl_->connect_callback(true);
}
// 检查是否有待发送的消息
if (hasMessages()) {
doWrite();
}
}
// 断开连接
void WebSocketClientSSL::disconnect() {
impl_->stopping = true;
impl_->auto_reconnect = false;
if (impl_->connected) {
asio::post(impl_->ioc, [this]() {
beast::error_code ec;
impl_->ws.close(websocket::close_code::normal, ec);
if (ec && impl_->error_callback) {
impl_->error_callback("Error closing WebSocket: " + ec.message());
}
impl_->connected = false;
stopHeartbeat();
if (impl_->close_callback) {
impl_->close_callback();
}
});
}
}
// 发送消息
bool WebSocketClientSSL::send(const std::string& message) {
return pushMessage(message, false);
}
bool WebSocketClientSSL::sendBinary(const void* data, size_t size) {
std::string message(static_cast<const char*>(data), size);
return pushMessage(message, true);
}
// 状态检查
bool WebSocketClientSSL::isConnected() const {
return impl_->connected;
}
std::string WebSocketClientSSL::getHost() const {
return impl_->host;
}
std::string WebSocketClientSSL::getPort() const {
return impl_->port;
}
std::string WebSocketClientSSL::getPath() const {
return impl_->path;
}
// 自动重连和心跳设置
void WebSocketClientSSL::setAutoReconnect(bool enable, int interval_ms) {
impl_->auto_reconnect = enable;
impl_->reconnect_interval = interval_ms;
}
void WebSocketClientSSL::setHeartbeatInterval(int interval_ms) {
impl_->heartbeat_interval = interval_ms;
if (impl_->connected && interval_ms > 0) {
startHeartbeat();
}
}
// ============ 私有方法实现 ============
void WebSocketClientSSL::doRead() {
impl_->ws.async_read(
impl_->read_buffer,
[this](beast::error_code ec, std::size_t bytes_transferred) {
onRead(ec, bytes_transferred);
});
}
void WebSocketClientSSL::onRead(beast::error_code ec, std::size_t bytes_transferred) {
if (ec) {
if (ec == websocket::error::closed) {
// 正常关闭
impl_->connected = false;
stopHeartbeat();
if (impl_->close_callback) {
impl_->close_callback();
}
// 自动重连
if (impl_->auto_reconnect && !impl_->stopping) {
impl_->reconnect_timer.expires_after(std::chrono::milliseconds(impl_->reconnect_interval));
impl_->reconnect_timer.async_wait(
[this](beast::error_code ec) {
if (!ec && !impl_->stopping) {
doConnect();
}
});
}
} else {
handleError("Read failed", ec);
}
return;
}
// 处理接收到的消息
if (impl_->message_callback) {
std::string message = beast::buffers_to_string(impl_->read_buffer.data());
impl_->message_callback(message);
}
// 清空缓冲区并继续读取
impl_->read_buffer.consume(bytes_transferred);
doRead();
}
void WebSocketClientSSL::doWrite() {
if (impl_->writing || !hasMessages()) {
return;
}
impl_->writing = true;
auto message = popMessage();
impl_->ws.async_write(
asio::buffer(message.first),
[this, is_binary = message.second](beast::error_code ec, std::size_t bytes_transferred) {
impl_->writing = false;
onWrite(ec, bytes_transferred);
// 如果是二进制消息,设置二进制模式
if (!ec && is_binary) {
impl_->ws.binary(true);
} else if (!ec) {
impl_->ws.text(true);
}
});
}
void WebSocketClientSSL::onWrite(beast::error_code ec, std::size_t bytes_transferred) {
(void)bytes_transferred; // 消除未使用参数警告
if (ec) {
handleError("Write failed", ec);
return;
}
// 继续发送队列中的下一条消息
if (hasMessages()) {
doWrite();
}
}
void WebSocketClientSSL::handleError(const std::string& message, beast::error_code ec, const std::string& extra) {
std::string error_msg = message;
if (ec) {
error_msg += ": " + ec.message();
}
if (!extra.empty()) {
error_msg += " (" + extra + ")";
}
impl_->connecting = false;
impl_->connected = false;
stopHeartbeat();
if (impl_->error_callback) {
impl_->error_callback(error_msg);
}
if (impl_->connect_callback) {
impl_->connect_callback(false);
}
// 自动重连
if (impl_->auto_reconnect && !impl_->stopping) {
impl_->reconnect_timer.expires_after(std::chrono::milliseconds(impl_->reconnect_interval));
impl_->reconnect_timer.async_wait(
[this](beast::error_code ec) {
if (!ec && !impl_->stopping) {
doConnect();
}
});
}
}
void WebSocketClientSSL::startHeartbeat() {
if (impl_->heartbeat_interval <= 0) {
return;
}
impl_->heartbeat_timer.expires_after(std::chrono::milliseconds(impl_->heartbeat_interval));
impl_->heartbeat_timer.async_wait(
[this](beast::error_code ec) {
heartbeatTimerCallback(ec);
});
}
void WebSocketClientSSL::stopHeartbeat() {
beast::error_code ec;
impl_->heartbeat_timer.cancel(ec);
}
void WebSocketClientSSL::heartbeatTimerCallback(beast::error_code ec) {
if (ec || impl_->stopping || !impl_->connected) {
return;
}
// 发送 ping 消息
beast::error_code ping_ec;
impl_->ws.ping(beast::websocket::ping_data{}, ping_ec);
if (ping_ec) {
handleError("Heartbeat ping failed", ping_ec);
return;
}
// 重新设置定时器
startHeartbeat();
}
// 线程安全队列操作
bool WebSocketClientSSL::pushMessage(const std::string& message, bool is_binary) {
if (!impl_->connected || impl_->stopping) {
return false;
}
{
std::lock_guard<std::mutex> lock(impl_->queue_mutex);
impl_->send_queue.push({message, is_binary});
}
impl_->queue_cv.notify_one();
// 触发写入
asio::post(impl_->ioc, [this]() {
if (!impl_->writing && hasMessages()) {
doWrite();
}
});
return true;
}
std::pair<std::string, bool> WebSocketClientSSL::popMessage() {
std::lock_guard<std::mutex> lock(impl_->queue_mutex);
if (impl_->send_queue.empty()) {
return {"", false};
}
auto msg = std::move(impl_->send_queue.front());
impl_->send_queue.pop();
return {std::move(msg.data), msg.is_binary};
}
bool WebSocketClientSSL::hasMessages() const {
std::lock_guard<std::mutex> lock(impl_->queue_mutex);
return !impl_->send_queue.empty();
}
} // namespace net
} // namespace pmc
+382
View File
@@ -0,0 +1,382 @@
// WebSocketServer.cpp - WebSocket服务器实现
// 基于Boost.Beast和Boost.Asio
#include "net/WebSocketServer.hpp"
#include <iostream>
#include <memory>
#include <string>
#include <unordered_map>
#include <mutex>
#include <deque>
#include <boost/beast/core.hpp>
#include <boost/beast/websocket.hpp>
#include <boost/asio/strand.hpp>
#include <boost/asio/ip/tcp.hpp>
namespace beast = boost::beast;
namespace websocket = beast::websocket;
namespace asio = boost::asio;
using tcp = asio::ip::tcp;
namespace pmc::net {
// 内部Session类实现
class WebSocketServer::Session : public std::enable_shared_from_this<Session> {
public:
Session(tcp::socket socket, WebSocketServer* server)
: ws_(std::move(socket))
, server_(server) {
// 获取远程地址
auto endpoint = ws_.next_layer().socket().remote_endpoint();
remoteAddress_ = endpoint.address().to_string() + ":" + std::to_string(endpoint.port());
}
void start() {
// 生成客户端ID
if (server_) {
clientId_ = server_->generateClientId();
} else {
// 如果server为空,使用默认ID
static std::atomic<uint64_t> counter{0};
clientId_ = "client_" + std::to_string(++counter);
}
// 异步接受WebSocket握手
ws_.async_accept(
beast::bind_front_handler(
&Session::onAccept,
shared_from_this()));
}
void send(const std::string& message) {
// 将消息添加到发送队列
bool write_in_progress = !sendQueue_.empty();
sendQueue_.push_back(message);
if (!write_in_progress) {
// 开始异步写入
doWrite();
}
}
const std::string& getClientId() const { return clientId_; }
const std::string& getRemoteAddress() const { return remoteAddress_; }
private:
websocket::stream<beast::tcp_stream> ws_;
beast::flat_buffer buffer_;
std::deque<std::string> sendQueue_;
WebSocketServer* server_;
std::string clientId_;
std::string remoteAddress_;
void onAccept(beast::error_code ec) {
if (ec) {
std::cerr << "WebSocket accept error: " << ec.message() << std::endl;
return;
}
// 添加到服务器客户端列表
if (server_) {
server_->addClient(clientId_, shared_from_this());
// 调用连接处理器
if (server_->connectHandler_) {
server_->connectHandler_(clientId_, remoteAddress_);
}
}
// 开始读取消息
doRead();
}
void doRead() {
// 异步读取消息
ws_.async_read(
buffer_,
beast::bind_front_handler(
&Session::onRead,
shared_from_this()));
}
void onRead(beast::error_code ec, std::size_t bytes_transferred) {
if (ec == websocket::error::closed) {
// 正常关闭
if (server_) {
server_->removeClient(clientId_);
if (server_->disconnectHandler_) {
server_->disconnectHandler_(clientId_);
}
}
return;
}
if (ec) {
std::cerr << "WebSocket read error: " << ec.message() << std::endl;
if (server_) {
server_->removeClient(clientId_);
if (server_->disconnectHandler_) {
server_->disconnectHandler_(clientId_);
}
}
return;
}
// 处理接收到的消息
std::string message = beast::buffers_to_string(buffer_.data());
buffer_.consume(bytes_transferred);
if (server_) {
// 查找消息处理器
for (const auto& handler : server_->messageHandlers_) {
// 这里可以添加路径匹配逻辑
handler.second(clientId_, message, [this](const std::string& response) {
send(response);
});
}
}
// 继续读取
doRead();
}
void doWrite() {
if (sendQueue_.empty()) {
return;
}
// 获取队列中的第一条消息
auto message = sendQueue_.front();
// 异步写入消息
ws_.async_write(
asio::buffer(message),
beast::bind_front_handler(
&Session::onWrite,
shared_from_this()));
}
void onWrite(beast::error_code ec, std::size_t bytes_transferred) {
if (ec) {
std::cerr << "WebSocket write error: " << ec.message() << std::endl;
if (server_) {
server_->removeClient(clientId_);
if (server_->disconnectHandler_) {
server_->disconnectHandler_(clientId_);
}
}
return;
}
// 移除已发送的消息
sendQueue_.pop_front();
// 继续发送队列中的下一条消息
if (!sendQueue_.empty()) {
doWrite();
}
}
};
// 内部Listener类实现
class WebSocketServer::Listener : public std::enable_shared_from_this<Listener> {
public:
static std::shared_ptr<Listener> create(asio::io_context& ioc, tcp::endpoint endpoint, WebSocketServer* server) {
return std::shared_ptr<Listener>(new Listener(ioc, endpoint, server));
}
void run() {
if (!acceptor_.is_open()) {
throw std::runtime_error("Acceptor is not open");
}
doAccept();
}
private:
Listener(asio::io_context& ioc, tcp::endpoint endpoint, WebSocketServer* server)
: ioc_(ioc)
, acceptor_(asio::make_strand(ioc))
, server_(server) {
beast::error_code ec;
// 打开acceptor
acceptor_.open(endpoint.protocol(), ec);
if (ec) {
std::cerr << "Failed to open acceptor: " << ec.message() << std::endl;
throw std::runtime_error("Failed to open acceptor: " + ec.message());
}
// 设置地址重用
acceptor_.set_option(asio::socket_base::reuse_address(true), ec);
if (ec) {
std::cerr << "Failed to set reuse address: " << ec.message() << std::endl;
throw std::runtime_error("Failed to set reuse address: " + ec.message());
}
// 绑定到端点
acceptor_.bind(endpoint, ec);
if (ec) {
std::cerr << "Failed to bind to endpoint: " << ec.message() << std::endl;
throw std::runtime_error("Failed to bind to endpoint: " + ec.message());
}
// 开始监听
acceptor_.listen(asio::socket_base::max_listen_connections, ec);
if (ec) {
std::cerr << "Failed to listen: " << ec.message() << std::endl;
throw std::runtime_error("Failed to listen: " + ec.message());
}
}
void doAccept() {
acceptor_.async_accept(
asio::make_strand(ioc_),
beast::bind_front_handler(
&Listener::onAccept,
shared_from_this()));
}
void onAccept(beast::error_code ec, tcp::socket socket) {
if (ec) {
std::cerr << "Accept error: " << ec.message() << std::endl;
} else {
// 创建新的Session处理连接
std::make_shared<Session>(std::move(socket), server_)->start();
}
// 继续接受新的连接
doAccept();
}
asio::io_context& ioc_;
tcp::acceptor acceptor_;
WebSocketServer* server_;
};
WebSocketServer::WebSocketServer(unsigned short port, unsigned int threads)
: port_(port)
, threads_(threads)
, impl_(std::make_unique<Impl>()) {}
WebSocketServer::~WebSocketServer() {
stop();
}
void WebSocketServer::start() {
if (running_) {
return;
}
// 创建监听器
tcp::endpoint endpoint(tcp::v4(), port_);
impl_->listener_ = Listener::create(impl_->ioc_, endpoint, this);
// 启动监听器
impl_->listener_->run();
// 启动工作线程
for (unsigned int i = 0; i < threads_; ++i) {
impl_->threads_.emplace_back([this]() {
impl_->ioc_.run();
});
}
running_ = true;
std::cout << "WebSocketServer started on port " << port_ << " with " << threads_ << " threads" << std::endl;
}
void WebSocketServer::stop() {
if (!running_) {
return;
}
// 停止io_context
impl_->ioc_.stop();
// 等待所有线程结束
for (auto& thread : impl_->threads_) {
if (thread.joinable()) {
thread.join();
}
}
impl_->threads_.clear();
running_ = false;
std::cout << "WebSocketServer stopped" << std::endl;
}
void WebSocketServer::onMessage(const std::string& path, WebSocketMessageHandler handler) {
messageHandlers_[path] = std::move(handler);
}
void WebSocketServer::onConnect(WebSocketConnectHandler handler) {
connectHandler_ = std::move(handler);
}
void WebSocketServer::onDisconnect(WebSocketDisconnectHandler handler) {
disconnectHandler_ = std::move(handler);
}
bool WebSocketServer::sendToClient(const std::string& clientId, const std::string& message) {
std::lock_guard<std::mutex> lock(clientsMutex_);
auto it = clients_.find(clientId);
if (it != clients_.end()) {
it->second->send(message);
return true;
}
return false;
}
size_t WebSocketServer::broadcast(const std::string& message) {
std::lock_guard<std::mutex> lock(clientsMutex_);
size_t count = 0;
for (auto& pair : clients_) {
pair.second->send(message);
count++;
}
return count;
}
void WebSocketServer::disconnectClient(const std::string& clientId) {
std::lock_guard<std::mutex> lock(clientsMutex_);
auto it = clients_.find(clientId);
if (it != clients_.end()) {
// 这里应该关闭WebSocket连接
// 目前只是从列表中移除
clients_.erase(it);
}
}
size_t WebSocketServer::getClientCount() const {
std::lock_guard<std::mutex> lock(clientsMutex_);
return clients_.size();
}
std::string WebSocketServer::generateClientId() const {
static std::atomic<uint64_t> counter{0};
return "client_" + std::to_string(++counter);
}
void WebSocketServer::addClient(const std::string& clientId, std::shared_ptr<Session> session) {
std::lock_guard<std::mutex> lock(clientsMutex_);
clients_[clientId] = session;
}
void WebSocketServer::removeClient(const std::string& clientId) {
std::lock_guard<std::mutex> lock(clientsMutex_);
clients_.erase(clientId);
}
std::shared_ptr<WebSocketServer::Session> WebSocketServer::getClient(const std::string& clientId) {
std::lock_guard<std::mutex> lock(clientsMutex_);
auto it = clients_.find(clientId);
if (it != clients_.end()) {
return it->second;
}
return nullptr;
}
} // namespace pmc::net