本文参考:封装利用libwebsockets写出的客户端、服务端程序为客户端服务端类_逍遥游的博客-CSDN博客_libwebsockets封装

最近项目需要使用C++连接websocket服务器,选择了纯C实现的libwebsockets库;

libwebsockets下载地址:GitHub - warmcat/libwebsockets: canonical libwebsockets.org networking library

添加的openssl是openssl-1.1.1f

编译:

cmake ..  -DLWS_WITH_HTTP2=1 -DLWS_OPENSSL_INCLUDE_DIRS=../../openssl-1.1.1f/include -DLWS_OPENSSL_LIBRARIES="../../openssl-1.1.1f/libssl.a;../../openssl-1.1.1f/libcrypto.a" -DLWS_WITH_LIBEV=1 -DLIBEV_INCLUDE_DIRS=../../libev -DLIBEV_LIBRARIES=../../libev/.libs/libev.a

常用的cmake参数:

  • -DLWS_WITH_STATIC=1:编译静态库
  • -DLWS_WITH_SHARED=1:编译动态库
  • -DLWS_WITH_SSL=1:添加openssl
  • -DLWS_WITH_HTTP2=1:添加http
  • -DLWS_OPENSSL_INCLUDE_DIRS=:添加openssl的头文件目录
  • -DLWS_OPENSSL_LIBRARIES=:添加openssl的库(动态库静态库均可)
  • -DLWS_WITH_LIBEV=1:添加libev
  • -DLIBEV_INCLUDE_DIRS=:添加libev的头文件目录
  • -DLIBEV_LIBRARIES=:添加libev的库(动态库静态库均可)

注:

  • 以上参数若不想再cmake时携带,也可以直接修改 CMakeLists.txt对用参数
  • 建议可以自己实现IO复用,使用lws_get_socket_fd()获取fd,自己实现epoll进行监听,也可以直接使用libwebsockets自带的poll

目前只实现了client封装类:

client.h

#ifndef _CLIENT
#define _CLIENT#include <iostream>
#include <fstream>
#include <stack>
#include <queue>
#include <signal.h>
#include <pthread.h>
#include "json/json.h"
#include "libwebsockets.h"using namespace std;#define MAX_PAYLOAD_SIZE  8 * 1024   typedef enum
{CLIENT_IDLE,CLIENT_CONNECTING,CLIENT_CONNECTED,CLIENT_AWAITING_SEND,ClIENT_COMPLETE_RECV,CLIENT_CLOSED
}TSTAT;typedef struct session_data {int msg_count;unsigned char buf[LWS_PRE + MAX_PAYLOAD_SIZE];int len;
}session_data, *SESSIONDATA;class CWSClient:public CLog
{public:CWSClient(string url);~CWSClient();void       init();int        set_ssl(const char* ca_filepath,const char* server_cert_filepath,const char*server_private_key_filepath,int ssl_conn);bool       create();bool       connect(int ssl_conn);bool       run(int wait_time);int        send(string data);int        recv(string data);int        getRecvSize();string     getRecvMsg();void       popRecvMsg();void       setLogLevel(int level);TSTAT      getConnStat();void       destroy();void       setEnd(bool flag);friend     void* WSpthreadFunc(void* arg);friend     int callback( struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len );private:string                            m_cacheFile;bool                              m_end;int                               m_waittime;pthread_t                         m_threadID;queue<string>                     m_sendMsgQueue;queue<string>                     m_recvMsgQueue;SESSIONDATA                       m_session_data;string                            m_url;const char*                       m_prot;const char*                       m_ads;const char*                       m_path;int                               m_port;TSTAT                             m_stat;int                               m_logLevel;struct lws_protocols *            m_protocols;struct lws_context_creation_info  m_ctx_info;  /* 用于创建vhost或者context的参数 */struct lws_context *              m_context;struct lws_client_connect_info    m_conn_info;struct lws *                      m_wsi;pthread_mutex_t                   m_rmutex; pthread_mutex_t                   m_smutex;
};#endif

client.cpp

