CCnWebSocket 类使用文档
一、类简介
CCnWebSocket
是基于 libwebsockets 库实现的 WebSocket 服务器类,采用单例模式设计,支持跨平台运行,提供连接管理、数据收发及连接关闭等核心功能。
二、类(cnwebsocket.h、cnwebsocket.cpp)
cpp
#ifndef _CN_WEBSOCKET_H_
#define _CN_WEBSOCKET_H_
class CCnWebSocket
{
public:
CCnWebSocket();
virtual ~CCnWebSocket();
static CCnWebSocket* GetInstance();
void Destroy();
void Init();
int RunWebSocketServer();
static int WebSocketCB(lws *pstWsi, lws_callback_reasons eReason, void *pUser, void *pIn, size_t dwLen);
int WebSocketConnect(lws *pstWsi);
int WebSocketSend(lws *pstWsi);
int WebSocketRcv(lws *pstWsi, u8 *pbyData, unsigned int dwDataLen);
int WebSocketClose(lws *pstWsi);
int SendWsMessage(void *pstWsi, const u8 *pbyData, unsigned int dwDataLen);
//主动关闭一个ws连接
//连接不会被立即关闭,而是被标记为待关闭
//这个函数是线程安全的函数
int CloseWebSocketConnection(lws *pstWsi);
public:
static CCnWebSocket* m_scInstance;
private:
static lws_protocols s_stProtocols[];
lws_context_creation_info m_stLwsInfo; //https上下文对象的信息
lws_context* m_pstLwsContext; //https上下文对象指针
bool m_bNeedCloseCurWebSocket;
};
#endif //_CN_WEBSOCKET_H_
cpp
#include "cnwebsocket.h"
//使用的默认扩展
static const struct lws_extension exts[] = {
{
"permessage-deflate",
lws_extension_callback_pm_deflate,
"permessage-deflate"
},
{ NULL, NULL, NULL /* terminator */ }
};
//注册协议,一种协议,对应一套处理方案(类似驱动中的设备树)
lws_protocols CCnWebSocket::s_stProtocols[] = {
/* first protocol must always be HTTP handler */
/*{ "http", CCnWebSocket::websocket_callback_http, 0, 0 },*/
{ "default-protocol", CCnWebSocket::WebSocketCB, 0, 1024*128 },
{ NULL, NULL, 0, 0 }
};
void* WebSocketServerTask( void* pParam )
{
CCnWebSocket* pcWebserver = (CCnWebSocket*)pParam;
if (pcWebserver)
{
pcWebserver->RunWebSocketServer();
}
return NULL;
}
CCnWebSocket* CCnWebSocket::m_scInstance = NULL;
CCnWebSocket::CCnWebSocket()
{
m_pstLwsContext = NULL;
memset(&m_stLwsInfo, 0, sizeof(m_stLwsInfo));
Init();
printf( "[CCnWebSocket] End.\n");
}
CCnWebSocket::~CCnWebSocket()
{
Destroy();
}
CCnWebSocket* CCnWebSocket::GetInstance()
{
if (NULL == m_scInstance)
{
m_scInstance = new CCnWebSocket();
}
return m_scInstance;
}
void CCnWebSocket::Destroy()
{
m_pstLwsContext = NULL;
memset(&m_stLwsInfo, 0, sizeof(m_stLwsInfo));
SAFE_DELETE(m_scInstance);
}
void CCnWebSocket::Init()
{
m_stLwsInfo.port = 5000;
m_stLwsInfo.iface = NULL;
m_stLwsInfo.protocols = CCnWebSocket::s_stProtocols;
m_stLwsInfo.options = 0|LWS_SERVER_OPTION_IPV6_V6ONLY_MODIFY;
m_stLwsInfo.extensions = exts; //lws_get_internal_extensions();
m_stLwsInfo.ssl_cert_filepath = NULL;
m_stLwsInfo.ssl_private_key_filepath = NULL;
m_stLwsInfo.ssl_ca_filepath = NULL;
m_stLwsInfo.pt_serv_buf_size = (1 << 17);
m_stLwsInfo.gid = -1;
m_stLwsInfo.uid = -1;
m_stLwsInfo.ka_time = 0;
m_stLwsInfo.ka_probes = 0;
m_stLwsInfo.ka_interval = 0;
#ifdef _LINUX_
// m_stLwsInfo.ssl_cert_filepath = "/usr/server.pem";
// m_stLwsInfo.ssl_private_key_filepath = "/usr/privkey.pem";
// m_stLwsInfo.ssl_ca_filepath = "/usr/root.pem";
// m_stLwsInfo.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
#endif
//Createprogress是创建线程函数,这个根据自己的业务需要自己实现 TASKHANDLE hTaskID需要根据业务自己一个定义成需要的
TASKHANDLE hTaskID = Createprogress( WebSocketServerTask, "WebSocketServerTask", 19, 2<<20, (uintptr_t)this, 0);
if ( !hTaskID )
{
printf("[CCnWebSocket::Init] Create WebSocketServerTask Fail!\n");
}
printf("[CCnWebSocket::Init] End.\n");
}
int CCnWebSocket::RunWebSocketServer()
{
printf( "[CCnWebSocket::RunWebSocketServer] Entry...\n");
m_pstLwsContext = lws_create_context(&m_stLwsInfo); //创建上下文对面,管理wss
if (NULL == m_pstLwsContext) {
printf( "[CCnWebSocket::RunWebserver] Create Websocket Context Fail.\n");
return -1;
}
printf( "[CCnWebSocket::RunWebserver] Create Websocket Context Success!\n");
int nWsServerBlockTime = 0;
while(1)
{
nWsServerBlockTime = 10;
lws_service(m_pstLwsContext, nWsServerBlockTime); //启动wss服务
usleep(1 * 1000);
}
lws_context_destroy(m_pstLwsContext);
return 1;
}
int CCnWebSocket::WebSocketCB(lws *pstWsi, lws_callback_reasons eReason, void *pUser, void *pData, size_t dwLen)
{
if ( NULL == pstWsi )
{
printf( "[CCnWebSocket::WebSocketCB] reason -> eReason:%d. pstWsi = NULL, return.\n", eReason);
return 0;
}
if(NULL == m_scInstance)
{
printf( "[CCnWebSocket::WebSocketCB] reason -> eReason:%d. m_scInstance = NULL, return.\n", eReason);
return 0;
}
int nRet = 0;
switch (eReason) {
/*初始化*/
case LWS_CALLBACK_PROTOCOL_INIT:
printf( "[CCnWebSocket::WebSocketCB] pstWsi:%p, reason -> LWS_CALLBACK_PROTOCOL_INIT!\n", pstWsi);
break;
/*建立连接*/
case LWS_CALLBACK_ESTABLISHED:
printf( "[CCnWebSocket::WebSocketCB] reason -> LWS_CALLBACK_ESTABLISHED!\n");
nRet = m_scInstance->WebSocketConnect(pstWsi);
break;
/*连接关闭*/
case LWS_CALLBACK_CLOSED:
printf( "[CCnWebSocket::WebSocketCB] reason -> LWS_CALLBACK_CLOSED!\n");
nRet = m_scInstance->WebSocketClose(pstWsi);
break;
/*收到数据*/
case LWS_CALLBACK_RECEIVE:
printf( "[CCnWebSocket::WebSocketCB] reason -> LWS_CALLBACK_RECEIVE!\n");
nRet = m_scInstance->WebSocketRcv(pstWsi, (u8*)pData, dwLen);
break;
/*服务端可写*/
case LWS_CALLBACK_SERVER_WRITEABLE: //此处会调用的前提是:lws_callback_on_writable_all_protocol(context, &protocols[1]); 被循环调用
printf( "[CCnWebSocket::WebSocketCB] reason -> LWS_CALLBACK_SERVER_WRITEABLE!\n");
if(m_bNeedCloseCurWebSocket)
{
//这个就是主动关闭连接的动作,可以根据业务需要修改相关,比喻多个连接等,需要自己分数组处理
return -1;
}
nRet = m_scInstance->WebSocketSend(pstWsi);
//sleep(1);
break;
/*WS对象销毁*/
case LWS_CALLBACK_WSI_DESTROY:
printf( "[CCnWebSocket::WebSocketCB] reason -> LWS_CALLBACK_WSI_DESTROY!\n");
//m_scInstance->WebSocketClose(pstWsi);
break;
default:
printf( "[CCnWebSocket::websocket_callback] other-> eReason:%d.\n", eReason);
break;
}
return nRet;
}
int CCnWebSocket::WebSocketConnect(lws *pstWsi)
{
//可以根据业务需要,自己添加
return 0;
}
int CCnWebSocket::WebSocketSend(lws *pstWsi)
{
//可以根据业务需要,自己添加
return 0;
}
int CCnWebSocket::WebSocketRcv(lws *pstWsi, u8 *pbyData, unsigned int dwDataLen)
{
//可以根据业务需要,自己添加
int nFirstFrag = lws_is_first_fragment(pstWsi);
int nFinalFrag = lws_is_final_fragment(pstWsi);
printf( "[CCnWebSocket::WebSocketRcv] nFirstFrag = %d, nFinalFrag = %d\n", nFirstFrag, nFinalFrag);
if (1 == nFirstFrag)//判断是否是收到的第一帧数据
{
}
else
{
}
if (1 == nFinalFrag)//判断是否是接收到的最后一帧数据
{
}
return 0;
}
int CCnWebSocket::WebSocketClose(lws *pstWsi)
{
//可以根据业务需要,自己添加
printf( "[CCnWebSocket::WebSocketClose] pstWsi: %p, close\n", pstWsi);
return 0;
}
int CCnWebSocket::SendWsMessage(void *pstWsi, const u8 *pbyData, unsigned int dwDataLen)
{
//可以根据业务需要,自己添加
return 1;
}
int CCnWebSocket::CloseWebSocketConnection(lws *pstWsi)
{
printf( "[CCnWebSocket::CloseWebSocketConnection] A connection is to be close.\n");
printf( "[CCnWebSocket::CloseWebSocketConnection] pstWsi: %p\n", pstWsi);
//此处设置个标识,等底层websocket回调之后返回-1,就可以主动关闭这个连个, 根据业务需要自己需要区分pstWsi,需要自己实现
m_bNeedCloseCurWebSocket = true;
return 1;
}
三、核心功能与实现细节
1. 单例模式实现
-
获取实例 :通过
GetInstance()
方法获取全局唯一实例,确保资源统一管理cppCCnWebSocket* CCnWebSocket::GetInstance() { if (NULL == m_scInstance) { m_scInstance = new CCnWebSocket(); } return m_scInstance; }
-
销毁实例 :
Destroy()
方法释放资源并重置单例指针
2. 初始化流程(Init())
-
配置 WebSocket 服务器核心参数:
- 端口:默认 5000
- 协议:注册 "default-protocol" 协议及回调函数
- 扩展:支持 "permessage-deflate" 压缩扩展
- 线程创建:通过
Createprogress
启动服务器线程(优先级 19,栈大小 2MB)
-
初始化代码片段:
cppvoid CCnWebSocket::Init() { m_stLwsInfo.port = 5000; m_stLwsInfo.protocols = CCnWebSocket::s_stProtocols; m_stLwsInfo.extensions = exts; // 其他配置参数... //通过 `Createprogress` 启动服务器线程 ,需要自己实现 TASKHANDLE hTaskID = Createprogress(WebSocketServerTask, "WebSocketServerTask", 19, 2<<20, (uintptr_t)this, 0); }
3. 服务器运行机制(RunWebSocketServer())
-
创建 libwebsockets 上下文(
lws_create_context
) -
进入事件循环,通过
lws_service
处理 WebSocket 事件(阻塞时间 10ms) -
循环中调用
usleep(1000)
降低 CPU 占用cppint CCnWebSocket::RunWebSocketServer() { m_pstLwsContext = lws_create_context(&m_stLwsInfo); if (NULL == m_pstLwsContext) { printf("[CCnWebSocket::RunWebserver] Create Websocket Context Fail.\n"); return -1; } while(1) { lws_service(m_pstLwsContext, 10); usleep(1 * 1000); } lws_context_destroy(m_pstLwsContext); return 1; }
4. 回调函数与事件处理(WebSocketCB())
-
核心回调函数,处理各类 WebSocket 事件:
LWS_CALLBACK_PROTOCOL_INIT
:协议初始化LWS_CALLBACK_ESTABLISHED
:连接建立(调用WebSocketConnect
)LWS_CALLBACK_CLOSED
:连接关闭(调用WebSocketClose
)LWS_CALLBACK_RECEIVE
:数据接收(调用WebSocketRcv
)LWS_CALLBACK_SERVER_WRITEABLE
:可写事件(调用WebSocketSend
)LWS_CALLBACK_WSI_DESTROY
:连接销毁
-
事件处理流程:
cppint CCnWebSocket::WebSocketCB(...) { switch (eReason) { case LWS_CALLBACK_ESTABLISHED: nRet = m_scInstance->WebSocketConnect(pstWsi); break; case LWS_CALLBACK_RECEIVE: nRet = m_scInstance->WebSocketRcv(pstWsi, (u8*)pData, dwLen); break; // 其他事件处理... } return nRet; }
5. 数据接收与处理(WebSocketRcv())
-
支持分片数据处理,通过以下函数判断分片状态:
lws_is_first_fragment(pstWsi)
:是否为第一个分片lws_is_final_fragment(pstWsi)
:是否为最后一个分片
cppint CCnWebSocket::WebSocketRcv(...) { int nFirstFrag = lws_is_first_fragment(pstWsi); int nFinalFrag = lws_is_final_fragment(pstWsi); // 分片数据处理逻辑... return 0; }
6. 连接关闭机制(CloseWebSocketConnection())
-
线程安全的延迟关闭实现:
- 通过
m_bNeedCloseCurWebSocket
标记连接待关闭 - 在
LWS_CALLBACK_SERVER_WRITEABLE
事件中返回 -1 触发关闭
cppint CCnWebSocket::CloseWebSocketConnection(lws *pstWsi) { m_bNeedCloseCurWebSocket = true; return 1; }
- 通过
四、使用方法
-
获取实例并初始化
cppCCnWebSocket* pWebSocket = CCnWebSocket::GetInstance();
(实例创建时自动调用
Init()
完成初始化) -
发送消息
cppu8* pData = ...; // 待发送数据 unsigned int dataLen = ...; // 数据长度 pWebSocket->SendWsMessage(wsi, pData, dataLen);
-
关闭连接
cpppWebSocket->CloseWebSocketConnection(wsi);
-
销毁实例
cpppWebSocket->Destroy();
五、注意事项
- 依赖 libwebsockets 库,需确保正确链接该库
- 默认端口为 5000,可在
Init()
中修改m_stLwsInfo.port
调整 - 支持 Linux 平台的 SSL 配置(通过注释代码可开启)
- 数据收发逻辑需在
WebSocketSend
和WebSocketRcv
中补充完整实现 - 线程安全:
CloseWebSocketConnection
是线程安全函数,其他方法需根据实际场景处理线程同步
六、扩展与定制
- 协议扩展 :可在
s_stProtocols
中添加新协议及对应回调函数 - 压缩配置 :默认启用 "permessage-deflate" 扩展,可通过修改
exts
数组调整 - 端口与网络配置 :在
Init()
中修改m_stLwsInfo
结构体可配置绑定接口、SSL 证书等参数 - 事件处理 :根据业务需求重写
WebSocketConnect
、WebSocketRcv
等事件处理函数