C++ 网络编程(22) beast网络库实现websocket服务器

一、const.h(全局头文件与命名空间定义)

作用:汇总项目所有依赖头文件,定义命名空间别名,防止头文件重复包含,作为项目基础头文件

cpp 复制代码
#pragma once
// 防止头文件被重复包含,保证全局只引入一次

// 标准输入输出头文件,用于控制台日志打印
#include <iostream>
// Boost.Asio核心库,提供异步网络IO、套接字、事件循环支持
#include<boost/asio.hpp>
// Boost.Beast库,封装WebSocket/HTTP协议,实现WebSocket通信核心逻辑
#include<boost/beast.hpp>
// 内存操作相关头文件,项目预留使用
#include<memory.h>
// Boost UUID库,用于生成每个连接的唯一标识ID
#include<boost/uuid/uuid.hpp>
#include<boost/uuid/uuid_io.hpp>
#include<boost/uuid/uuid_generators.hpp>
// 队列容器,用于存储待发送的消息,保证消息顺序发送
#include<queue>
// 互斥锁,保证多线程环境下消息队列操作的线程安全
#include<mutex>

// 命名空间别名,简化代码书写,避免冗长的全命名空间写法
namespace net = boost::asio;
namespace beast = boost::beast;
// 直接引入WebSocket相关命名空间,方便直接调用相关类和函数
using namespace boost::beast;
using namespace boost::beast::websocket;

二、Connection.h(单个WebSocket连接类头文件)

作用:封装单个客户端WebSocket连接的所有功能,定义类成员变量和成员函数接口,继承智能指针相关类,解决异步回调生命周期问题

cpp 复制代码
#pragma once
// 引入全局基础头文件
#include"const.h"

// 单个WebSocket连接处理类
// 继承std::enable_shared_from_this,用于在异步回调中获取自身的shared_ptr,防止对象提前销毁导致野指针
class Connection :public std::enable_shared_from_this<Connection>
{
public:
    // 构造函数
    // 参数:ioc - 异步IO上下文,引用传递,复用全局IO上下文
    Connection(net::io_context& ioc);

    // 获取当前连接的唯一UUID标识
    std::string GetUid();

    // 获取底层TCP套接字,供服务器监听端接收连接使用
    net::ip::tcp::socket& GetSocket();

    // 异步执行WebSocket握手,将TCP连接升级为WebSocket连接
    void AsyncAccept();

    // 启动连接,开始监听客户端消息,进入读写循环
    void Start();

    // 异步发送消息给客户端
    // 参数:msg - 要发送的字符串消息
    void AsyncSend(std::string msg);

private:
    // WebSocket流对象,封装底层TCP流,unique_ptr保证独占所有权,禁止拷贝
    std::unique_ptr<stream<tcp_stream>> _ws_ptr;
    // 当前连接的唯一UUID字符串,用于连接管理和标识
    std::string _uuids;
    // 全局IO上下文引用,复用服务端事件循环
    net::io_context& _ioc;
    // 接收数据缓冲区,Beast提供的高效缓冲区,存储客户端发送的消息
    flat_buffer _recv_buffer;
    // 消息发送队列,缓存待发送消息,避免并发发送冲突
    std::queue<std::string> _send_que;
    // 发送队列互斥锁,保证多线程操作队列时的线程安全
    std::mutex _send_mtx;
};

三、Connection.cpp(单个WebSocket连接类实现)

作用:实现Connection类的所有成员函数,处理WebSocket握手、消息读取、消息发送、连接维护等核心逻辑

cpp 复制代码
#include "Connection.h"
// 引入连接管理器,用于注册和移除当前连接
#include"ConnectionMgr.h"

// 构造函数实现
// 初始化IO上下文引用,创建WebSocket流对象,生成唯一UUID
Connection::Connection(net::io_context& ioc)
    : _ioc(ioc),
    // 创建WebSocket流,绑定strand保证异步回调单线程执行,避免多线程冲突
    _ws_ptr(std::make_unique<stream<tcp_stream>>(make_strand(ioc)))
{
    // 创建UUID生成器,生成随机唯一ID
    boost::uuids::random_generator generator;
    boost::uuids::uuid uuid = generator();
    // 将UUID转换为字符串,存储为当前连接标识
    _uuids = boost::uuids::to_string(uuid);
}