#include "websocketclient.h" static void emit_log(int level, const char *line)
{/*自己实现日子打印回调,如将对应日志写入文件,如果此回调置为NULL的话,则日志默认打印到stderr*/
}
void* WSpthreadFunc(void* arg);int callback( struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len )
{CWSClient *wsclient = NULL;const struct lws_protocols* protocols = lws_get_protocol(wsi);if(protocols && protocols[0].user)wsclient = (CWSClient *) protocols[0].user;switch ( reason ){case LWS_CALLBACK_PROTOCOL_INIT:{lwsl_notice("Protocol init\n");}break;case LWS_CALLBACK_CLIENT_ESTABLISHED:   // 连接到服务器后的回调{lwsl_notice( "Connected to server ok!\n" );wsclient->m_stat = CLIENT_CONNECTED;}break;case LWS_CALLBACK_CLIENT_RECEIVE:       // 接收到服务器数据后的回调,数据为in,其长度为len{lwsl_notice( "recv: %s\n", (char *) in );char *in_str = (char *) in; }break;case LWS_CALLBACK_CLIENT_WRITEABLE:     // 当此客户端可以发送数据时的回调if( !wsclient->m_sendMsgQueue.empty() ){lwsl_notice( "entry callback client write \n");pthread_mutex_lock(&wsclient->m_smutex);string tts_params = wsclient->m_sendMsgQueue.front(); memset( wsclient->m_session_data->buf, 0, sizeof( wsclient->m_session_data->buf ));char *msg = (char*)&wsclient->m_session_data->buf[ LWS_PRE ];wsclient->m_session_data->len = sprintf( msg, "%s", tts_params.c_str() );lwsl_notice( "send: %s\n", msg );/*LWS will buffer the remainder automatically, and send it out autonomously.*/lws_write( wsi, &wsclient->m_session_data->buf[ LWS_PRE ], wsclient->m_session_data->len, LWS_WRITE_TEXT );wsclient->m_session_data->msg_count++;wsclient->m_sendMsgQueue.pop();pthread_mutex_unlock(&wsclient->m_smutex);}break;case LWS_CALLBACK_CLIENT_CLOSED:{lwsl_notice( "ws_client is closed!\n" );wsclient->m_stat = CLIENT_CLOSED;}break;default:break;}return 0;
}CWSClient::CWSClient(string url)
{m_url          = url;m_context      = NULL;m_wsi          = NULL;m_stat         = CLIENT_IDLE;m_end          = false;       //结束标志memset(&m_ctx_info,0x00,sizeof(m_ctx_info));memset(&m_conn_info,0x00,sizeof(m_conn_info));pthread_mutex_init(&m_smutex, NULL);pthread_mutex_init(&m_rmutex, NULL);
}CWSClient::~CWSClient()
{pthread_mutex_destroy( &m_smutex );pthread_mutex_destroy( &m_rmutex );lws_context_destroy( m_context );if (m_session_data)delete(m_session_data);if(m_protocols)delete(m_protocols);m_end = true;
}void CWSClient::init()
{m_protocols = new (struct lws_protocols[2]);   //该列表以具有空回调指针的条目结束m_ctx_info.port = CONTEXT_PORT_NO_LISTEN;m_ctx_info.iface = NULL;m_ctx_info.protocols = m_protocols;m_ctx_info.gid = -1;m_ctx_info.uid = -1;m_protocols[0].name  = "wss";m_protocols[0].callback = &callback;m_protocols[0].per_session_data_size = 0;m_protocols[0].rx_buffer_size = 0;m_protocols[0].id = 0;m_protocols[0].user = this;m_protocols[1].name  = NULL;m_protocols[1].callback = NULL;m_protocols[1].per_session_data_size = 0;m_session_data = new session_data;}/**client一般不使用证书,直接置为NULL就可以**/
int CWSClient::set_ssl(const char* ca_filepath,const char* server_cert_filepath,const char*server_private_key_filepath,int ssl_conn)
{m_ctx_info.ssl_ca_filepath = ca_filepath;m_ctx_info.ssl_cert_filepath  = server_cert_filepath;m_ctx_info.ssl_private_key_filepath = server_private_key_filepath;if(ssl_conn == LCCSCF_USE_SSL || ssl_conn == (LCCSCF_USE_SSL|LCCSCF_ALLOW_INSECURE))m_ctx_info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;return ssl_conn;
}bool CWSClient::create()
{/**创建一个WebSocket处理器**/m_context = lws_create_context( &m_ctx_info );if(!m_context){lwsl_err("%s: lws_create_context error \n", __func__);return false;}return true;
}bool CWSClient::connect(int ssl_conn)
{char tmp_url[1024] = {0};strncpy(tmp_url,m_url.c_str(),sizeof(tmp_url)-1);  //lws_parse_uri解析之后会改变原url,不能使用string = ,浅拷贝if (lws_parse_uri(tmp_url, &m_prot, &m_ads, &m_port, &m_path))lwsl_err("%s: uri error %s\n", __func__, m_url.c_str());m_conn_info.context = m_context;m_conn_info.address = m_ads;m_conn_info.port = m_port;m_conn_info.ssl_connection = ssl_conn;m_conn_info.path = (char*)m_url.c_str();m_conn_info.host = m_ads;m_conn_info.sys_tls_client_cert = 0;m_conn_info.protocol = m_protocols[0].name;// 下面的调用触发LWS_CALLBACK_PROTOCOL_INIT事件// 创建一个客户端连接m_stat = CLIENT_CONNECTING;m_wsi = lws_client_connect_via_info( &m_conn_info );if(!m_wsi){lwsl_err("%s: lws_client_connect_via_info error \n", __func__);return false;}return true;
}bool CWSClient::run(int wait_time)
{m_waittime = wait_time;pthread_attr_t attr;pthread_attr_init(&attr);pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);pthread_attr_setstacksize(&attr, 512*1024);if(pthread_create(&m_threadID, &attr, WSpthreadFunc, this) != 0){   lwsl_err("[CWSClient] Failed with pthread_create: %s\n", strerror(errno));return false;}   elselwsl_notice("[CWSClient] Successed with pthread_create\n");return true;
}int CWSClient::send(string data)
{pthread_mutex_lock(&m_smutex);m_sendMsgQueue.push(data);pthread_mutex_unlock(&m_smutex);return (int)data.length();
}int CWSClient::recv(string data)
{pthread_mutex_lock(&m_rmutex);m_recvMsgQueue.push(data);pthread_mutex_unlock(&m_rmutex);return (int)data.length();
}int CWSClient::getRecvSize()
{return m_recvMsgQueue.size();
}string CWSClient::getRecvMsg()
{string data;pthread_mutex_lock(&m_rmutex);if( !m_recvMsgQueue.empty() )data = m_recvMsgQueue.front();pthread_mutex_unlock(&m_rmutex);return data;
}void CWSClient::popRecvMsg()
{pthread_mutex_lock(&m_rmutex);if( !m_recvMsgQueue.empty() )m_recvMsgQueue.pop();pthread_mutex_unlock(&m_rmutex);
}void CWSClient::setLogLevel(int level)
{/*第二个参数置为NULL ,打印到stderr*/lws_set_log_level(level, emit_log);
}TSTAT CWSClient::getConnStat()
{return m_stat;
}void CWSClient::destroy()
{pthread_mutex_destroy( &m_smutex );lws_context_destroy( m_context );if(m_session_data){delete(m_session_data);m_session_data = NULL;}if(m_protocols){delete[] m_protocols;m_protocols = NULL;}m_end = true;
}void CWSClient::setEnd(bool flag)
{m_end = flag;lwsl_notice("WSpthreadFunc end = %d\n",flag);
}void* WSpthreadFunc(void* arg)
{CWSClient* wsClient = (CWSClient*)arg;if(!wsClient){lwsl_err("WSpthreadFunc is NULL\n");return NULL;}while(1){if(wsClient == NULL || wsClient->m_end){lwsl_notice("WSpthreadFunc exit\n");break;}lws_service( wsClient->m_context, wsClient->m_waittime );/***下面的调用的意义是:当连接可以接受新数据时,触发一次WRITEABLE事件回调*当连接正在后台发送数据时,它不能接受新的数据写入请求,所有WRITEABLE事件回调不会执行**/if(!wsClient->m_sendMsgQueue.empty()){lws_callback_on_writable( wsClient->m_wsi );wsClient->m_stat = CLIENT_AWAITING_SEND;}}wsClient->m_threadID = 0;return NULL;
}

C++ 使用libwebsockets开源库封装client类相关推荐

  1. Fresco图片加载框架的介绍,相关开源库以及工具类的封装

    Fresco图片加载框架的介绍,相关开源库以及工具类的封装 本文已授权微信公众号:鸿洋(hongyangAndroid)在微信公众号平台原创首发. 简介 Fresco 是Facebook开源的安卓上的 ...

  2. libcurl开源库封装ftp工具,支持多线程并发、断点续传、超时连接、传输速率控制

    各位朋友好,第一次在CSDN写博客,后续时间充足的话会陆续更新一些资源,大家一起探讨交流,感谢!!! 如有任何疑问,可以留言. 目的:再次封装CURL接口,使用FTP相关接口更方便,更易懂 功能: 可 ...

  3. 基于 Multitype 开源库封装更好用的RecyclerView.Adapter

    前言 MultiType 这个项目,至今 v3.x 稳定多时,考虑得非常多,但也做得非常克制.原则一直是 直观.灵活.可靠.简单纯粹(其中直观和灵活是非常看重的). 这是 MultiType 框架作者 ...

  4. PHP使用GD库封装验证码类

    调试小技巧:当图片无法显示时,将header函数注释掉就可以看到报错信息了 字体文件放在当前文件目录的font文件夹中,windows的字体可以到C:\Windows\Fonts目录下复制过来,处理好 ...

  5. 【转】DICOM医学图像处理:开源库mDCM与DCMTK的比較分析(一),JPEG无损压缩DCM图像

    转自:https://www.cnblogs.com/mfrbuaa/p/4004114.html 有修订 背景介绍: 近期项目需求,需要使用C#进行最新的UI和相关DICOM3.0医学图像模块的开发 ...

  6. 粉笔网iPhone端使用的第三方开源库

    粉笔网iPhone端使用了哪些第三方的开源库.我在这儿整理了一下,分享给大家. ASIHttpRequest ASIHttpRequest 是一个被广泛使用的第三方网络访问开源库.用于提供更加友好的网 ...

  7. 开源库MusicPlayManager - 封装StarrySky音乐库

    开源库MusicPlayManager - 封装StarrySky音乐库 关于 效果图 使用 Java版初始化 Kotlin版初始化 java版权限检查工具使用方法: kotlin权限检查工具使用方法 ...

  8. 使用linux c开源库libwebsockets编写的websocket客户端

    <一>: 背景 19年中旬做的一个嵌入式项目, 应用层需要有一个心跳的功能, 当时决定用websocket协议, 所以当时就研究了下libwebsockets的使用. 网上的资料并不多, ...

  9. Python之tkinter:动态演示调用python库的tkinter带你进入GUI世界(Button的command/Label/PhotoImage/封装为类)

    Python之tkinter:动态演示调用python库的tkinter带你进入GUI世界(Button的command/Label/PhotoImage/封装为类) 目录 tkinter应用案例五 ...

最新文章

  1. 一文了解 2018年最火爆的30个机器学习项目
  2. SAP RETAIL 参考PO创建分配表之二
  3. 一个Portal处理流程
  4. 终身学习这件事,比你想的还要重要
  5. php curl ssr,php curl模拟登陆
  6. OpenCASCADE绘制测试线束:图形命令之AIS 查看器——网格可视化服务
  7. surefire 拉起testng单元测试类的源码流程阅读(二)
  8. 51 NOD 1363 最小公倍数之和 (欧拉函数思维应用)
  9. Vczh Library++ 语法分析器开发指南
  10. bootstrapV4.6.0 图片宫格布局(案例篇)
  11. ORB_SLAM2中的疑难杂症
  12. WebForms简介
  13. 控制台应用程序《石头剪刀布》——新手,
  14. mybatis配置sql超时时间
  15. php投影,投影效果怎么做?PS制作逼真的投影效果
  16. 4G内存适合装哪个版本matlab,4G内存装win7 32位还是64位|单条4G内存选32位还是64位系统性能实测...
  17. 计算机专业的黑板报内容,新学期黑板报文字资料参考
  18. 网易2017春招[编程题]赶去公司@Java
  19. [转]ISE中如何将自己的verilog源代码.v或VHDL源代码.vhd封装打包成IP核?
  20. 《游戏改变世界》读后感 一 (懊悔?and我眼中的好游戏)

热门文章

  1. TMS320C6678开发笔记---SRIO例程3
  2. android访问WebService(axis)
  3. HTTPS URL是否被加密?
  4. 怎样制作刷机包,让你的rom更个性
  5. Trends in Neurosciences:通过脑振荡的夹带调节人类记忆
  6. win10通过命令行查看无线密码
  7. java 使用Thumbnailator 上传图片 并压缩图片大小
  8. Oracle的概念和术语 (一)
  9. 计算数组的和以及平均值
  10. erlang nif 测试