跳到主要内容
C++ WebSocket 在线五子棋对战系统全栈实战 | 极客日志
C++ 大前端 算法
C++ WebSocket 在线五子棋对战系统全栈实战 基于 C++ WebSocket 构建的在线五子棋对战系统涵盖了服务端网络通信、数据库交互及会话管理。文章详细解析了 websocketpp 库的使用、MySQL 数据持久化方案以及前后端联调流程。核心模块包括房间管理、匹配队列设计及 Session 生命周期控制,解决了长连接状态维持与用户鉴权难题。通过实际代码演示了从注册登录到游戏对弈的完整业务闭环,适合希望深入理解 C++ 网络编程与实时对战逻辑的开发者参考。
SecGuard 发布于 2026/3/16 更新于 2026/4/25 1 浏览C++ WebSocket 在线五子棋对战系统全栈实战
WebSocket 基础认知
WebSocket 是 HTML5 引入的一种网页端与服务端保持长连接的消息推送机制。传统的 Web 程序遵循'一问一答'模式,客户端发送 HTTP 请求,服务器返回响应。在这种模式下,服务器处于被动地位,若客户端不主动发起请求,服务器无法主动推送消息。
对于即时聊天或五子棋这类强依赖'消息推送'的应用,仅靠原生 HTTP 协议实现推送通常采用'轮询'方式。轮询成本高且实时性差。WebSocket 更接近 TCP 级别的通信方式,一旦连接建立,客户端和服务端均可主动向对方发送数据。
协议切换与搭建流程
WebSocket 基于 HTTP 协议进行握手切换。客户端发送升级请求,服务端响应同意,随后转为 WebSocket 通信。
使用 websocketpp 库搭建服务器的标准流程如下:
实例化 server 对象
设置日志输出等级
初始化 asio 框架的调度器
设置业务处理回调函数
设置监听端口
开始获取新建连接
启动服务器
#include <iostream>
#include <string>
#include <websocketpp/server.hpp>
#include <websocketpp/config/asio_no_tls.hpp>
typedef websocketpp::server<websocketpp::config::asio> wsserver_t ;
void print (const std::string &str) {
std::cout << str << std::endl;
}
void http_callback (wsserver_t *srv, websocketpp::connection_hdl hdl) {
wsserver_t ::connection_ptr conn = srv->get_con_from_hdl (hdl);
std::cout << "body: " << conn->get_request_body () << std::endl;
websocketpp::http::parser::request req = conn->get_request ();
std::cout << "method: " << req.get_method () << std::endl;
std::cout << "uri: " << req. () << std::endl;
std::string body = ;
conn-> (body);
conn-> ( , );
conn-> (websocketpp::http::status_code::ok);
}
{
std::cout << ;
}
{
std::cout << ;
}
{
::connection_ptr conn = srv-> (hdl);
std::cout << << msg-> () << std::endl;
std::string rsp = + msg-> ();
conn-> (rsp, websocketpp::frame::opcode::text);
}
{
wssrv;
wssrv. (websocketpp::log::alevel::none);
wssrv. ();
wssrv. ( );
wssrv. (std:: (http_callback, &wssrv, std::placeholders::_1));
wssrv. (std:: (wsopen_callback, &wssrv, std::placeholders::_1));
wssrv. (std:: (wsclose_callback, &wssrv, std::placeholders::_1));
wssrv. (std:: (wsmsg_callback, &wssrv, std::placeholders::_1, std::placeholders::_2));
wssrv. ( );
wssrv. ();
wssrv. ();
;
}
get_uri
"<html><body><h1>Hello World</h1></body></html>"
set_body
append_header
"Content-Type"
"text/html"
set_status
void wsopen_callback (wsserver_t *srv, websocketpp::connection_hdl hdl)
"websocket 握手成功!!\n"
void wsclose_callback (wsserver_t *srv, websocketpp::connection_hdl hdl)
"websocket 连接断开!!\n"
void wsmsg_callback (wsserver_t *srv, websocketpp::connection_hdl hdl, wsserver_t ::message_ptr msg)
wsserver_t
get_con_from_hdl
"wsmsg: "
get_payload
"client say: "
get_payload
send
int main ()
wsserver_t
set_access_channels
init_asio
set_reuse_addr
true
set_http_handler
bind
set_open_handler
bind
set_close_handler
bind
set_message_handler
bind
listen
8085
start_accept
run
return
0
MySQL 数据库交互 访问 MySQL 的标准操作流程包括初始化句柄、连接服务器、设置字符集、选择数据库、执行 SQL、处理结果集及释放资源。对于查询操作,需将结果保存到本地并遍历获取。
#include <stdio.h>
#include <string.h>
#include <mysql/mysql.h>
#define HOST "127.0.0.1"
#define PORT 3306
#define USER "root"
#define PASS "[email protected] "
#define DBNAME "gobang"
int main () {
MYSQL *mysql = mysql_init (NULL );
if (mysql == NULL ) {
printf ("mysql init failed!\n" );
return -1 ;
}
if (mysql_real_connect (mysql, HOST, USER, PASS, DBNAME, PORT, NULL , 0 ) == NULL ) {
printf ("connect mysql server failed : %s\n" , mysql_error (mysql));
mysql_close (mysql);
return -1 ;
}
if (mysql_set_character_set (mysql, "utf8" ) != 0 ) {
printf ("set client character failed : %s\n" , mysql_error (mysql));
mysql_close (mysql);
return -1 ;
}
char *sql = "select * from stu;" ;
int ret = mysql_query (mysql, sql);
if (ret != 0 ) {
printf ("mysql query failed : %s\n" , mysql_error (mysql));
mysql_close (mysql);
return -1 ;
}
MYSQL_RES *res = mysql_store_result (mysql);
if (res == NULL ) {
mysql_close (mysql);
return -1 ;
}
int num_row = mysql_num_rows (res);
int num_col = mysql_num_fields (res);
for (int i = 0 ; i < num_row; i++) {
MYSQL_ROW row = mysql_fetch_row (res);
for (int j = 0 ; j < num_col; j++) {
printf ("%s\t" , row[j]);
}
printf ("\n" );
}
mysql_free_result (res);
mysql_close (mysql);
return 0 ;
}
核心工具模块封装 为了提高开发效率,我们封装了日志、MySQL、JSON、字符串分割及文件读取等通用工具类。
日志宏实现 利用预处理器定义不同级别的日志宏,支持自动记录文件名和行号。
#ifndef __M_LOGGER_H__
#define __M_LOGGER_H__
#include <stdio.h>
#include <time.h>
#define INF 0
#define DBG 1
#define ERR 2
#define DEFAULT_LOG_LEVEL INF
#define LOG(level, format, ...) do{\
if (DEFAULT_LOG_LEVEL > level) break;\
time_t t = time(NULL);\
struct tm *lt = localtime(&t);\
char buf[32] = {0};\
strftime(buf, 31, "%H:%M:%S" , lt);\
fprintf(stdout, "[%s %s:%d] " format "\n" , buf, __FILE__, __LINE__, ##__VA_ARGS__);\
}while(0)
#define ILOG(format, ...) LOG(INF, format, ##__VA_ARGS__)
#define DLOG(format, ...) LOG(DBG, format, ##__VA_ARGS__)
#define ELOG(format, ...) LOG(ERR, format, ##__VA_ARGS__)
#endif
JSON 与字符串处理 使用 jsoncpp 进行序列化与反序列化,配合自定义字符串分割工具处理协议报文。
class json_util {
public :
static bool serialize (const Json::Value &root, std::string &str) {
Json::StreamWriterBuilder swb;
std::unique_ptr<Json::StreamWriter>sw (swb.newStreamWriter ());
std::stringstream ss;
int ret = sw->write (root, &ss);
if (ret != 0 ) {
ELOG ("json serialize failed!!" );
return false ;
}
str = ss.str ();
return true ;
}
};
数据管理与用户模型
用户表设计 设计包含 ID、用户名、密码、积分、总场次及胜场的基础信息表。
drop database if exists gobang;
create database if not exists gobang;
use gobang;
create table if not exists user (
id int primary key auto_increment,
username varchar (32 ) unique key not null ,
password varchar (128 ) not null ,
score int ,
total_count int ,
win_count int
);
数据管理模块 封装 user_table 类,提供注册、登录验证、信息查询及胜负更新功能。注意使用互斥锁保护数据库访问,防止并发冲突。
class user_table {
private :
MYSQL *_mysql;
std::mutex _mutex;
public :
bool insert (Json::Value &user) {
#define INSERT_USER "insert user values(null, '%s', password('%s'), 1000, 0, 0);"
if (user["password" ].isNull () || user["username" ].isNull ()) {
DLOG ("INPUT PASSWORD OR USERNAME" );
return false ;
}
char sql[4096 ] = {0 };
sprintf (sql, INSERT_USER, user["username" ].asCString (), user["password" ].asCString ());
bool ret = mysql_util::mysql_exec (_mysql, sql);
return ret;
}
bool login (Json::Value &user) {
return true ;
}
};
注:在 MySQL 8.0 中 password() 函数已被移除,实际项目中建议改用 MD5 或其他哈希算法存储密码。
在线用户与会话管理
在线用户管理 维护游戏大厅和游戏房间的用户连接映射。当 WebSocket 连接建立时加入列表,断开时移除。
class online_manager {
private :
std::mutex _mutex;
std::unordered_map<uint64_t , wsserver_t ::connection_ptr> _hall_user;
std::unordered_map<uint64_t , wsserver_t ::connection_ptr> _room_user;
public :
void enter_game_hall (uint64_t uid, wsserver_t ::connection_ptr &conn) {
std::unique_lock<std::mutex> lock (_mutex) ;
_hall_user.insert (std::make_pair (uid, conn));
}
};
Session 生命周期控制 Session 用于维持用户登录状态。通过定时器管理过期时间,进入大厅或房间后设为永久有效,退出后恢复为超时销毁。
class session_manager {
private :
uint64_t _next_ssid;
std::mutex _mutex;
std::unordered_map<uint64_t , session_ptr> _session;
wsserver_t *_server;
public :
void set_session_expire_time (uint64_t ssid, int ms) {
}
};
游戏房间与匹配逻辑
房间设计 每个房间包含棋盘状态、玩家信息及胜负判定逻辑。走棋时需校验位置合法性及是否已占位,并检测是否达成五连珠。
class room {
private :
uint64_t _room_id;
std::vector<std::vector<int >> _board;
bool five (int row, int col, int row_off, int col_off, int color) {
}
Json::Value handle_chess (Json::Value &req) {
}
};
匹配队列 使用线程安全的链表实现匹配队列,根据用户积分分为普通、高手、大神三个档次。匹配成功后创建房间并通知双方。
template <class T > class match_queue {
private :
std::list<T> _list;
std::mutex _mutex;
std::condition_variable _cond;
public :
void push (const T &data) { }
bool pop (T &data) { }
};
前后端联调与业务闭环
静态资源与动态请求 服务器需同时处理静态文件(HTML/CSS)和动态 API 请求。静态资源直接读取文件系统,动态请求解析 JSON 并调用后端逻辑。
登录与鉴权流程
注册 :前端提交 JSON,后端写入数据库。
登录 :验证通过后生成 Session,通过 Set-Cookie 返回 SSID。
鉴权 :后续请求携带 Cookie,服务端解析 SSID 验证 Session 有效性。
前端页面使用 jQuery 发起 AJAX 请求,WebSocket 负责游戏过程中的实时通信。游戏大厅页面负责展示用户信息及匹配按钮,游戏房间页面负责 Canvas 绘制棋盘及接收走棋指令。
常见问题排查
MySQL 版本兼容性 :如前所述,注意密码加密函数的差异。
Session 过期 :确保长连接期间 Session 被设置为永久有效,避免频繁重登。
跨域问题 :前端 JS 请求需注意同源策略,必要时配置 CORS。
项目总结 本项目完整实现了从网络层到业务层的在线五子棋对战系统。核心技术点包括:
网络通信 :基于 websocketpp 实现双向长连接。
数据持久化 :MySQL 存储用户信息与对局记录。
并发控制 :利用互斥锁保护共享资源,条件变量优化匹配等待。
状态管理 :Session 机制保障用户会话安全。
通过此实战案例,可以深入理解 C++ 在网络编程领域的实际应用,特别是如何处理高并发下的状态同步与实时交互。
相关免费在线工具 加密/解密文本 使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
Gemini 图片去水印 基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online
Base64 字符串编码/解码 将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
Base64 文件转换器 将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
Markdown转HTML 将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
HTML转Markdown 将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online