// 获取当前连接唯一UUID的实现
std::string Connection::GetUid()
{
    return  _uuids;
}

// 获取底层TCP套接字实现
// 通过get_lowest_layer获取WebSocket流最底层的TCP套接字
net::ip::tcp::socket& Connection::GetSocket()
{
    return boost::beast::get_lowest_layer(*_ws_ptr).socket();
}

// 异步WebSocket握手实现
// 等待客户端发起握手请求,完成协议升级
void Connection::AsyncAccept()
{
    // 获取自身智能指针,保证异步回调期间对象不被销毁
    auto self = shared_from_this();
    // 异步接受WebSocket握手,回调函数处理握手结果
    _ws_ptr->async_accept([self](boost::system::error_code err) {
        try {
            // 错误码为空,代表握手成功
            if (!err)
            {
                // 将当前连接加入全局连接管理器,统一管理
                ConnectionMgr::GetInstance().AddConnection(self);
                // 握手成功,启动连接,开始监听消息
                self->Start();
            }
            else
            {
                // 握手失败,打印错误信息
                std::cout << "websocket accept failed, err is " << err.what() << std::endl;
            }
        }
        catch (std::exception& exp)
        {
            // 捕获异常,防止程序崩溃,打印异常信息
            std::cout << "websocket async accept exception is " << exp.what();
        }
        });
}

// 启动连接,进入消息读取循环
void Connection::Start()
{
    // 获取自身智能指针
    auto self = shared_from_this();
    // 异步读取客户端发送的消息,数据存入接收缓冲区
    _ws_ptr->async_read(_recv_buffer, [self](error_code err, size_t buffer_bytes) {
        try {
            // 读取出现错误,代表连接断开或异常
            if (err)
            {
                std::cout << "websocket async read error is " << err.what();
                // 从连接管理器移除当前连接,释放资源
                ConnectionMgr::GetInstance().RmvConnection(self->GetUid());
                return;
            }
            // 设置发送消息类型与客户端发送类型一致(文本/二进制)
            self->_ws_ptr->text(self->_ws_ptr->got_text());
            // 将缓冲区数据转换为字符串,方便处理
            std::string recv_data = boost::beast::buffers_to_string(self->_recv_buffer.data());
            // 清空接收缓冲区,准备下一次读取
            self->_recv_buffer.consume(self->_recv_buffer.size());
            // 打印接收到的客户端消息
            std::cout << "websocket receive msg is " << recv_data << std::endl;
            // 将接收到的消息回传给客户端(回声模式)
            self->AsyncSend(std::move(recv_data));
            // 递归调用Start,持续监听客户端后续消息
            self->Start();
        }
        catch (std::exception& exp)
        {
            // 捕获异常,打印信息并移除连接
            std::cout << "exception is " << exp.what() << std::endl;
            ConnectionMgr::GetInstance().RmvConnection(self->GetUid());
        }
        });
}

// 异步发送消息实现
// 通过队列保证消息顺序发送,避免并发发送导致的问题
void Connection::AsyncSend(std::string msg)
{
    {
        // 加锁,保证队列操作的线程安全
        std::lock_guard<std::mutex>lck_guard(_send_mtx);
        // 获取当前队列长度
        int que_len = _send_que.size();
        // 将消息加入发送队列
        _send_que.push(msg);
        // 如果队列原本不为空,说明已有消息在发送,直接返回,等待上一条发送完成
        if (que_len > 0)
        {
            return;
        }
    }

    // 获取自身智能指针
    auto self = shared_from_this();
    // 异步发送消息,将字符串转为asio缓冲区发送
    _ws_ptr->async_write(boost::asio::buffer(msg.c_str(), msg.length()),
        [self](error_code err, std::size_t nsize) {
            try {
                // 发送失败,打印错误并移除连接
                if (err)
                {
                    std::cout << "async_send err is" << err.what() << std::endl;
                    ConnectionMgr::GetInstance().RmvConnection(self->GetUid());
                    return;
                }
                std::string send_msg;
                {
                    // 加锁操作发送队列
                    std::lock_guard<std::mutex> lck_gurad(self->_send_mtx);
                    // 移除已发送完成的消息
                    self->_send_que.pop();
                    // 队列为空,无后续消息,直接返回
                    if (self->_send_que.empty())
                    {
                        return;
                    }
                    // 获取队列中下一条待发送消息
                    send_msg = self->_send_que.front();
                    // 发送下一条消息
                    self->AsyncSend(std::move(send_msg));
                }
            }
            catch (std::exception& exp)
            {
                // 捕获异常,处理连接
                std::cout << "exception is " << exp.what() << std::endl;
                ConnectionMgr::GetInstance().RmvConnection(self->GetUid());
            }
        });
}

