Boost 库实现异步高并发服务器

Boost 库实现异步高并发服务器

本工程以 Boost.Asio 网络编程为主线,涉及 Boost.Beast (HTTP/WebSocket)、Boost.UUID 等子库,根据项目工程中用到的关于 Boost 的内容系统化整理,包括:基本概念与思想、常用类及函数用法、编程范式与易错点。


一、总体概念与设计思想

1.1 从 Socket 编程到 Boost.Asio 的面向对象抽象

传统 C 语言 Socket 编程由离散的数据结构sockaddr / sockaddr_in)和一系列函数按固定次序组成:

角色 流程
服务器 ① 创建监听 socket → bind()listen()accept() 接受客户端 connect();② 由 accept() 返回值得到与客户端通信的新 socket,用 recv()/send() 通信
客户端 ① 创建通信 socket,直接 connect() 连接服务器;② 成功后用该 socket + recv()/send() 通信

要点:服务器监听 socket 需手动绑定固定 IP/端口;通信 socket 自动绑定本服务线程、本地 IP 和端口(服务器)/随机端口(客户端)。

Boost.Asio 在 Socket 编程之上用面向对象 思想做封装,并采用异步事件循环机制实现异步回调函数处理,以实现异步高并发

Socket 概念 Boost.Asio 封装
sockaddr 数据结构(IP+端口) boost::asio::ip::tcp::endpoint
服务器(socket/bind/listen/accept) ip::tcp::acceptor 类成员函数(无需专门监听 socket)
客户端(socket/connect/recv/send) ip::tcp::socket 类成员函数,额外提供 remote_endpoint()/local_endpoint()
域名/IP 与端口解析 ip::tcp::resolver
高并发非阻塞通信 boost::asio::io_context 异步事件循环

1.2 同步 vs 异步(阻塞 vs 非阻塞)

  • 同步 :操作阻塞当前线程直到完成(如 read_somewriteconnect),编码简单,适合客户端或低并发场景。
  • 异步 :函数立即返回、不阻塞线程,由 io_context 在后台完成后再调用回调函数(handler),适合服务器高并发场景。

关键规律:凡是使用异步 函数后,必须调用 io_context::run()poll() 驱动事件循环,否则异步操作永不执行。

1.3 Boost库异步回调函数签名规律

所有异步操作的回调形参里都有 boost::system::error_code,这个对象用来表示异步操作是否正确

cpp 复制代码
// 同步:结果通过返回值 + error_code& 获得
size_t n = socket.read_some(buffer, ec);

// 异步:结果在回调参数中,签名一般是 void(error_code, size_t)
socket.async_read_some(buffer, [](boost::system::error_code ec, std::size_t n){ ... });

二、头文件与模块说明

