基于Boost WebSocket示例客户端,如何添加事件并输出消息结果?
实现Boost WebSocket客户端的事件驱动处理(类似socket.io的
socket.on) 你已经搞定了Boost WebSocket的连接,接下来要实现类似socket.io的事件监听,核心思路是自己封装一个事件订阅-分发机制——因为Boost WebSocket本身只提供基础的消息收发能力,没有内置的事件系统。咱们一步步来实现:
1. 先约定消息格式
socket.io的事件本质是带标识的结构化消息,所以咱们得先和服务端约定好消息格式,最通用的方式是用JSON封装事件名和数据,比如:
{"event": "SomeEvent", "data": "Hello from server!"}
这样客户端收到消息后,就能解析出对应的事件名和数据,再触发对应的回调逻辑。
2. 封装事件处理器类
咱们写一个轻量的EventHandler类,用来管理事件的订阅和触发,模拟socket.io的on/emit逻辑:
#include <unordered_map> #include <vector> #include <functional> #include <nlohmann/json.hpp> // 用这个流行的JSON库解析消息,直接下载头文件即可使用 using json = nlohmann::json; using EventCallback = std::function<void(const json&)>; class EventHandler { public: // 订阅事件,对应socket.io的socket.on void on(const std::string& event_name, EventCallback callback) { m_event_map[event_name].push_back(std::move(callback)); } // 触发事件,收到对应消息时调用 void emit(const std::string& event_name, const json& data) { auto it = m_event_map.find(event_name); if (it != m_event_map.end()) { // 遍历所有订阅该事件的回调并执行 for (const auto& callback : it->second) { callback(data); } } } private: // 存储事件名到回调列表的映射 std::unordered_map<std::string, std::vector<EventCallback>> m_event_map; };
3. 集成到Boost WebSocket客户端
把事件处理器和Boost WebSocket的消息接收逻辑结合起来,修改你已有的客户端代码:
#include <boost/beast.hpp> #include <boost/asio.hpp> #include <iostream> namespace beast = boost::beast; namespace websocket = beast::websocket; namespace net = boost::asio; using tcp = boost::asio::ip::tcp; class WsClient { public: WsClient(net::io_context& ioc, tcp::resolver::results_type endpoints) : m_ws(ioc) { connect(endpoints); } // 对外暴露事件订阅接口,让外部可以像socket.io一样调用 void on(const std::string& event, EventCallback callback) { m_event_handler.on(event, callback); } private: websocket::stream<tcp::socket> m_ws; beast::flat_buffer m_buffer; EventHandler m_event_handler; void connect(tcp::resolver::results_type endpoints) { net::async_connect(m_ws.next_layer(), endpoints, [this](beast::error_code ec, tcp::endpoint) { if (ec) { std::cerr << "连接失败: " << ec.message() << std::endl; return; } // 执行WebSocket握手 m_ws.async_handshake("localhost", "/", [this](beast::error_code ec) { if (ec) { std::cerr << "握手失败: " << ec.message() << std::endl; return; } // 握手成功后开始监听消息 receive_message(); }); }); } void receive_message() { m_ws.async_read(m_buffer, [this](beast::error_code ec, std::size_t bytes_transferred) { boost::ignore_unused(bytes_transferred); if (ec) { std::cerr << "读取消息失败: " << ec.message() << std::endl; return; } // 把收到的二进制消息转换成字符串 std::string msg = beast::buffers_to_string(m_buffer.data()); m_buffer.consume(m_buffer.size()); // 清空缓冲区准备下一次接收 try { // 解析JSON格式的消息 json parsed_msg = json::parse(msg); std::string event_name = parsed_msg["event"]; json event_data = parsed_msg["data"]; // 触发对应的事件回调,把数据传给订阅者 m_event_handler.emit(event_name, event_data); } catch (const std::exception& e) { std::cerr << "解析消息出错: " << e.what() << std::endl; } // 递归调用,持续监听下一条消息 receive_message(); }); } };
4. 最终使用示例
现在你就可以像用socket.io那样订阅事件,把消息输出到控制台了:
int main() { try { net::io_context ioc; tcp::resolver resolver(ioc); // 替换成你的服务端地址和端口 auto endpoints = resolver.resolve("localhost", "8080"); WsClient client(ioc, endpoints); // 订阅SomeEvent事件,收到消息后打印到控制台 client.on("SomeEvent", [](const json& data) { std::cout << "收到SomeEvent事件: " << data.dump() << std::endl; }); // 可以订阅多个不同的事件 client.on("ChatMessage", [](const json& data) { std::cout << "新聊天消息: " << data["content"] << std::endl; }); // 启动IO循环,等待事件触发 ioc.run(); } catch (const std::exception& e) { std::cerr << "程序出错: " << e.what() << std::endl; return 1; } return 0; }
额外提示
- 如果不想用JSON,也可以自定义简单的分隔格式(比如用
|分隔事件名和数据),但JSON的扩展性更好; - 可以给
EventHandler扩展更多功能,比如取消订阅(类似socket.off)、只触发一次的事件(类似socket.once),只需要在映射里管理回调的生命周期即可。
内容的提问来源于stack exchange,提问作者user4150916
相关产品推荐
相关产品推荐