四、ConnectionMgr.h(连接管理器头文件)

作用:单例模式实现全局连接管理器,统一管理所有客户端连接,提供添加、移除连接接口

cpp 复制代码
#pragma once
// 引入全局头文件
#include"const.h"
// Boost无序哈希表,用于存储连接,查询效率更高
#include"boost/unordered_map.hpp"
// 引入连接类
#include "Connection.h"

// 连接管理类,采用单例模式,全局唯一
class ConnectionMgr
{
public:
    // 获取单例实例,静态局部变量实现,线程安全
    static ConnectionMgr& GetInstance();

    // 添加新连接到管理器
    // 参数:conptr - 连接对象的智能指针
    void AddConnection(std::shared_ptr<Connection>conptr);

    // 根据UUID移除连接
    // 参数:id - 连接的唯一UUID
    void RmvConnection(std::string);

public:
    // 禁用拷贝构造函数,防止单例被拷贝
    ConnectionMgr(const ConnectionMgr&) = delete;
    // 禁用赋值运算符,保证单例唯一性
    ConnectionMgr& operator = (const ConnectionMgr&) = delete;

    // 构造函数私有化,禁止外部创建实例
    ConnectionMgr();

    // 存储所有活跃连接,key为UUID,value为连接智能指针
    boost::unordered_map<std::string, std::shared_ptr<Connection>> _map_cons;
};

五、ConnectionMgr.cpp(连接管理器实现)

作用:实现单例连接管理器的具体功能,完成连接的添加、移除操作

cpp 复制代码
#include "ConnectionMgr.h"

// 获取单例实例实现
// 静态局部变量在程序运行期间只初始化一次,保证全局唯一
ConnectionMgr& ConnectionMgr::GetInstance()
{
    static ConnectionMgr instance;
    return instance;
}

// 添加连接实现
// 将连接的UUID作为键,智能指针作为值存入哈希表
void ConnectionMgr::AddConnection(std::shared_ptr<Connection> conptr)
{
    _map_cons[conptr->GetUid()] = conptr;
}

// 移除连接实现
// 根据UUID删除哈希表中对应的连接项
void ConnectionMgr::RmvConnection(std::string id)
{
    _map_cons.erase(id);
}

// 构造函数实现,空实现
ConnectionMgr::ConnectionMgr()
{
}

六、WebSocketServer.h(服务器核心类头文件)

作用:定义WebSocket服务器核心类,负责监听端口、异步接收客户端TCP连接

cpp 复制代码
#pragma once
// 引入全局头文件
#include"const.h"
// 引入连接管理器
#include"ConnectionMgr.h"

// WebSocket服务器核心类
class WebSocketServer
{
public:
    // 禁用拷贝构造,防止服务器对象被拷贝
    WebSocketServer(const WebSocketServer&) = delete;
    // 禁用赋值运算符
    WebSocketServer& operator = (const WebSocketServer&) = delete;

    // 构造函数
    // 参数:ioc - 全局IO上下文;port - 服务器监听端口
    WebSocketServer(net::io_context& ioc, unsigned short port);

    // 开始异步监听客户端连接
    void StartAccept();

private:
    // TCP接收器,负责监听端口,接收客户端TCP连接
    net::ip::tcp::acceptor _acceptor;
    // 全局IO上下文引用
    net::io_context& _ioc;
};

