【在线五子棋对战】二、WebSocket协议与服务器搭建
WebSocket++ 是一个跨平台、头文件为主的 C++ 库,实现了 RFC6455 标准,底层使用 Boost.Asio 或 C++11 标准库的 Asio。支持 HTTP 和 WebSocket(同时监听)支持客户端和服务端模式事件驱动(通过回调函数处理连接、消息、关闭等)线程安全(部分接口)文档: http://docs.websocketpp.org/
上一篇我们完成了项目环境的搭建,这一篇将深入 WebSocket 协议的原理,并使用 WebSocket++ 库搭建一个既能处理 HTTP 又能处理 WebSocket 的服务器,为后续的五子棋业务做好准备。
1. 为什么需要 WebSocket?
传统 HTTP 协议是“请求-响应”模式:客户端主动发起请求,服务器被动返回响应。服务器无法主动向客户端推送消息。
对于五子棋对战这类实时性要求高的场景,如果使用 HTTP 轮询(客户端每隔几秒询问一次),会有以下问题:
- 实时性差(延迟取决于轮询间隔)
- 资源浪费(大量无效请求)
- 服务器压力大
WebSocket 应运而生,它:
- 全双工:客户端和服务端可以随时向对方发送消息
- 持久连接:握手成功后保持长连接,无需重复建立 TCP 连接
- 低延迟:没有 HTTP 头部冗余,适合频繁交互
2. WebSocket 协议原理
2.1 握手过程
WebSocket 的握手通过 HTTP 请求升级协议完成。步骤如下:
- 客户端发送一个特殊的 HTTP GET 请求,包含
Upgrade: websocket和Connection: Upgrade头部。 - 服务器验证后返回
101 Switching Protocols状态码,表示同意升级。 - 之后双方就在同一 TCP 连接上使用 WebSocket 帧格式通信。
客户端握手请求示例:
GET /chat HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
服务器成功响应:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
2.2 数据帧格式(简略)
WebSocket 传输的数据被分割成帧。我们主要关注几个字段:
FIN:是否为最后一帧(消息可能分多帧)opcode:帧类型(文本、二进制、连接关闭、ping/pong)Mask&Masking-Key:客户端发送给服务器的数据必须掩码,服务端发送不需要Payload length:载荷长度(7位、7+16位、7+64位扩展)Payload Data:实际数据(文本或二进制)
实际开发中,WebSocket++ 库已封装好帧解析与生成,我们无需手动处理这些细节。
3. WebSocket++ 库介绍
WebSocket++ 是一个跨平台、头文件为主的 C++ 库,实现了 RFC6455 标准,底层使用 Boost.Asio 或 C++11 标准库的 Asio。
主要特点:
- 支持 HTTP 和 WebSocket(同时监听)
- 支持客户端和服务端模式
- 事件驱动(通过回调函数处理连接、消息、关闭等)
- 线程安全(部分接口)
官方资源:
- GitHub: https://github.com/zaphoyd/websocketpp
- 文档: http://docs.websocketpp.org/
4. 环境安装(回顾)
上一篇我们已经安装过,再确认一下:
# Ubuntu 下
sudo apt install libboost-all-dev
git clone https://github.com/zaphoyd/websocketpp.git
cd websocketpp
mkdir build && cd build
cmake -DCMAKE_INSTALL_PREFIX=/usr ..
sudo make install
验证安装:ls /usr/include/websocketpp/ 应显示大量头文件。
5. 编写第一个 WebSocket++ 服务器
我们将创建一个 echo 服务器:客户端发来什么消息,服务器原样返回。
5.1 基础代码(echo_server.cpp)
#include <iostream>
#include <websocketpp/config/asio_no_tls.hpp>
#include <websocketpp/server.hpp>
using namespace std;
typedef websocketpp::server<websocketpp::config::asio> wsserver_t;
typedef wsserver_t::message_ptr message_ptr;
void on_open(wsserver_t* srv, websocketpp::connection_hdl hdl) {
cout << "新连接建立" << endl;
}
void on_close(wsserver_t* srv, websocketpp::connection_hdl hdl) {
cout << "连接关闭" << endl;
}
void on_message(wsserver_t* srv, websocketpp::connection_hdl hdl, message_ptr msg) {
string payload = msg->get_payload();
cout << "收到消息: " << payload << endl;
// 原样返回
srv->send(hdl, payload, msg->get_opcode());
}
int main() {
wsserver_t server;
// 关闭所有日志(可选)
server.set_access_channels(websocketpp::log::alevel::none);
// 初始化 asio
server.init_asio();
// 注册回调
server.set_open_handler(bind(&on_open, &server, placeholders::_1));
server.set_close_handler(bind(&on_close, &server, placeholders::_1));
server.set_message_handler(bind(&on_message, &server, placeholders::_1, placeholders::_2));
// 监听端口 9002
server.listen(9002);
server.start_accept();
cout << "WebSocket 服务器启动,端口 9002" << endl;
server.run();
return 0;
}
5.2 编译与测试
g++ -std=c++11 echo_server.cpp -o echo_server -lpthread -lboost_system
./echo_server
使用浏览器控制台或在线 WebSocket 测试工具连接 ws://127.0.0.1:9002,发送消息,应该能收到相同回复。
5.3 同时支持 HTTP 静态页面
五子棋项目需要:HTTP 提供 HTML/CSS/JS 文件,WebSocket 处理游戏业务。WebSocket++ 允许在同一个端口同时处理两种协议。
我们添加一个 HTTP 回调函数:
#include <fstream>
#include <sstream>
void on_http(wsserver_t* srv, websocketpp::connection_hdl hdl) {
auto conn = srv->get_con_from_hdl(hdl);
string uri = conn->get_request().get_uri();
// 简单处理根路径,返回 HTML
string real_path = "./www/" + uri;
if (uri == "/") real_path = "./www/index.html";
ifstream file(real_path);
if (file) {
stringstream buf;
buf << file.rdbuf();
conn->set_body(buf.str());
conn->set_status(websocketpp::http::status_code::ok);
conn->append_header("Content-Type", "text/html");
} else {
conn->set_body("<h1>404 Not Found</h1>");
conn->set_status(websocketpp::http::status_code::not_found);
}
}
// 在 main 中注册
server.set_http_handler(bind(&on_http, &server, placeholders::_1));
这样,浏览器访问 http://127.0.0.1:9002 时能显示网页,同时 WebSocket 连接也走同一端口(路径需区分)。
6. 项目中的服务器设计思路
在五子棋对战中,我们会有两个不同的 WebSocket 端点:
- 大厅 (
/hall):用户匹配、在线状态 - 房间 (
/room):下棋、聊天
因此需要在 open_handler 中根据请求的 URI 分发到不同的处理逻辑。
6.1 区分 URI
void on_open(wsserver_t* srv, websocketpp::connection_hdl hdl) {
auto conn = srv->get_con_from_hdl(hdl);
string uri = conn->get_request().get_uri();
if (uri == "/hall") {
// 进入游戏大厅
} else if (uri == "/room") {
// 进入游戏房间
}
}
类似地,on_message 和 on_close 也要根据 URI 分别处理。
6.2 管理连接
为了向特定用户推送消息,我们需要将用户 ID 与 connection_hdl 映射起来。后面篇章会详细介绍在线用户管理模块。
7. 本章小结
本篇我们学习了:
- WebSocket 协议的核心原理:握手 + 全双工通信
- WebSocket++ 库的基本使用方法
- 搭建一个同时服务 HTTP 和 WebSocket 的服务器
- 为五子棋项目设计区分 URI 的连接处理框架
下一步:我们将学习 JSON 数据格式与 JsonCpp 库,用于前后端结构化消息的序列化与反序列化。
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐


所有评论(0)