头文件 说明 本项目使用点
boost/asio.hpp Asio 全部模块与功能 各 .cpp/.h/.hpp
boost/beast.hpp Beast 全部主要模块(Core/HTTP/WebSocket/Version) beast_httpserver.hpp
boost/beast/core.hpp Core:网络操作、缓冲区管理、错误处理 (被 beast.hpp 包含)
boost/beast/http.hpp HTTP 协议:请求/响应构造、解析、收发 beast_httpserver.hpp
boost/beast/version.hpp Beast 版本信息 (被 beast.hpp 包含)
boost/uuid/uuid.hpp boost::uuids::uuid 类型(128 位唯一标识) tcp_server.h
boost/uuid/uuid_io.hpp UUID 输入输出支持(流操作、to_string tcp_server.h
boost/uuid/uuid_generators.hpp UUID 生成器类 tcp_server.h

三、核心基础设施类

1. boost::system::error_code

Boost 的错误类,默认构造值 0 表示无错误,非 0 表示错误码。

cpp 复制代码
boost::system::error_code ec;
int v = ec.value();        // 获取错误码
std::string m = ec.message(); // 获取错误信息字符串
if (ec) { /* 隐式转换 bool:非 0 即 true */ }

2. boost::system::system_error

Boost 的异常类,内部封装 boost::system::error_code

cpp 复制代码
catch (boost::system::system_error& sec) {
    sec.code().value(); // 取错误码
    sec.what();         // 取错误信息字符串
}

同步操作的函数有两套版本

  • 抛异常版本 :不传 boost::system::error_code,失败抛 boost::system::system_error,用 try-catch 统一处理。适合 open/listen/bind/connect/accept
  • 错误码版本 :传 boost::system::error_code&,根据错误码精细处理。适合 read_some/receive/read/write_some/send/write

异步操作的函数特点

​ 对于所有异步操作都会要求在回调函数callback中的形参有boost::system::error_code对象,来接收异常操作的执行成功或失败情况设置的错误码,在回调函数内进行判断执行结果是否成功。

cpp 复制代码
// 常用错误码
boost::asio::error::connection_reset  // 异常断开连接
boost::asio::error::connection_eof    // 正常断开连接(eof)
boost::asio::error::operation_aborted // 主动取消(如 cancel() 定时器)

3. boost::asio::buffer

模板函数 boost::asio::buffer 把各种数据转换成 I/O 缓冲区:

cpp 复制代码
// 可转换:原生数组、智能指针数组、std::vector<T>、std::string、自定义连续缓冲区结构
socket.read_some(boost::asio::buffer(buff, 1024), ec);          // 数组首地址+大小
boost::asio::write(sock, boost::asio::buffer(recv_data_ptr.get(), 1024), ec); // 智能指针数组
boost::asio::buffer(str);   // string/vector 可不给大小,自动推断

返回类型:

  • boost::asio::mutable_buffer / mutable_buffers_1:可读写,用于接收
  • boost::asio::const_buffer / const_buffers_1:只读,用于发送

4. boost::asio::io_context

​ Asio 的异步核心,管理着所有的异步操作(非阻塞方式)并且是线程安全的,可以被多线程共享。负责等待异步操作的完成,并调用相应的处理函数,所有异步回调函数必须在boost::asio::io_context::run启动后才会进行异步回调处理,但是在异步事件为空时会直接退出,因此通常需要采用boost::asio::executor_work_guard<boost::asio::io_context::executor_type>类进行管理

​ 在需要退出io_context需要退出异步事件循环时,该管理对象需要执行boost::asio::executor_work_guard::reset函数释放管理的io_context对象,然后io_context执行boost::asio::io_context::stop退出异步事件循环

函数 作用
run() 启动事件循环,阻塞当前线程 直到无异步任务或调用 stop()。通常在子线程中运行以与主线程并发
poll() run() 的非阻塞替代,立即处理当前已就绪的事件、不等待新事件进行阻塞,但需多次调用
stop() 停止执行异步操作及其回调
reset() 重新启动事件循环
post(callback) 投递异步任务,在事件循环某时刻执行
dispatch(callback) 投递异步任务;正在事件循环则立即执行,否则入队
get_executor() 获取 io_context::executor_type 执行器对象
cpp 复制代码
boost::asio::io_context ioc;
// ... 投递异步任务 ...
ioc.run(); // 阻塞驱动事件循环

5.boost::asio::executor_work_guard<T>

用于管理与T类型执行器相关的工作状态,常见的是用于管理io_context不会在异步事件为空时直接退出异步事件循环 ,在io_context需要退出异步事件循环时,需要去哦执行boost::asio::executor_work_guard<T>::reset函数释放管理。

  • io_context::get_executor() 返回执行器,负责"任务如何执行"。
  • executor_work_guard<T>:模板类,管理执行器的工作状态,防止 io_context 在异步事件为空时直接退出 run()
cpp 复制代码
// 常用方式一:显式传 executor
boost::asio::executor_work_guard<boost::asio::io_context::executor_type>
    work_guard(io_context.get_executor());

// 常用方式二:make_work_guard 直接由 io_context 构造(更简洁)
auto work_guard = boost::asio::make_work_guard(io_context);

work_guard.reset(); // 通知工作结束,之后 io_context 无任务时可退出

老版本(< 1.78.0)常用 boost::asio::io_context::work 类管理,用法:work w(ioc); / w.reset(ioc);

6. boost::asio::strand<T>

用于io_context对象同时在多个线程进行run启动时,该io_context异步事件循环的异步操作会被多个线程同时执行造成线程安全问题采用 boost::asio::strand<T>对象和io_context的异步回调函数采用boost::asio::bind_executor进行绑定 ,可以确保即使在多线程情况下启动同一个io_contextio_context 的异步回调函数在事件循环中按顺序(串行)执行,避免多线程同时访问共享资源造成数据竞争。

cpp 复制代码
// 创建 strand,绑定到 io_context 执行器
boost::asio::strand<boost::asio::io_context::executor_type> _strand(io_context.get_executor());

// boost::asio::bind_executor将异步回调绑定到 strand 串行执行
socket.async_read_some(
    boost::asio::buffer(...),
    boost::asio::bind_executor(_strand, read_callback)
);

原理:线程取消直接调用方式,回调被投递到 strand 队列中由统一线程串行调用,换取线程安全(牺牲部分并发)。

7. boost::asio::steady_timer

​ 基于单调时钟的定时器,时间到触发异步超时操作,需要绑定一个启动的io_context异步事件循环,定时器超时时在该异步事件循环中执行异步回调函数std::function<void(boost::system::error_code)>

cpp 复制代码
// 常用构造
boost::asio::steady_timer t(ioc);                                   // 关联 io_context,未设到期
boost::asio::steady_timer t(ioc, boost::asio::chrono::seconds(60)); // 关联并设置相对到期时间

t.expires_after(boost::asio::chrono::seconds(60)); // 重新设置相对到期并开始计时
t.async_wait([](boost::system::error_code ec) {    // 异步等待,到期调用回调
    if (ec.value() == 0) { /* 到期 */ }
    else if (ec.value() == boost::asio::error::operation_aborted) { /* 被 cancel */ }
});
t.wait();              // 同步阻塞等待到期
std::size_t n = t.cancel();     // 取消所有等待的异步操作,返回取消个数
std::size_t n = t.cancel_one(); // 取消一个

boost::asio::chronostd::chrono 功能类似,含 steady_clock::durationsecondsmilliseconds 等特化。

8. boost::asio::signal_set

允许在异步操作中注册一个或多个信号,并在信号被触发时执行相应的异步回调函数,在创建时需要绑定一个启动的异步事件循环io_context对象和监听的信号,实现在执行其他任务的同时,监听注册的信号并执行回调函数处理,而不需要阻塞主线程。

​ 回调函数类型std::function<void(boost::system::error_code ec,int single),其中如果ec为boost::system::errc::success,表示信号被成功捕获一个注册信号single;否则表示发生错误。

cpp 复制代码
boost::asio::signal_set signals(io_context, SIGINT, SIGTERM); // 注册 Ctrl+C 与终止信号
signals.async_wait([](boost::system::error_code ec, int sig) {
    // 回调签名:void(error_code, int)
    if (ec.value() == 0) { /* 捕获到信号 sig,做相应处理 */ }
});
std::size_t n = signals.cancel(); // 取消挂起等待

可以使用优雅退出服务器:

cpp 复制代码
signals.async_wait([&io_context, &iocontext_guard, &httpserver](auto, auto) {
    iocontext_guard.reset(); // 告知外部 io_context 工作结束
    io_context.stop();       // 停止事件循环
    httpserver.stop();       // 停止内部通信线程池
});

四、Asio 基础网络库(IP / TCP)

1. ip::address / address_v4 / address_v6

ip::address_v4ip::address_v6分别表示IPV4地址和IPV6地址,ip::address是通用的IP地址对象。

cpp 复制代码
// 字符串 -> 地址
boost::asio::ip::address ip_address = boost::asio::ip::make_address("10.231.29.166"); // 推荐(自动识别 v4/v6)
boost::asio::ip::address ip_address = boost::asio::ip::address::from_string("10.231.29.166"); // 或静态成员

// 地址 -> 字符串
std::string ip_str = ip_address.to_string();

// 特殊地址
boost::asio::ip::address_v4::any(); // 任意本机 IPv4(服务器监听用)
boost::asio::ip::address_v6::any(); // 任意本机 IPv6

// 支持 `== != < <= > >= >>` 等运算符重载。

2. ip::tcp::protocol

​ 表示 TCP 协议族的静态工具,提供一系列与TCP协议相关的方法和属性,通常不与用户代码直接交互,而是通过其他类(如boost::asio::ip::tcp::socketboost::asio::ip::tcp::acceptor)来间接使用。

cpp 复制代码
boost::asio::ip::tcp::protocol p = boost::asio::ip::tcp::v4(); // IPv4 TCP
boost::asio::ip::tcp::protocol p = boost::asio::ip::tcp::v6(); // IPv6 TCP

p.family();    // 地址族 AF_INET / AF_INET6
p.type();      // 套接字类型 SOCK_STREAM
p.protocol();  // 协议号 IPPROTO_TCP(6)

3. ip::tcp::endpoint

表示一个TCP连接的端点,包含IP地址和端口号,一个 TCP 端点 = IP 地址 + 端口号。

cpp 复制代码
// 服务器:任意本机 IP + 端口(用于监听)
// IP协议版本+端口号创建
boost::asio::ip::tcp::endpoint ep(boost::asio::ip::tcp::v4(), 9998);
// IP地址+端口号创建
boost::asio::ip::tcp::endpoint ep2(boost::asio::ip::address_v4::any(), 9998);

// 客户端:指定服务器 IP + 端口
boost::asio::ip::tcp::endpoint server_ep(server_ip, server_port);

ep.address();    // ip::address
ep.port();       // unsigned short
ep.protocol();   // ip::tcp::protocol

4. ip::tcp::resolver

​ 把主机名 (域名或 IP 字符串)和服务名 (服务名或端口字符串)解析成一系列ip::tcp::endpoint端点列表,在创建时需要传入异步事件循环boost::asio::io_context用于进行异步解析操作ip::tcp::resolver::async_resolve ,同时也可以进行同步解析ip::tcp::resolver::resolve

cpp 复制代码
boost::asio::ip::tcp::resolver resolver(io_context);

// resolve同步解析(新版:返回 results_type 迭代器,可像 iterator 一样遍历)
boost::asio::ip::tcp::resolver::results_type results =
    resolver.resolve(boost::asio::ip::tcp::v4(), host, service, ec);

// async_resolve异步解析(新版)
resolver.async_resolve(tcp::v4(), host, service,
    [](boost::system::error_code ec, boost::asio::ip::tcp::resolver::results_type results){ ... });

旧版组件(配合老接口理解):

  • ip::tcp::resolver::query:保存解析请求(主机名/服务名/选项)。常用 flags:address_configuredall_matchingnumeric_hostnumeric_service
  • ip::tcp::resolver::iterator:端点迭代器,默认构造即末尾,配合 while 遍历所有解析结果。
cpp 复制代码
resolver.resolve(query);              // 返回 iterator(阻塞)
resolver.async_resolve(query, cb);    // 异步
boost::asio::connect(socket, begin, end, ec); // 依次尝试每个端点直至成功

5. ip::tcp::socket(通信套接字)

​ 用于表示一个TCP套接字,专门用于客户端和服务端的TCP通信 ,不用于服务器监听。内部封装Socket的同时,提供服务器与客户端通过套接字通信的连接读取发送 的同步或异步方法。在创建时需要传入一个异步事件循环boost::asio::io_context,用于实现异步事件回调操作的执行

cpp 复制代码
boost::asio::ip::tcp::socket socket(io_context); // 关联 io_context

打开

c++ 复制代码
// 使用某种IP协议打开socket,可以省略后续会自动检测协议版本进行打开。
open(ip::tcp::protocol protocol):

连接(客户端)

​ 客户端通信套接字进行连接指定socket地址,包括指定IP地址和端口的ip::tcp::endpointip::tcp::resolver解析出来的ip::tcp::endpoint列表,包括同步和异步两种方式。

cpp 复制代码
// 同步
connect(const ip::tcp::endpoint& endpoint,boost::system::error_code& ec); // 连接指定端点
boost::asio::connect(ip::tcp::socket& socket, ip::tcp::resolver::iterator& begin,
       ip::tcp::resolver::iterator& end, boost::system::error_code& ec);  // 连接端点列表
// 异步
async_connect(const ip::tcp::endpoint& endpoint, std::function<void(boost::system::error_code)> handler);
boost::asio::async_connect(ip::tcp::socket& socket, ip::tcp::resolver::iterator& begin,
                           std::function<void(boost::system::error_code, ip::tcp::endpoint)> handler);

读取(对应 recv/read)

​ 从通信套接字读取数据到接收缓冲区,返回读取的字符个数,并写入错误状态。包括同步版本和异步版本,同步版本会一直阻塞直到满足条件;异步版本会立即返回,在后台运行后通过异步事件循环io_context执行异步回调函数

read_someasync_read_some函数只保证读取到数据,不保证读满缓冲区receiveboost::asio::readasync_receiveboost::asio::async_read保证读满缓冲区 。这些函数会返回两个值,包括:读取字节数和错误状态码 ,同步版本会直接返回读取字节数,同时对传入的boost::system::error_code&写入错误状态;异步版本会在回调函数中传入读取字节数size_t和错误状态boost::system::error_code。其中错误状态码非0时,表示读取错误;boost::asio::error::eof表示正常断开连接;boost::asio::error::connection_reset表示客户端异常。

c++ 复制代码
// 同步方式
size_t read_some(mutable_buffers_1& buff, boost::system::error_code& ec);
size_t receive(mutable_buffers_1& buff);
size_t boost::asio::read(ip::tcp::socket& socket,mutable_buffers_1& buff, boost::system::error_code& ec);

// 异步方式
async_read_some(mutable_buffers_1& buff, std::function<void(size_t, boost::system::error_code)> callback);
async_receive(mutable_buffers_1& buff, std::function<void(size_t, boost::system::error_code)> callback);
boost::asio::async_read(ip::tcp::socket, mutable_buffers_1& buff, 
                        				std::function<void(size_t, boost::system::error_code)> callback);

写入(对应 send/write)

​ 通过通信套接字将发送缓冲区的数据进行发送,返回发送成功的字节数,并写入错误状态。类似的同样包括同步版本和异步版本,同步版本会一直阻塞直到满足条件;异步版本会立即返回,在后台运行后通过异步事件循环io_context执行异步回调函数

write_someasync_write_some函数不保证写完发送待缓冲区数据sendboost::asio::writeasync_sendboost::asio::async_write函数保证可以写完待发送缓冲区数据 。这些函数会返回两个值,包括:发送字节数和错误状态码 ,同步版本会直接返回读取字节数,同时对传入的boost::system::error_code&写入错误状态;异步版本会在回调函数中传入发送字节数size_t和错误状态boost::system::error_code

c++ 复制代码
// 同步方式
size_t write_some(const_buffers_1& buff, boost::system::error_code& ec);
size_t send(const_buffers_1& buff);
size_t boost::asio::write(ip::tcp::socket& socket, const_buffers_1& buff, boost::system::error_code& ec);

// 异步方式
async_write_some(const_buffers_1& buff, std::function<void(size_t, boost::system::error_code)> callback);
async_send(const_buffers_1& buff, std::function<void(size_t, boost::system::error_code)> callback);
boost::asio::async_write(ip::tcp::socket, const_buffers_1& buff, 
                         				std::function<void(size_t, boost::system::error_code)> callback);

boost::asio::buffer<T>缓冲区转换函数

​ 可以将标准容器包括:std::vectorstd::string等、原生数组、智能指针、自定义缓冲区等转换为Boost库的mutable_buffers_1const_buffers_1类型,其中非const类型转换为mutable_buffers_1,const类型转换为const_buffers_1

c++ 复制代码
// T为 数组 / 连续容器 / 字符串(可自动推断大小)
template <typename T>
mutable_buffers_1 buffer(T);               // 非 const 数组,可读写
template <typename T>
const_buffers_1 buffer(const T);            // const 数组,只读

// T为原生数组、智能指针、自定义缓冲区(显式指定大小)
template <typename T>
mutable_buffers_1 buffer(T* data, size_t size);        // 可写内存块

template <typename T>
const_buffers_1 buffer(const T* data, size_t size);    // 只读内存块

关闭

​ 关闭通信套接字,包括全关闭、半关闭等。

cpp 复制代码
socket.close(boost::system::error_code& ec);      // 关闭并释放资源(触发四次挥手)  
socket.shutdown(boost::asio::ip::tcp::socket::shutdown_send);    // 只关发送
socket.shutdown(boost::asio::ip::tcp::socket::shutdown_receive); // 只关接收
socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both);    // 全关

