当前位置: 首页 > news >正文

基于 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;
    }
    

四、使用方法

  1. 获取实例并初始化

    CCnWebSocket* pWebSocket = CCnWebSocket::GetInstance();
    

    (实例创建时自动调用 Init() 完成初始化)

  2. 发送消息

    u8* pData = ...; // 待发送数据
    unsigned int dataLen = ...; // 数据长度
    pWebSocket->SendWsMessage(wsi, pData, dataLen);
    
  3. 关闭连接

    pWebSocket->CloseWebSocketConnection(wsi);
    
  4. 销毁实例

    pWebSocket->Destroy();
    

五、注意事项

  1. 依赖 libwebsockets 库,需确保正确链接该库
  2. 默认端口为 5000,可在 Init() 中修改 m_stLwsInfo.port 调整
  3. 支持 Linux 平台的 SSL 配置(通过注释代码可开启)
  4. 数据收发逻辑需在 WebSocketSendWebSocketRcv 中补充完整实现
  5. 线程安全:CloseWebSocketConnection 是线程安全函数,其他方法需根据实际场景处理线程同步

六、扩展与定制

  1. 协议扩展:可在 s_stProtocols 中添加新协议及对应回调函数
  2. 压缩配置:默认启用 “permessage-deflate” 扩展,可通过修改 exts 数组调整
  3. 端口与网络配置:在 Init() 中修改 m_stLwsInfo 结构体可配置绑定接口、SSL 证书等参数
  4. 事件处理:根据业务需求重写 WebSocketConnectWebSocketRcv 等事件处理函数
http://www.dtcms.com/a/328438.html

相关文章:

  • Shader warning in ‘Universal Render Pipeline/Particles/Simple Lit‘
  • provide()函数和inject()函数
  • 【UEFI系列】Super IO
  • VUE+SPRINGBOOT从0-1打造前后端-前后台系统-语音评测
  • 嵌入式学习(day25)文件IO:open read/write close
  • VGG改进(2):基于Local Attention的模型优化
  • 书籍数组中未出现的最小正整数(8)0812
  • 《飞算JavaAI:新一代智能编码引擎,革新Java研发范式》
  • 跑腿平台开发实战:同城O2O系统源码的模块化与可扩展性方案
  • 每日一练:将一个数字表示成幂的和的方案数;动态规划、深度优先搜索
  • 【Altium designer】快速建立原理图工程的步骤
  • 2025开放计算技术大会|开源开放推动系统创新 加速AIDC全球协作
  • 过拟合、欠拟合与方差/偏差的关系
  • Langchain结合deepseek:框架+模型的AI测试实践
  • 小白学习pid环控制-实现篇
  • 杰里平台7083G 如何支持4M flash
  • 【oracle闪回查询】记录字段短时间被修改的记录
  • MyBatis-Plus核心内容
  • AAC音频编码器技术详解:原理、应用与发展
  • Java数组排序
  • 嵌入式系统分层开发:架构模式与工程实践(四)(状态机的应用和面向对象的编程)
  • redis认识缓存击穿
  • 特征工程--机器学习
  • [ 数据结构 ] 时间和空间复杂度
  • Linux中Apache与Web之虚拟主机配置指南
  • 栈和队列:数据结构中的基础与应用​
  • GaussDB 数据库架构师修炼(十三)安全管理(2)-数据库权限管理
  • 专题:2025城市NOA智能驾驶研究报告|附70+份报告PDF 汇总下载
  • Spring MVC 处理请求的完整流程详解
  • Kubernetes1.28-单Master集群部署