基于 libwebsockets 库实现的 WebSocket 服务器类

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() 方法获取全局唯一实例,确保资源统一管理

    cpp 复制代码
    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)
  • 初始化代码片段:

    cpp 复制代码
    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 占用

    cpp 复制代码
    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:连接销毁
  • 事件处理流程:

    cpp 复制代码
    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):是否为最后一个分片
    cpp 复制代码
    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 触发关闭
    cpp 复制代码
    int CCnWebSocket::CloseWebSocketConnection(lws *pstWsi)
    {
        m_bNeedCloseCurWebSocket = true;
        return 1;
    }

四、使用方法

  1. 获取实例并初始化

    cpp 复制代码
    CCnWebSocket* pWebSocket = CCnWebSocket::GetInstance();

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

  2. 发送消息

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

    cpp 复制代码
    pWebSocket->CloseWebSocketConnection(wsi);
  4. 销毁实例

    cpp 复制代码
    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 等事件处理函数