服务器大量连接时主动 close 会造成 close_wait 资源浪费;优雅做法是 shutdown(shutdown_send) 后等客户端 close

端点信息

cpp 复制代码
socket.remote_endpoint().address().to_string(); // 对端(客户端/服务器)IP
socket.remote_endpoint().port();                // 对端端口
socket.local_endpoint().address().to_string();  // 本地自动绑定 IP
socket.local_endpoint().port();                 // 本地端口

6. ip::tcp::acceptor(服务器监听)

​ 专门用于服务器监听并接受客户端的连接,内部会自动创建Socket套接字去监听客户端的连接,并提供一系列方法实现监听客户单:openbindlistenacceptasync_accept函数。

构造函数

c++ 复制代码
// 默认构造创建未绑定的tcp::acceptor,需要手动open、bind、listen
acceptor(io_context& io_context);
// 创建未绑定的tcp::acceptor并进行打开,需要手动bind、listen
acceptor(io_context& io_context,ip::tcp::protocol protocol);
// 创建并初始化tcp::acceptor,自动完成open、bind、listen
acceptor(io_context& io_context, const ip::tcp::endpoint& endpoint);

打开、绑定、监听、接受函数

​ 对应于Socket编程的socketbindlistenaccept函数,实现服务器监听客户端的连接,接受客户端的连接包括同步方式和异步方式,同步方式会阻塞直到服务器监听到客户端的连接,异步方式会立即返回,在异步事件循环io_context中执行接受客户端连接后的回调函数。

c++ 复制代码
// 打开操作
// 静态函数ip::tcp::v4()、ip::tcp::v6()或ip::tcp::endpoint::protocol成员函数获取ip::tcp::protocol类型的IP协议对象
open(ip::tcp::protocol protocol):

// 绑定操作
// 将acceptor绑定到本地IP地址和端口号上,用于监听外部客户端对服务器的连接请求。
bind(const ip::tcp::endpoint& endpoint);

// 监听操作
// acceptor对象设置监听连接请求,backlog表示一次性最大的客户端连接请求个数,有默认值socket_base::max_connections为128。
listen(int backlog=socket_base::max_connections)

// 接受操作
// 先创建一个服务器与客户端通信的套接字socket传入
// 同步方式
void accept(ip::tcp::socket& socket);	
// 异步方式
void async_accept(ip::tcp::socket& socket,std::function<const boost::system::error_code&> callback);

7. 字节序转换函数

字节序(大小端):主机多小端,网络传输用大端(网络字节序)。

cpp 复制代码
// 主机字节序 -> 网络字节序
short h2n_short = boost::asio::detail::socket_ops::host_to_network_short(host_short); // 16 位
int   h2n_long  = boost::asio::detail::socket_ops::host_to_network_long(host_int);    // 32 位

// 网络字节序 -> 主机字节序
short n2h_short = boost::asio::detail::socket_ops::network_to_host_short(net_short);
int   n2h_long  = boost::asio::detail::socket_ops::network_to_host_long(net_long);

二进制数据(整数、浮点)收发需做字节序转换;文本字符串一般无需转换。


五、Boost.Beast HTTP 网络库

1. HTTP 协议

基于 TCP 的无状态、请求-响应式应用层协议,短连接,通信完连接关闭。报文分包头(Header)和包体(Body):

  • 请求 :请求行(方法 + 路径 + 版本,如 GET /index.html HTTP/1.1)+ 请求头(Host/Accept/User-Agent/Cookie/Connection...)+ 请求体(POST/PUT 携带数据)。
  • 响应 :状态行(如 HTTP/1.1 200 OK)+ 响应头(Content-Type/Content-Length/Set-Cookie/Server...)+ 响应体。

用裸 Asio 手写 HTTP 服务器需自行解析请求行/请求头并构造响应,非常复杂。使用 Beast 的 http::request/http::responsehttp::async_read/http::async_write 即可自动解析/封装 HTTP 报文。

2. 消息体 Body 类型

Body 决定消息体如何存储与处理:

Body 类型 说明 常用函数
http::string_body 字符串存消息体,适合小文本(JSON/XML) std::string& data()size()
http::file_body 文件系统文件,适合大文件传输 open(path, beast::file_mode::scan, ec)size()
http::dynamic_body 底层为 multi_buffer,动态增减,适合大小未知数据 data()size()empty()

file_modescan(读文件作响应体)、write(写文件)、write_new(覆盖/新建写)。

3. http::request / http::response

模板类型:request = http::message<true, Body, Fields>response = http::message<false, Body, Fields>

request 常用成员

cpp 复制代码
boost::beast::http::request<boost::beast::http::dynamic_body> req;

req.method(boost::beast::http::verb::get);       // 设请求方法
req.method();                                     // 取 http::verb
req.target("/count");                             // 设资源路径
req.version(11);                                  // HTTP/1.1
req.body();                                       // 请求体引用
req.prepare_payload();                            // 自动同步 Content-Length 等头部
req.set(boost::beast::http::field::content_type, "application/json"); // 设头部
req[boost::beast::http::field::host];             // 取头部

HTTP 字段枚举 http::fieldhostacceptuser_agentcookieservercontent_typecontent_length...

response 常用成员

cpp 复制代码
boost::beast::http::response<boost::beast::http::dynamic_body> res;

res.result(boost::beast::http::status::ok);        // 200
res.result(boost::beast::http::status::not_found); // 404
res.version(_request.version());                   // 版本跟随请求
res.keep_alive(false);                             // 短连接
res.set(boost::beast::http::field::server, "Beast");
res.set(boost::beast::http::field::content_type, "text/html");
boost::beast::ostream(res.body()) << "<html>...</html>"; // 流式写响应体
res.content_length(res.body().size());

http::status 常用:ok(200)bad_request(400)not_found(404)

判断请求类型(服务器路由):

cpp 复制代码
switch (_request.method()) {
case boost::beast::http::verb::get:  ... break;
case boost::beast::http::verb::post: ... break;
default: ...
}
if (_request.target() == "/count") { ... }

4. http::async_read / http::async_write

cpp 复制代码
// 异步读取并解析一个 HTTP 报文到 msg
boost::beast::http::async_read(_socket, _recv_buffer, _request,
    [self](boost::system::error_code ec, std::size_t byte){ ... });
// stream: socket/tcp_stream; buffer: flat_buffer/multi_buffer;
// msg: request 或 response; handler: void(ec, size_t)

// 异步写一个 HTTP 报文
boost::beast::http::async_write(_socket, _response,
    [self](boost::system::error_code ec, std::size_t byte){ ... });

5. Beast 常用辅助(buffer / ostream / flat_buffer)

  • boost::beast::ostream(body):把动态缓冲区当作 std::ostream 流式写入。
  • boost::beast::buffers_to_string(buffers.data()):缓冲区 → std::string
  • boost::beast::flat_buffer(n):连续内存的动态缓冲区,data()/size()/consume(n)
  • boost::beast::multi_buffer:多片段缓冲区,data()/size()
  • boost::ignore_unused(x):显式忽略未使用参数,避免告警。

