基于 libwebsockets 库实现的 WebSocket 服务器类
CCnWebSocket 类使用文档
一、类简介
CCnWebSocket
是基于 libwebsockets 库实现的 WebSocket 服务器类,采用单例模式设计,支持跨平台运行,提供连接管理、数据收发及连接关闭等核心功能。
二、类(cnwebsocket.h、cnwebsocket.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_
#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); //创建上下文对面,管理wssif (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()
方法获取全局唯一实例,确保资源统一管理CCnWebSocket* 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)
-
初始化代码片段:
void 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 占用int 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
:连接销毁
-
事件处理流程:
int 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)
:是否为最后一个分片
int 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 触发关闭
int CCnWebSocket::CloseWebSocketConnection(lws *pstWsi) {m_bNeedCloseCurWebSocket = true;return 1; }
- 通过
四、使用方法
-
获取实例并初始化
CCnWebSocket* pWebSocket = CCnWebSocket::GetInstance();
(实例创建时自动调用
Init()
完成初始化) -
发送消息
u8* pData = ...; // 待发送数据 unsigned int dataLen = ...; // 数据长度 pWebSocket->SendWsMessage(wsi, pData, dataLen);
-
关闭连接
pWebSocket->CloseWebSocketConnection(wsi);
-
销毁实例
pWebSocket->Destroy();
五、注意事项
- 依赖 libwebsockets 库,需确保正确链接该库
- 默认端口为 5000,可在
Init()
中修改m_stLwsInfo.port
调整 - 支持 Linux 平台的 SSL 配置(通过注释代码可开启)
- 数据收发逻辑需在
WebSocketSend
和WebSocketRcv
中补充完整实现 - 线程安全:
CloseWebSocketConnection
是线程安全函数,其他方法需根据实际场景处理线程同步
六、扩展与定制
- 协议扩展:可在
s_stProtocols
中添加新协议及对应回调函数 - 压缩配置:默认启用 “permessage-deflate” 扩展,可通过修改
exts
数组调整 - 端口与网络配置:在
Init()
中修改m_stLwsInfo
结构体可配置绑定接口、SSL 证书等参数 - 事件处理:根据业务需求重写
WebSocketConnect
、WebSocketRcv
等事件处理函数