七、WebSocketServer.cpp(服务器核心类实现)

作用:实现服务器监听、接收客户端连接的逻辑,循环接收新连接,分配给对应连接对象处理

cpp 复制代码
#include "WebSocketServer.h"

// 构造函数实现
// 初始化接收器,绑定指定端口,监听所有网卡
WebSocketServer::WebSocketServer(net::io_context& ioc, unsigned short port)
    :_ioc(ioc),
    // 绑定IPv4地址和指定端口,开启监听
    _acceptor(ioc, net::ip::tcp::endpoint(net::ip::tcp::v4(), port))
{
    // 打印服务器启动信息,显示监听端口
    std::cout << "Server start on port :" << port << std::endl;
}

// 开始接收客户端连接实现
void WebSocketServer::StartAccept()
{
    // 创建新的连接对象,每个客户端对应一个独立连接对象
    auto con_ptr = std::make_shared<Connection>(_ioc);
    // 异步接收TCP连接,回调处理连接结果
    _acceptor.async_accept(con_ptr->GetSocket(), [this, con_ptr](error_code err) {
        try {
            // 接收连接成功
            if (!err)
            {
                // 执行WebSocket握手,升级连接
                con_ptr->AsyncAccept();
            }
            else
            {
                // 接收连接失败,打印错误信息
                std::cout << "acceptor async_accept failed, err is" << err.what() << std::endl;
            }
            // 递归调用自身,持续监听下一个客户端连接
            StartAccept();
        }
        catch (std::exception& exp)
        {
            // 捕获异常,防止服务器崩溃
            std::cout << "async_accept error is " << exp.what() << std::endl;
        }
        });
}

八、beast_websocket.cpp(程序主入口文件)

作用:程序执行入口,初始化IO上下文,启动服务器,运行事件循环

cpp 复制代码
#include"const.h"
// 引入服务器核心类
#include"WebSocketServer.h"

int main()
{
    // 创建Boost.Asio核心IO上下文,负责所有异步事件调度
    net::io_context ioc;
    // 创建WebSocket服务器对象,监听10086端口
    WebSocketServer server(ioc, 10086);
    // 启动服务器,开始监听客户端连接
    server.StartAccept();
    // 运行IO上下文事件循环,阻塞程序,处理所有异步网络事件
    ioc.run();
    return 0;
}

代码整体说明

  • 核心功能:基于Boost.Beast实现WebSocket服务器,监听10086端口,接收客户端连接,完成WebSocket握手,实现消息回声(接收客户端消息后原路返回),支持多客户端同时连接

  • 设计模式:单例模式(连接管理器)、智能指针管理对象生命周期、异步IO模型

  • 线程安全:通过互斥锁保护消息队列,strand保证异步回调单线程执行

  • 运行流程:主函数启动→服务器监听端口→接收客户端TCP连接→升级为WebSocket连接→消息收发循环→连接断开后移除资源

相关推荐
心前阳光2 小时前
Mirror网络库插件使用4
java·linux·网络·unity·c#·游戏引擎
西野.xuan2 小时前
【effective c++】条款四十三:学习处理模版化基类内的名称
java·c++·学习
jimy12 小时前
字节流(XML、JSON、文件、网络、图像、加密…)必须用无符号语义unsigned char
xml·c语言·网络·json
乾元2 小时前
本地大模型:如何在内网部署 Llama/Qwen 等安全增强模型
运维·网络·人工智能·安全·机器学习·llama·安全架构
8Qi82 小时前
LeetCode61. 旋转链表
c语言·数据结构·c++·算法·leetcode·链表·力扣
被AI抢饭碗的人2 小时前
高并发内存池实现
开发语言·c++
灰阳阳2 小时前
Docker-网络类型详解
网络·docker·容器
.小小陈.2 小时前
C++进阶7:深入理解哈希表,从原理到 C++ 实践
开发语言·c++·学习·哈希算法
Johnstons2 小时前
当网络运维遇上全流量回溯:一次关于「看得见」的实践
运维·网络