HTTP 服务器完整通信对象骨架 (本项目 HttpServer_Communicate):

cpp 复制代码
class HttpServer_Communicate : public std::enable_shared_from_this<HttpServer_Communicate> {
    boost::asio::ip::tcp::socket _socket;
    boost::beast::flat_buffer _recv_buffer;                 // 收包缓冲
    boost::beast::http::request<boost::beast::http::dynamic_body> _request;
    boost::beast::http::response<boost::beast::http::dynamic_body> _response;
    boost::asio::steady_timer _deadline_timer;              // 超时断连

    void start_http_communicate() { check_deadline(); recv_request(); }
    // 1) async_read 收请求 -> 2) process_request 按 method/target 构造响应
    // 3) async_write 发响应 -> shutdown(shutdown_send) + cancel() 定时器
};

本项目还演示了 HTTP 服务器接收 POST 的 JSON,用 jsoncpp 解析后回 JSON 响应:

cpp 复制代码
std::string body_str = boost::beast::buffers_to_string(_request.body().data());
Json::Reader reader;
Json::Value recv_root;
bool ok = reader.parse(body_str, recv_root);
std::string email = recv_root["email"].asString();
Json::Value root; root["error"] = 0; root["msg"] = "提交数据成功!";
boost::beast::ostream(_response.body()) << root.toStyledString();

六、Boost.Beast WebSocket 网络库

WebSocket 本质是在 TCP 长连接之上的进一步封装,提供全双工 、基于的通信。两种建立方式:

  1. 先 TCP 连接、再协议升级 :把已连接 socket 转移构造为 websocket::stream,服务器读 HTTP 请求头判断 Upgrade: websocket,回 101,再握手升级。
  2. 直接构造 stream :从 stream 取最低层裸 socket 连接,成功后用握手函数升级。
cpp 复制代码
// 底层流类型:socket(基础)或 beast::tcp_stream(支持超时、速率限制等高级功能)
boost::beast::websocket::stream<boost::beast::tcp_stream> ws(ioc);

// 客户端/服务器握手
ws.handshake(host, target);          // 客户端同步
ws.async_handshake(host, target, cb);// 客户端异步
ws.accept();                         // 服务器同步
ws.async_accept(cb);                 // 服务器异步

// 收发(自动封装/解析 websocket 帧)
ws.async_read(buf, cb);   // void(ec, size_t)
ws.async_write(buf, cb);  // void(ec, size_t)

// 控制帧与关闭
ws.ping("reason"); / ws.async_ping(...);
ws.pong("reason"); / ws.async_pong(...);
ws.close(boost::beast::websocket::close_code::normal, "bye");
ws.async_close(...);

// 底层访问
ws.next_layer();          // 下一层(tcp)引用
boost::beast::get_lowest_layer(ws); // 最底层 socket

注意事项:websocket::stream 非线程安全,异步多线程下需用 strand 保证串行;若用 tcp_stream 作为底层,需禁用其自身超时,避免与 WebSocket 超时冲突(未定义行为)。


七、异步高并发服务器

1. 三种 Asio 多线程模型

  1. 多 io_context 并发、io_context 内串行Asio_Iocontext_Pool):创建多个io_context并创建相应数量的子线程,让每一个io_context在子线程中执行run函数,实现每个io_context的异步事件循环在不同的子线程中进行。并提供一个外界使用io_context的接口给外界使用,在创建服务器与客户端通信的socket时使用,这样就会使基于该socket通信的异步读写事件在相应的io_context的异步事件循环中,从而在不同子线程中执行。

    c++ 复制代码
    class Asio_Iocontext_Pool:public Singleton<Asio_Iocontext_Pool> {
    	friend class Singleton<Asio_Iocontext_Pool>;//声明CRTP方式的单例父类为友元
    private:
    	//无参构造函数:RAII思想
    	Asio_Iocontext_Pool(int core_num = std::thread::hardware_concurrency()):Singleton<Asio_Iocontext_Pool>(), vect_io_contexts(core_num) {
    		//循环创建work_guard管理每一个io_context对象
    		for (int i = 0; i < core_num; i++)
    			vect_work_guards.emplace_back(vect_io_contexts.at(i).get_executor());
    		//循环创建子线程,每个子线程运行一个io_context的run方法的异步事件循环,在该子线程内执行异步事件循环。
    		for (int i = 0; i < core_num; i++)
    			vect_threads.emplace_back([this, i]() {
    				vect_io_contexts[i].run();//每一个io_context在一个子线程中执行run函数进行在该子线程内异步事件循环
    			});
    	}
    	//成员变量
    	std::vector<boost::asio::io_context> vect_io_contexts;//保存多个io_context对象,分别代表一个线程执行一部分socket的异步操作
    	std::vector<boost::asio::executor_work_guard<boost::asio::io_context::executor_type>> vect_work_guards;//管理io_context对象,防止没有异步事件直接退出异步事件循环
    	std::vector<std::thread> vect_threads;//保存多个io_context对象的运行子线程
    	
    public:
    	//析构函数:RAII思想
    	~Asio_Iocontext_Pool() {
    		stop();
    	}
    	//停止函数,方便停止所有io_context的异步事件循环,等待子线程运行结束,释放所有资源
    	void stop() {
    		int core_num = std::thread::hardware_concurrency();//获取可以并行的线程个数,即并行的io_context的个数
    		//先关闭所有事件循环的io_context
    		for (int i = 0; i < core_num; i++) {
    			vect_work_guards.at(i).reset();//重置所有io_context的管理者,以告知io_context的异步事件结束
    			vect_io_contexts[i].stop();//关闭所有的io_context的异步事件循环
    		}
    		//等待所有子线程运行结束
    		for (std::thread& t : vect_threads)
    			if (t.joinable())
    				t.join();
    	}
    	//提供可以访问到io_context对象引用的公共函数,以方便外部使用,采用io_context_number记录以顺序的方式使用io_context,保证平均分配
    	boost::asio::io_context& get_Io_context() {
    		static 	unsigned short io_context_number = 0;//记录使用的io_context对象的序号,静态变量只在第一次使用时初始化,并且在生命周期和进程相同,防止不必要的重复创建
    		static unsigned short core_num = std::thread::hardware_concurrency();//获取并行线程数,即io_context的个数
    		boost::asio::io_context& temp = vect_io_contexts[io_context_number];
    		io_context_number = (io_context_number + 1) % core_num;//遍历方式平均使用io_context
    		return temp;//引用方式返回io_context对象
    	}
    };
  2. 单 io_context、io_context 内并发Asio_Thread_Pool):创建一个io_context和多个子线程,每个子线程执行该io_context的run方法,这样使每个子线程都能执行该io_context的异步事件循环的异步就绪事件,实现并发,但会造成读写的线程安全问题,还需要提供一个绑定该io_context的strand对象,使用boost::asio::bind_exector函数绑定异步操作的回调函数到该strand对象上,实现异步事件在io_context的串行执行,以牺牲并发效率实现线程安全。

    c++ 复制代码
    class Asio_Thread_Pool:public Singleton<Asio_Thread_Pool> {
    	friend class Singleton<Asio_Thread_Pool>;
    private:
    	//构造函数:RAII思想
    	Asio_Thread_Pool(int core_num = std::thread::hardware_concurrency()):Singleton<Asio_Thread_Pool>(), work_guard(io_context.get_executor()), bind_strand(io_context.get_executor()) {
    		//创建多个子线程执行io_context的run函数
    		for (int i = 0; i < core_num; i++)
    			vect_threads.emplace_back([this]() {
    				io_context.run();//实现子线程并发执行io_context的异步事件循环
    			});
    	}
    	//成员变量
    	boost::asio::io_context io_context;//asio多线程使用的同一个io_context异步事件循环
    	boost::asio::executor_work_guard<boost::asio::io_context::executor_type> work_guard;//管理io_context,防止在异步事件没有时直接退出循环
    	boost::asio::strand<boost::asio::io_context::executor_type> bind_strand;//绑定io_context异步事件到strand对象中,实现串行执行
    	std::vector<std::thread> vect_threads;//asio多线程模型中的子线程,用于执行run方法
    
    public:
    	//析构函数:RAII思想
    	~Asio_Thread_Pool() {
    		stop();
    	}
    	//停止函数,停止事件循环,并等到线程运行结束,释放资源
    	void stop() {
    		//停止io_context的事件循环
    		work_guard.reset();
    		io_context.stop();
    		//等待所有子线程运行结束
    		for (std::thread& t : vect_threads)
    			if (t.joinable())
    				t.join();
    	}
    	//获取asio线程池的io_context对象的引用,以提供给外界使用
    	boost::asio::io_context& get_Io_context() {
    		return io_context;
    	}
    	//获取asio线程池的strand对象的引用,以提供给外界使用,实现绑定异步事件的回调函数在io_context的事件循环中串行执行
    	boost::asio::strand<boost::asio::io_context::executor_type>& get_Strand() {
    		return bind_strand;
    	}
    };
  3. 多 io_context 并发且 io_context 内并发Asio_Iocontext_Thread_Pool,项目采用):多个 io_context × 每 io_context 多个线程,每个 io_context 配一个 strand;连接层用 seq_num 轮询选 io_context 与 strand。

    C++ 复制代码
    class Asio_Iocontext_Thread_Pool :public Singleton<Asio_Iocontext_Thread_Pool> {
    	friend class Singleton<Asio_Iocontext_Thread_Pool>;
    private:
    	//构造函数:RAII思想
    	Asio_Iocontext_Thread_Pool(int core_num = std::thread::hardware_concurrency()) :Singleton<Asio_Iocontext_Thread_Pool>(), vect_io_context(core_num), vvect_thread(core_num) {
    		for (int i = 0; i < core_num; i++) {
    			vect_work_guard.emplace_back(vect_io_context.at(i).get_executor());
    			vect_strand.emplace_back(vect_io_context[i].get_executor());
    		}
    		for (int i = 0; i < core_num; i++)
    			for (int j = 0; j < 2; j++)
    				vvect_thread[i].emplace_back([this, i]() {
    					vect_io_context[i].run();//vect_io_context[i]表示第i个io_context创建子线程运行run方法,默认创建两个子线程
    				});
    	}
    	//成员变量
    	std::vector<boost::asio::io_context> vect_io_context;//保存多个io_context
    	std::vector<boost::asio::executor_work_guard<boost::asio::io_context::executor_type>> vect_work_guard;//管理io_context在没有异步事件时不退出
    	std::vector<boost::asio::strand<boost::asio::io_context::executor_type>> vect_strand;//为每一个io_context提供一个strand进行管理异步事件串行执行
    	std::vector<std::vector<std::thread>> vvect_thread;//二维vector保存子线程,行数和io_context并发数相等,列数和io_context内并发线程数据相等
    
    public:
    	//析构函数:RAII思想
    	~Asio_Iocontext_Thread_Pool() {
    		stop();
    	}
    	//停止函数,先停止io_context的异步事件循环,阻塞等待所有子线程的退出,最后释放资源
    	void stop() {
    		//停止io_context的异步事件循环
    		for (boost::asio::executor_work_guard<boost::asio::io_context::executor_type>& work : vect_work_guard)
    			work.reset();
    		for (boost::asio::io_context& ioc : vect_io_context)
    			ioc.stop();
    		//阻塞等待子线程执行完毕
    		for (int i = 0; i < std::thread::hardware_concurrency(); i++)
    			for (std::thread& t : vvect_thread[i])
    				if (t.joinable())
    					t.join();
    	}
    	//提供给外部使用io_context的接口
    	boost::asio::io_context& get_Io_context(int i) {
    		return vect_io_context.at(i);//返回第i个io_context,用于创建服务器与客户端通信的io_context
    	}
    	//提供给外部使用strand的接口
    	boost::asio::strand<boost::asio::io_context::executor_type>& get_Strand(int i) {
    		return vect_strand[i];//返回第i个strand,用于绑定第i个io_context的异步事件
    	}
    };

2. Boost库实现异步高并发服务器

​ 本服务器采用 分层架构(连接层 / 通信层 / 逻辑层) + TLV 应用层协议 + 多 io_context 多线程 Asio 模型,解决同步阻塞、粘包、字节序、发送有序性、对象生命周期、通信逻辑解耦等网络编程核心问题。


一、总体架构与分层设计

服务器按职责划分为三层,外加一个"多线程运行底座",各层职责单一、通过接口解耦:

层次 职责
服务器连接层 Tcp_Server 创建 acceptor 循环 async_accept;为每个客户端创建通信对象并登记到 map<uuid, shared_ptr> 统一管理
服务器通信层 Server_Communicate_Client 拥有通信 socket;异步接收/发送数据;在接收回调内做粘包切包;把完整消息投递给逻辑层
服务器逻辑处理层 Logic_System(单例)+ LogicNode 独立线程从队列取消息,按消息 ID 查表执行处理函数,处理结果通过通信层对象发回客户端
公共缓冲结构 MsgNode TLV 收/发缓冲区:消息ID(2B)+消息长度(2B)+消息本身,记录总长/当前长实现收发完整性
运行底座(多线程模型) Asio_Iocontext_Thread_Pool 提供多个 io_context、每个 io_context 配 strand 和多线程事件循环,通信层按 seq_num 轮询选用

连接建立阶段

​ 服务器先准备一个客户端连接器,然后异步等待接收客户端的TCP连接,在接受客户端连接的异步回调函数中,对连接成功的客户端通过该客户端连接器进行全双工通信。
Asio_Iocontext_Thread_Pool Tcp_Server(连接层) Server_Communicate_Client Tcp_Server.acceptor 客户端(任意) Asio_Iocontext_Thread_Pool Tcp_Server(连接层) Server_Communicate_Client Tcp_Server.acceptor 客户端(任意) #mermaid-svg-9kHjbYr6ShgR1WpI{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-9kHjbYr6ShgR1WpI .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-9kHjbYr6ShgR1WpI .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-9kHjbYr6ShgR1WpI .error-icon{fill:#552222;}#mermaid-svg-9kHjbYr6ShgR1WpI .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-9kHjbYr6ShgR1WpI .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-9kHjbYr6ShgR1WpI .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-9kHjbYr6ShgR1WpI .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-9kHjbYr6ShgR1WpI .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-9kHjbYr6ShgR1WpI .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-9kHjbYr6ShgR1WpI .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-9kHjbYr6ShgR1WpI .marker{fill:#333333;stroke:#333333;}#mermaid-svg-9kHjbYr6ShgR1WpI .marker.cross{stroke:#333333;}#mermaid-svg-9kHjbYr6ShgR1WpI svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-9kHjbYr6ShgR1WpI p{margin:0;}#mermaid-svg-9kHjbYr6ShgR1WpI .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-9kHjbYr6ShgR1WpI text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-9kHjbYr6ShgR1WpI .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-9kHjbYr6ShgR1WpI .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-9kHjbYr6ShgR1WpI .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-9kHjbYr6ShgR1WpI .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-9kHjbYr6ShgR1WpI #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-9kHjbYr6ShgR1WpI .sequenceNumber{fill:white;}#mermaid-svg-9kHjbYr6ShgR1WpI #sequencenumber{fill:#333;}#mermaid-svg-9kHjbYr6ShgR1WpI #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-9kHjbYr6ShgR1WpI .messageText{fill:#333;stroke:none;}#mermaid-svg-9kHjbYr6ShgR1WpI .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-9kHjbYr6ShgR1WpI .labelText,#mermaid-svg-9kHjbYr6ShgR1WpI .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-9kHjbYr6ShgR1WpI .loopText,#mermaid-svg-9kHjbYr6ShgR1WpI .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-9kHjbYr6ShgR1WpI .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-9kHjbYr6ShgR1WpI .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-9kHjbYr6ShgR1WpI .noteText,#mermaid-svg-9kHjbYr6ShgR1WpI .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-9kHjbYr6ShgR1WpI .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-9kHjbYr6ShgR1WpI .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-9kHjbYr6ShgR1WpI .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-9kHjbYr6ShgR1WpI .actorPopupMenu{position:absolute;}#mermaid-svg-9kHjbYr6ShgR1WpI .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-9kHjbYr6ShgR1WpI .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-9kHjbYr6ShgR1WpI .actor-man circle,#mermaid-svg-9kHjbYr6ShgR1WpI line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-9kHjbYr6ShgR1WpI :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} altec == 0 连接成功ec != 0 失败 getInstance()->>get_Io_context(seq_num) + get_Strand(seq_num)1make_shared<Server_Communicate_Client>(ioc,this,seq_num)2acceptor.async_accept(comm->>get_socket(), accept_callback)3connect() 发起 TCP 连接4accept_callback(ec, comm_ptr)5comm_ptr->>communicate() 开始全双工通信6map_communicateuuid=comm_ptr (加锁)7seq_num=(seq_num+1)%core 轮询下一个 io_context8打印错误9circle_acceptor() 继续接受下一个连接10

一次数据请求处理(读 → 逻辑 → 写)

​ 在客户端完成和服务器的连接并建立全双工通信后,服务器开始异步接收客户端发送的数据,在异步接收的回调函数中进行黏包处理,采用TLV格式先读取指定长度的包头获取消息ID和消息长度,然后根据消息长度进行读取分隔一个数据包体,得到一个完整到的TLV格式的数据包,并将该数据包提交给逻辑系统进行处理,逻辑系统根据消息ID调用对应注册的回调函数处理,将处理结果通过异步发送回调函数进行发送。
Server连接层 Server_Communicate_Client(send/write_callback) Logic_System 单例(逻辑线程) MsgNode(recv_head_node/recv_data_node) Server_Communicate_Client(receive/read_some_callback) Server_Communicate_Client.socket 客户端 Server连接层 Server_Communicate_Client(send/write_callback) Logic_System 单例(逻辑线程) MsgNode(recv_head_node/recv_data_node) Server_Communicate_Client(receive/read_some_callback) Server_Communicate_Client.socket 客户端 #mermaid-svg-4R8bBqe8BO9QQv8B{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-4R8bBqe8BO9QQv8B .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-4R8bBqe8BO9QQv8B .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-4R8bBqe8BO9QQv8B .error-icon{fill:#552222;}#mermaid-svg-4R8bBqe8BO9QQv8B .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-4R8bBqe8BO9QQv8B .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-4R8bBqe8BO9QQv8B .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-4R8bBqe8BO9QQv8B .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-4R8bBqe8BO9QQv8B .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-4R8bBqe8BO9QQv8B .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-4R8bBqe8BO9QQv8B .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-4R8bBqe8BO9QQv8B .marker{fill:#333333;stroke:#333333;}#mermaid-svg-4R8bBqe8BO9QQv8B .marker.cross{stroke:#333333;}#mermaid-svg-4R8bBqe8BO9QQv8B svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-4R8bBqe8BO9QQv8B p{margin:0;}#mermaid-svg-4R8bBqe8BO9QQv8B .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-4R8bBqe8BO9QQv8B text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-4R8bBqe8BO9QQv8B .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-4R8bBqe8BO9QQv8B .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-4R8bBqe8BO9QQv8B .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-4R8bBqe8BO9QQv8B .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-4R8bBqe8BO9QQv8B #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-4R8bBqe8BO9QQv8B .sequenceNumber{fill:white;}#mermaid-svg-4R8bBqe8BO9QQv8B #sequencenumber{fill:#333;}#mermaid-svg-4R8bBqe8BO9QQv8B #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-4R8bBqe8BO9QQv8B .messageText{fill:#333;stroke:none;}#mermaid-svg-4R8bBqe8BO9QQv8B .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-4R8bBqe8BO9QQv8B .labelText,#mermaid-svg-4R8bBqe8BO9QQv8B .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-4R8bBqe8BO9QQv8B .loopText,#mermaid-svg-4R8bBqe8BO9QQv8B .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-4R8bBqe8BO9QQv8B .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-4R8bBqe8BO9QQv8B .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-4R8bBqe8BO9QQv8B .noteText,#mermaid-svg-4R8bBqe8BO9QQv8B .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-4R8bBqe8BO9QQv8B .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-4R8bBqe8BO9QQv8B .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-4R8bBqe8BO9QQv8B .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-4R8bBqe8BO9QQv8B .actorPopupMenu{position:absolute;}#mermaid-svg-4R8bBqe8BO9QQv8B .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-4R8bBqe8BO9QQv8B .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-4R8bBqe8BO9QQv8B .actor-man circle,#mermaid-svg-4R8bBqe8BO9QQv8B line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-4R8bBqe8BO9QQv8B :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} "客户端发送 TLV 包 ID\|LEN\|DATA" "校验 data_len>MAX 或 data_id>MAX 则置 receive_error 丢弃" loop"while byte_num \> 0 (一次可能包含多个完整包)" altec != 0数据合法 "数据到达(可能粘包/半包)"1"async_read_some(bind_executor(strand, read_some_callback, shared_from_this()))"2"read_some_callback(ec, byte_num, self)"3"delete_map_communicate_member(uuid) / 投递错误消息"4"拷贝拼装头部(recv_head_node) → 解析 data_id/data_len"5"接收数据本身(recv_data_node) 凑齐完整包"6"post_LogicNode(make_shared<LogicNode>(shared_from_this(), recv_data_node))"7"继续处理本次剩余字节 / receive() 再次接收(保证一直接收)"8"逻辑线程 wait 队列被唤醒, pop LogicNode"9"map_id_funcmsg-\>\>get_ID() 找到处理函数并执行"10"communicate_ptr->>send(data,len,id) 发送处理结果"11"入发送队列 queue_send_data (加锁, 若队空才发起 async_write)"12"async_write(bind_executor(strand, write_callback, shared_from_this()))"13"write_callback(ec, n, self)"14"队首已发完"15"收到服务器响应数据"16

服务器关闭流程
#mermaid-svg-ylPMHO3Klzqj4HAX{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ylPMHO3Klzqj4HAX .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ylPMHO3Klzqj4HAX .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ylPMHO3Klzqj4HAX .error-icon{fill:#552222;}#mermaid-svg-ylPMHO3Klzqj4HAX .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ylPMHO3Klzqj4HAX .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ylPMHO3Klzqj4HAX .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ylPMHO3Klzqj4HAX .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ylPMHO3Klzqj4HAX .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ylPMHO3Klzqj4HAX .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ylPMHO3Klzqj4HAX .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ylPMHO3Klzqj4HAX .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ylPMHO3Klzqj4HAX .marker.cross{stroke:#333333;}#mermaid-svg-ylPMHO3Klzqj4HAX svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ylPMHO3Klzqj4HAX p{margin:0;}#mermaid-svg-ylPMHO3Klzqj4HAX .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ylPMHO3Klzqj4HAX .cluster-label text{fill:#333;}#mermaid-svg-ylPMHO3Klzqj4HAX .cluster-label span{color:#333;}#mermaid-svg-ylPMHO3Klzqj4HAX .cluster-label span p{background-color:transparent;}#mermaid-svg-ylPMHO3Klzqj4HAX .label text,#mermaid-svg-ylPMHO3Klzqj4HAX span{fill:#333;color:#333;}#mermaid-svg-ylPMHO3Klzqj4HAX .node rect,#mermaid-svg-ylPMHO3Klzqj4HAX .node circle,#mermaid-svg-ylPMHO3Klzqj4HAX .node ellipse,#mermaid-svg-ylPMHO3Klzqj4HAX .node polygon,#mermaid-svg-ylPMHO3Klzqj4HAX .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ylPMHO3Klzqj4HAX .rough-node .label text,#mermaid-svg-ylPMHO3Klzqj4HAX .node .label text,#mermaid-svg-ylPMHO3Klzqj4HAX .image-shape .label,#mermaid-svg-ylPMHO3Klzqj4HAX .icon-shape .label{text-anchor:middle;}#mermaid-svg-ylPMHO3Klzqj4HAX .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ylPMHO3Klzqj4HAX .rough-node .label,#mermaid-svg-ylPMHO3Klzqj4HAX .node .label,#mermaid-svg-ylPMHO3Klzqj4HAX .image-shape .label,#mermaid-svg-ylPMHO3Klzqj4HAX .icon-shape .label{text-align:center;}#mermaid-svg-ylPMHO3Klzqj4HAX .node.clickable{cursor:pointer;}#mermaid-svg-ylPMHO3Klzqj4HAX .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ylPMHO3Klzqj4HAX .arrowheadPath{fill:#333333;}#mermaid-svg-ylPMHO3Klzqj4HAX .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ylPMHO3Klzqj4HAX .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ylPMHO3Klzqj4HAX .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ylPMHO3Klzqj4HAX .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ylPMHO3Klzqj4HAX .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ylPMHO3Klzqj4HAX .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ylPMHO3Klzqj4HAX .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ylPMHO3Klzqj4HAX .cluster text{fill:#333;}#mermaid-svg-ylPMHO3Klzqj4HAX .cluster span{color:#333;}#mermaid-svg-ylPMHO3Klzqj4HAX div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ylPMHO3Klzqj4HAX .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ylPMHO3Klzqj4HAX rect.text{fill:none;stroke-width:0;}#mermaid-svg-ylPMHO3Klzqj4HAX .icon-shape,#mermaid-svg-ylPMHO3Klzqj4HAX .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ylPMHO3Klzqj4HAX .icon-shape p,#mermaid-svg-ylPMHO3Klzqj4HAX .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ylPMHO3Klzqj4HAX .icon-shape .label rect,#mermaid-svg-ylPMHO3Klzqj4HAX .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ylPMHO3Klzqj4HAX .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ylPMHO3Klzqj4HAX .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ylPMHO3Klzqj4HAX :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 用户按 Ctrl+C
signal_set 触发 SIGINT/SIGTERM
async_wait 回调
外层 io_context_guard.reset + io_context.stop
Asio_Iocontext_Thread_Pool 单例 stop
逐个 work_guard.reset
逐个 io_context.stop
join 所有子线程
外层 io_context.run 返回
执行到右花括号释放对象


服务器连接层实现

职责:监听端口、循环异步接受连接、管理所有通信对象、为每个连接轮询分配 io_context/strand。

cpp 复制代码
class Tcp_Server {
    boost::asio::io_context& io_context;               // 外层事件循环引用
    boost::asio::ip::tcp::acceptor acceptor;           // 监听器
    unsigned short listen_port;
    boost::asio::executor_work_guard<io_context::executor_type> work_guard; // 防止外层 ioc 空转退出
    std::map<std::string, std::shared_ptr<Server_Communicate_Client>> map_communicate; // 登记所有通信对象
    std::mutex map_communicate_mutex;                  // 保护 map(多线程访问)
    int seq_num;                                       // 轮询:使用第几个 io_context/strand
};

构造函数(RAII)

cpp 复制代码
Tcp_Server::Tcp_Server(io_context& ioc, unsigned short port)
    : io_context(ioc), listen_port(port), acceptor(ioc),
      work_guard(ioc.get_executor()), seq_num(0) {
    acceptor.open(boost::asio::ip::tcp::v4());                       // 打开 IPv4
    acceptor.bind(boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), listen_port));
    acceptor.listen(128);                                            // 监听
    circle_acceptor();                                               // 开始循环接受
}

循环接受(异步 + 智能指针保活 + 轮询分配 io_context):

cpp 复制代码
void Tcp_Server::circle_acceptor() {
    // 使用线程池第 seq_num 个 io_context 创建通信对象
    std::shared_ptr<Server_Communicate_Client> ptr =
        std::make_shared<Server_Communicate_Client>(
            Asio_Iocontext_Thread_Pool::getInstance()->get_Io_context(seq_num), this, seq_num);
    acceptor.async_accept(ptr->get_socket(),
        std::bind(&Tcp_Server::accept_callback, this, std::placeholders::_1, ptr));
}

接受回调

cpp 复制代码
void Tcp_Server::accept_callback(boost::system::error_code ec,
                                 std::shared_ptr<Server_Communicate_Client> ptr) {
    if (ec.value() == 0) {
        ptr->communicate();                       // 1. 开始全双工通信
        std::lock_guard<std::mutex> lock(map_communicate_mutex);
        map_communicate.insert({ptr->get_uuid(), ptr});   // 2. 登记,防止对象被析构
        seq_num = (seq_num + 1) % std::thread::hardware_concurrency(); // 3. 轮询
    } else {
        // 打印连接失败
    }
    circle_acceptor();                            // 4. 继续接受下一个
}

移除通信对象(回调出错时只从 map 移除,不 delete):

cpp 复制代码
void Tcp_Server::delete_map_communicate_member(std::string uuid) {
    std::lock_guard<std::mutex> lock(map_communicate_mutex);
    map_communicate.erase(uuid);
}

设计要点

  • map<uuid, shared_ptr> 保存通信对象 → 保证在服务器存活期间通信对象不会被析构,便于按 uuid 管理/查找。
  • map 被多线程访问 → 用 std::mutex 保护。
  • 出错回调只做"移除",配合 shared_from_this() 保活 → 避免二次析构

服务器通信层

职责 :持有一个通信 socket;循环异步接收;在接收回调里完成粘包切包与合法性校验;把完整消息投递给逻辑层;通过发送队列保证高并发下异步发送的有序与完整。

cpp 复制代码
class Server_Communicate_Client : public std::enable_shared_from_this<Server_Communicate_Client> {
    boost::asio::ip::tcp::socket socket;
    std::string uuid;                       // 唯一标识(来自 boost::uuids)
    std::queue<std::shared_ptr<MsgNode>> queue_send_data;  // 发送队列
    std::mutex queue_send_data_mutex;                       // 保护发送队列
    std::shared_ptr<char[]> recv_data_ptr;  // 原始接收缓冲(最大包长)
    std::shared_ptr<MsgNode> recv_data_node;// 当前正在接收的数据本身
    std::shared_ptr<MsgNode> recv_head_node;// 头部拼装/缓冲节点
    bool head_finish;                       // 头部是否已拼完
    bool receive_error;                     // 是否处于丢弃非法数据状态
    Tcp_Server* server;                     // 反向引用连接层
    int seq_num;                            // 记录所用 io_context/strand 序号
};

构造函数:初始化 socket、分配接收缓冲;创建头部节点;生成唯一 uuid:

cpp 复制代码
Server_Communicate_Client::Server_Communicate_Client(io_context& ioc, Tcp_Server* server, int seq)
    : socket(ioc), server(server), head_finish(false), receive_error(false), seq_num(seq) {
    recv_data_ptr.reset(new char[MAX_MSG_LENGTH + HEAD_TOTAL_LENGTH]);
    recv_head_node = std::make_shared<MsgNode>(HEAD_TOTAL_LENGTH);
    boost::uuids::uuid uid = boost::uuids::random_generator()();
    uuid = boost::uuids::to_string(uid);
}

接收入口(异步读 + 绑定 strand + 伪闭包):

cpp 复制代码
void Server_Communicate_Client::receive() {
    memset(recv_data_ptr.get(), 0, MAX_MSG_LENGTH + HEAD_TOTAL_LENGTH);
    socket.async_read_some(
        boost::asio::buffer(recv_data_ptr.get(), MAX_MSG_LENGTH + HEAD_TOTAL_LENGTH),
        boost::asio::bind_executor(
            Asio_Iocontext_Thread_Pool::getInstance()->get_Strand(seq_num),
            std::bind(&Server_Communicate_Client::read_some_callback, this,
                      std::placeholders::_1, std::placeholders::_2, shared_from_this())));
}

接收回调核心逻辑(切包状态机):
#mermaid-svg-PSiBMQ500W6grR8a{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-PSiBMQ500W6grR8a .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-PSiBMQ500W6grR8a .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-PSiBMQ500W6grR8a .error-icon{fill:#552222;}#mermaid-svg-PSiBMQ500W6grR8a .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-PSiBMQ500W6grR8a .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-PSiBMQ500W6grR8a .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-PSiBMQ500W6grR8a .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-PSiBMQ500W6grR8a .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-PSiBMQ500W6grR8a .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-PSiBMQ500W6grR8a .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-PSiBMQ500W6grR8a .marker{fill:#333333;stroke:#333333;}#mermaid-svg-PSiBMQ500W6grR8a .marker.cross{stroke:#333333;}#mermaid-svg-PSiBMQ500W6grR8a svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-PSiBMQ500W6grR8a p{margin:0;}#mermaid-svg-PSiBMQ500W6grR8a .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-PSiBMQ500W6grR8a .cluster-label text{fill:#333;}#mermaid-svg-PSiBMQ500W6grR8a .cluster-label span{color:#333;}#mermaid-svg-PSiBMQ500W6grR8a .cluster-label span p{background-color:transparent;}#mermaid-svg-PSiBMQ500W6grR8a .label text,#mermaid-svg-PSiBMQ500W6grR8a span{fill:#333;color:#333;}#mermaid-svg-PSiBMQ500W6grR8a .node rect,#mermaid-svg-PSiBMQ500W6grR8a .node circle,#mermaid-svg-PSiBMQ500W6grR8a .node ellipse,#mermaid-svg-PSiBMQ500W6grR8a .node polygon,#mermaid-svg-PSiBMQ500W6grR8a .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-PSiBMQ500W6grR8a .rough-node .label text,#mermaid-svg-PSiBMQ500W6grR8a .node .label text,#mermaid-svg-PSiBMQ500W6grR8a .image-shape .label,#mermaid-svg-PSiBMQ500W6grR8a .icon-shape .label{text-anchor:middle;}#mermaid-svg-PSiBMQ500W6grR8a .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-PSiBMQ500W6grR8a .rough-node .label,#mermaid-svg-PSiBMQ500W6grR8a .node .label,#mermaid-svg-PSiBMQ500W6grR8a .image-shape .label,#mermaid-svg-PSiBMQ500W6grR8a .icon-shape .label{text-align:center;}#mermaid-svg-PSiBMQ500W6grR8a .node.clickable{cursor:pointer;}#mermaid-svg-PSiBMQ500W6grR8a .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-PSiBMQ500W6grR8a .arrowheadPath{fill:#333333;}#mermaid-svg-PSiBMQ500W6grR8a .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-PSiBMQ500W6grR8a .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-PSiBMQ500W6grR8a .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-PSiBMQ500W6grR8a .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-PSiBMQ500W6grR8a .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-PSiBMQ500W6grR8a .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-PSiBMQ500W6grR8a .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-PSiBMQ500W6grR8a .cluster text{fill:#333;}#mermaid-svg-PSiBMQ500W6grR8a .cluster span{color:#333;}#mermaid-svg-PSiBMQ500W6grR8a div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-PSiBMQ500W6grR8a .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-PSiBMQ500W6grR8a rect.text{fill:none;stroke-width:0;}#mermaid-svg-PSiBMQ500W6grR8a .icon-shape,#mermaid-svg-PSiBMQ500W6grR8a .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-PSiBMQ500W6grR8a .icon-shape p,#mermaid-svg-PSiBMQ500W6grR8a .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-PSiBMQ500W6grR8a .icon-shape .label rect,#mermaid-svg-PSiBMQ500W6grR8a .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-PSiBMQ500W6grR8a .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-PSiBMQ500W6grR8a .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-PSiBMQ500W6grR8a :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 否




未拼完
不足

非法
合法
未收完
已收完


read_some_callback 收到 byte_num 字节
ec.value==0?
出错: delete_map / 投递错误消息
receive_error 丢弃非法数据?
本次是否丢弃完非法字节
不够: 更新剩余非法字节继续 receive
相等: 恢复合法状态 receive
部分非法: 丢弃前段, 继续处理剩余
while byte_num>0
return
head_finish? 头部拼装
拼完 4 字节头?
拷入 recv_head_node 后 receive 继续收
解析 data_id/data_len 并转主机字节序
data_len>MAX 或 id>MAX?
投递错误消息到逻辑层 + 置 receive_error 丢弃后续
接收数据本身到 recv_data_node
本次是否收完整包
拷入节点后 receive 继续收
投递 LogicNode 到逻辑层
还有剩余字节?
再次 receive 继续监听

要点:读回调内永远再次调用 receive() → 保证服务器"一直接收"客户端数据,实现全双工

发送入口(队列化串行异步发送):

cpp 复制代码
void Server_Communicate_Client::send(char* send_data_ptr, int data_len, short data_id) {
    bool send_nofinish = false;
    std::lock_guard<std::mutex> lock(queue_send_data_mutex);
    if (!queue_send_data.empty()) send_nofinish = true;      // 上次还没发完
    if (queue_send_data.size() > QUEUE_SEND_SIZE) return;    // 队列已满则丢弃
    queue_send_data.push(std::make_shared<MsgNode>(send_data_ptr, data_len, data_id));
    if (send_nofinish) return;                               // 正在发,不能并发发起 async_write

    boost::asio::async_write(
        socket,
        boost::asio::buffer(queue_send_data.front()->data, queue_send_data.front()->total_lenth),
        boost::asio::bind_executor(Asio_Iocontext_Thread_Pool::getInstance()->get_Strand(seq_num),
            std::bind(&Server_Communicate_Client::write_callback, this,
                      std::placeholders::_1, std::placeholders::_2, shared_from_this())));
}

发送回调(发完队首 → 若队列仍有数据继续发下一个):

cpp 复制代码
void Server_Communicate_Client::write_callback(error_code ec, size_t byte_num, shared_ptr self) {
    if (ec.value() == 0) {
        std::lock_guard<std::mutex> lock(queue_send_data_mutex);
        queue_send_data.pop();                               // 队首已发完
        if (!queue_send_data.empty())                        // 继续发下一个节点
            boost::asio::async_write(socket, buffer(queue_send_data.front()->...),
                std::bind(&...::write_callback, this, _1, _2, self));
    } else if (eof / connection_reset) {
        server->delete_map_communicate_member(uuid);
    } else {
        Logic_System::getInstance()->post_LogicNode(/*SEND_ERROR*/);
        server->delete_map_communicate_member(uuid);
    }
}

为什么用队列 :同一 socket 若同时存在多个进行中的 async_write,底层发送会交错、无法保证时序。采用"队列缓存 + 每次只发队首 + 发完再发下一个 ",从机制上保证发送有序且完整


TLV 数据缓冲区类

作用:自定义收发缓冲,把"原始数据"包装成 TLV 包(发送)或按长度承接接收数据(接收)。

字段:

  • char* data:数据首地址;
  • short id:数据 ID(默认 -1 表示头部节点,业务 ID 1001--2048);
  • short total_lenth:应收/应发总长;
  • short cur_lenth:已收/已发当前长度。

发送构造(把裸数据封装成 TLV,头部转网络字节序):

cpp 复制代码
MsgNode(char* msg, short msg_len, short id)
    : total_lenth(msg_len + HEAD_TOTAL_LENGTH), cur_lenth(0), id(id) {
    data = new char[total_lenth + 1]();
    short network_id = boost::asio::detail::socket_ops::host_to_network_short(id);
    memcpy(data, &network_id, ID_LENGTH);                          // [0..2) ID
    short network_msg_len = boost::asio::detail::socket_ops::host_to_network_short(msg_len);
    memcpy(data + ID_LENGTH, &network_msg_len, REAL_MSG_LENGTH);   // [2..4) 长度
    memcpy(data + HEAD_TOTAL_LENGTH, msg, msg_len);                // [4..)  数据
    data[total_lenth] = '\0';
}

接收构造

cpp 复制代码
MsgNode(short data_len, short id = -1)
    : total_lenth(data_len), cur_lenth(0), id(id) {
    data = new char[data_len + 1]();
    data[data_len] = '\0';
}

常用方法Clear()(清零并重置 cur_lenth)、get_ID()get_DATA()get_LENGTH()、析构 delete[] data;友元重载 << 打印数据本身。

发送时数据从"主机字节序"转"网络字节序"写入头部;接收切包时用 network_to_host_short 转回主机字节序再使用。


服务器逻辑处理层

CRTP 线程安全单例

cpp 复制代码
template<typename T>
class Singleton {
    static std::shared_ptr<T> instance;     // 全局唯一实例
    Singleton(const Singleton&) = delete;   // 禁拷贝
    Singleton& operator=(const Singleton&) = delete;
protected:
    Singleton() = default;
public:
    static std::shared_ptr<T> getInstance() {
        static std::once_flag once;         // 保证 call_once 只真正调用一次
        std::call_once(once, [&]() { instance = std::shared_ptr<T>(new T); });
        return instance;
    }
};
template<typename T> std::shared_ptr<T> Singleton<T>::instance = nullptr;

特点 :懒汉式、std::call_once 保证多线程下只创建一次、子类继承获得单例能力(CRTP)。

逻辑数据节点

cpp 复制代码
class LogicNode {
    friend class Logic_System;
    std::shared_ptr<Server_Communicate_Client> communicate_ptr; // 该消息来自哪个通信对象
    std::shared_ptr<MsgNode> recv_message;                      // TLV 消息(含 ID 与数据)
public:
    LogicNode(std::shared_ptr<Server_Communicate_Client> c, std::shared_ptr<MsgNode> m)
        : communicate_ptr(c), recv_message(m) {}
};

逻辑层只依赖"通信对象 + 消息",从而与具体 socket 细节解耦。

逻辑处理系统
cpp 复制代码
class Logic_System : public Singleton<Logic_System> {
    friend class Singleton<Logic_System>;
    std::queue<std::shared_ptr<LogicNode>> queue_logicnode;   // 待处理队列
    std::mutex queue_logicnode_lock;
    std::condition_variable queue_logicnode_cond;             // 队列空则阻塞逻辑线程
    std::thread logic_thread;                                 // 逻辑工作线程(单线程)
    std::map<short, ID_CALLBACK_FUNC> map_id_func;            // ID -> 处理函数
    std::atomic<bool> server_stop;
    // 构造函数: register_id_callback() + 启动 logic_thread
    // 析构: server_stop=true; notify_all; join 逻辑线程
public:
    void post_LogicNode(const std::shared_ptr<LogicNode> message); // 通信层调用
};

注册表类型typedef std::function<void(std::shared_ptr<LogicNode>)> ID_CALLBACK_FUNC;

逻辑线程循环

cpp 复制代码
void Logic_System::work_deal_message() {
    while (1) {
        std::unique_lock<std::mutex> lock(queue_logicnode_lock);
        queue_logicnode_cond.wait(lock, [this]() {
            return server_stop || !queue_logicnode.empty();   // 空队列+未停止 → 阻塞
        });
        if (server_stop) { while(!queue_logicnode.empty()){/* 处理剩余 */} break; }
        auto node = queue_logicnode.front(); queue_logicnode.pop();
        lock.unlock();
        auto it = map_id_func.find(node->recv_message->get_ID());
        if (it == map_id_func.end()) continue;                // 未注册则忽略
        it->second(node);                                     // 执行对应处理
    }
}

注册处理函数(示例)

cpp 复制代码
map_id_func.insert({RETURN_MESSAGE, [](std::shared_ptr<LogicNode> node) {
    node->communicate_ptr->send(node->recv_message->get_DATA(),
                                node->recv_message->get_LENGTH(), SEND_MESSAGE);
}});
// ERROR_MESSAGE_LENGTH / ERROR_ID_NUM / SEND_ERROR / RECEIVE_ERROR 等类似

投放消息接口

cpp 复制代码
void Logic_System::post_LogicNode(const std::shared_ptr<LogicNode> message) {
    std::unique_lock<std::mutex> lock(queue_logicnode_lock);
    queue_logicnode.push(message);
    queue_logicnode_cond.notify_one();   // 唤醒可能阻塞的逻辑线程
}

为什么解耦 :通信层收到完整包后立即返回继续接收,逻辑处理交给单线程逻辑层 → ①通信不被慢逻辑阻塞;②单线程处理天然避免数据竞争;③便于扩展不同业务(只需注册新的 ID 处理函数)。

3.粘包与自定义应用层 TLV 协议

TCP 是字节流,需应用层定义包格式(本项目用 TLV = 消息ID(2B) + 消息长度(2B) + 消息本身,头部共 4 字节 HEAD_TOTAL_LENGTH)。

  • 发送:封装成 TLV 再发(头部字节序转网络序)。
  • 接收切包 两种思路:
    1. async_read_some 不定长读取 + 回调内手动切包(复杂);
    2. async_read/read 先收固定长度头部,解析出 length 后再收固定长度消息体,循环(简单,本项目采用思路 2 于客户端,服务器则用思路 1 的完整切包实现)。
  • 同时校验 data_len > MAX_MSG_LENGTHdata_id > MAX_LOGIC_ID 丢弃非法包。
相关推荐
阿明61 小时前
基础编辑工具使用【Linux】
linux·运维·服务器
Tinyundg1 小时前
关于Termux的基本使用——安卓手机爆改Linux服务器?
linux·运维·服务器·智能手机·个人开发
g10565591391 小时前
华为 OceanStor 基础使用入门指南
服务器·数据库·性能优化
鱼很腾apoc1 小时前
【Linux】第15期 讲解IP网络层核心原理与机制
linux·服务器·网络协议·学习·tcp/ip·ip·网络层
AbandonForce1 小时前
简谈线程池
开发语言·c++·算法
zx_741484811 小时前
【Linux入门】常用基本命令(一)——帮助、文件目录操作、时间日期与用户组管理
linux·服务器
AutumnWind04201 小时前
【Shell 知识速查表】
linux
Mr_liu_6661 小时前
ns3-gym例子解析_基础例子与wifi例子_DQN(3)
开发语言·c++·python·dqn·ns3
风哥2号1 小时前
数据库教程FGMT04‑生产环境Linux+Oracle19c RAC集群安装配置与项目实战
linux·数据库