<一>: 背景

19年中旬做的一个嵌入式项目, 应用层需要有一个心跳的功能, 当时决定用websocket协议, 所以当时就研究了下libwebsockets的使用. 网上的资料并不多, 当时只能研究开源库提供的测试代码, 然后改改后就合并到项目里了. 这几天又把libwebsockets重新编译了下, 顺手重新写了一个简单的websocket客户端. 主要包含了以下三个功能:

1): 定时发送心跳数据;

2): 掉网重连;

3): 是否使用ssl;

代码如<二>, 不足之处请多指教.

<二>: 代码

WebsocketClient.h如下:

#ifndef WEBSOCKET_CLIENT_H
#define WEBSOCKET_CLIENT_H#include <string.h>
#include <libwebsockets.h>#define _THIS_ "this"
#define _ADDR_ "addr"
#define _PORT_ "port"
#define _PATH_ "path"
#define _SSL_  "ssl"class WebsocketClient;
typedef struct ClientData {struct lws_context *context;struct lws_vhost *vhost;struct lws *client_wsi;const char **addr;int *port;const char **path;int *ssl;WebsocketClient *wsc;
}_ClientData;class WebsocketClient
{
public:WebsocketClient();~WebsocketClient(){if (mContext != NULL){lws_context_destroy(mContext);mContext = NULL;}}public:bool Start(int argc, const char **argv);private:bool init(int argc, const char **argv);public:char msg[1024];private:struct lws_context *mContext;struct lws_protocol_vhost_options lpvoAddr; // 地址struct lws_protocol_vhost_options lpvoPort; // 端口struct lws_protocol_vhost_options lpvoPath; // 路径struct lws_protocol_vhost_options lpvoSSL;  // 是否使用sslstruct lws_protocol_vhost_options lpvoThis; // 传递的本类struct lws_protocol_vhost_options lpvo;
};#endif // WEBSOCKET_CLIENT_H

WebsocketClient.cpp如下:


#include "WebsocketClient.h"static const char *addr = "localhost", *path = "/";
static int port = 7681, ssl = 0;static int callBackFunc(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len);
#define WEBSOCKET_CLIENT \
{ \"websocket-client", \callBackFunc, \0, \1024, \0, NULL, 0 \
}
static struct lws_protocols lProtocols[] = {WEBSOCKET_CLIENT,{ NULL, NULL, 0, 0 }};// 用于事件回调
static void scheduleCallBack(struct lws *wsi, int reason, int secs)
{lws_timed_callback_vh_protocol(lws_get_vhost(wsi), lws_get_protocol(wsi), reason, secs);
}// 连接服务
static int connectServer(_ClientData *ClientData)
{struct lws_client_connect_info lccInfo;memset(&lccInfo, 0, sizeof(lccInfo));char host[32];lws_snprintf(host, sizeof(host), "%s:%u", *ClientData->addr, *ClientData->port);lccInfo.context = ClientData->context;lccInfo.port = *ClientData->port;lccInfo.address = *ClientData->addr;lccInfo.path = *ClientData->path;lccInfo.host = host;lccInfo.origin = host;lccInfo.ssl_connection = *ClientData->ssl;lccInfo.vhost = ClientData->vhost;lccInfo.pwsi = &ClientData->client_wsi;lwsl_user("connecting to ws://%s:%d%s\n", lccInfo.address, lccInfo.port, lccInfo.path);return !lws_client_connect_via_info(&lccInfo);
}// lws_service回调
static int callBackFunc(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len)
{_ClientData *clientData = (_ClientData *)lws_protocol_vh_priv_get(lws_get_vhost(wsi), lws_get_protocol(wsi));switch (reason) {case LWS_CALLBACK_PROTOCOL_INIT:lwsl_user("LWS_CALLBACK_PROTOCOL_INIT\n");clientData = (_ClientData *)lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi), lws_get_protocol(wsi), sizeof(_ClientData));if (!clientData)return -1;clientData->context = lws_get_context(wsi);clientData->vhost = lws_get_vhost(wsi);clientData->port = (int *)lws_pvo_search((const struct lws_protocol_vhost_options *)in, _PORT_)->value;clientData->addr = (const char **)lws_pvo_search((const struct lws_protocol_vhost_options *)in, _ADDR_)->value;clientData->path = (const char **)lws_pvo_search((const struct lws_protocol_vhost_options *)in, _PATH_)->value;clientData->ssl = (int *)lws_pvo_search((const struct lws_protocol_vhost_options *)in, _SSL_)->value;char *pptr;pptr = (char *)lws_pvo_search((const struct lws_protocol_vhost_options *)in, _THIS_)->value;clientData->wsc = reinterpret_cast<WebsocketClient *>(pptr);if (connectServer(clientData))scheduleCallBack(wsi, LWS_CALLBACK_USER, 1);break;// 连接创建成功事件, 会调用: LWS_CALLBACK_TIMERcase LWS_CALLBACK_CLIENT_ESTABLISHED:lwsl_user("LWS_CALLBACK_CLIENT_ESTABLISHED\n");lws_set_timer_usecs(wsi, 5 * LWS_USEC_PER_SEC);break;// 写事件case LWS_CALLBACK_CLIENT_WRITEABLE:char *tmp;int sendLen, retLen;tmp = (char *)"{\"ping\":1,\"msg\":\"ping msg\"}";sendLen = strlen(tmp);char sendMsg[LWS_PRE + 1024];memcpy(sendMsg + LWS_PRE, tmp, sendLen);retLen = lws_write(wsi, ((unsigned char *)sendMsg) + LWS_PRE, sendLen, LWS_WRITE_TEXT);if (retLen < sendLen) {lwsl_err("sendMsg error\n");return -1;}lwsl_user("LWS_CALLBACK_CLIENT_WRITEABLE: %d, sendMsg: %s, sendLen: %d\n", reason, tmp, sendLen);break;// 收事件case LWS_CALLBACK_CLIENT_RECEIVE:lwsl_user("LWS_CALLBACK_CLIENT_RECEIVE: %d, receiveMsg: %s, receiveLen: %d\n", reason, in, len);memcpy(clientData->wsc->msg, (char *)in, len);lwsl_user("clientData->wsc->msg: %s\n", clientData->wsc->msg);break;// 连接错误事件, 会调用: LWS_CALLBACK_USERcase LWS_CALLBACK_CLIENT_CONNECTION_ERROR:lwsl_err("CLIENT_CONNECTION_ERROR\n");scheduleCallBack(wsi, LWS_CALLBACK_USER, 1);break;// 时间事件, 会调用: LWS_CALLBACK_CLIENT_WRITEABLEcase LWS_CALLBACK_TIMER:lws_callback_on_writable(wsi);lws_set_timer_usecs(wsi, 5 * LWS_USEC_PER_SEC);break;// 客户端session关闭事件case LWS_CALLBACK_CLIENT_CLOSED:lwsl_user("LWS_CALLBACK_CLIENT_CLOSED\n");lws_cancel_service(lws_get_context(wsi));scheduleCallBack(wsi, LWS_CALLBACK_USER, 1);break;// 自定义事件, 当前用于重连case LWS_CALLBACK_USER:lwsl_notice("%s: LWS_CALLBACK_USER\n", __func__);if (connectServer(clientData))scheduleCallBack(wsi, LWS_CALLBACK_USER, 1);break;default:break;}return 0;
}WebsocketClient::WebsocketClient()
{memset(msg, 0x00, sizeof(msg));lpvoAddr = {NULL, NULL, _ADDR_, (char *)((void *)&addr)};lpvoPort = {&lpvoAddr, NULL, _PORT_, (char *)((void *)&port)};lpvoPath = {&lpvoPort, NULL, _PATH_, (char *)((void *)&path)};lpvoSSL = {&lpvoPath, NULL, _SSL_, (char *)((void *)&ssl)};char *tmp = reinterpret_cast<char *>(this);lpvoThis = {&lpvoSSL, NULL, _THIS_, (char *)((void *)tmp)};lpvo = {NULL, &lpvoThis, "websocket-client", ""};
}bool WebsocketClient::init(int argc, const char **argv)
{struct lws_context_creation_info lcrInfo;memset(&lcrInfo, 0, sizeof lcrInfo);const char *tmpPtr = NULL;int logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE;// 连接地址if ((tmpPtr = lws_cmdline_option(argc, argv, "--addr")))addr = tmpPtr;// 连接端口if ((tmpPtr = lws_cmdline_option(argc, argv, "--port")))port = atoi(tmpPtr);// 虚拟路径if ((tmpPtr = lws_cmdline_option(argc, argv, "--path")))path = tmpPtr;// 是否使用sslif ((tmpPtr = lws_cmdline_option(argc, argv, "--ssl")))ssl = atoi(tmpPtr);if (ssl){if ((tmpPtr = lws_cmdline_option(argc, argv, "--ca_path"))){lcrInfo.client_ssl_ca_filepath = tmpPtr;  }      else {lwsl_err("cannot find ca_path\n");return false;}}// 日志输出等级if ((tmpPtr = lws_cmdline_option(argc, argv, "--log_level")))logs = atoi(tmpPtr);lws_set_log_level(logs, NULL);lcrInfo.port = CONTEXT_PORT_NO_LISTEN;  /* we do not run any server */lcrInfo.protocols = lProtocols;lcrInfo.pvo = &lpvo;lcrInfo.pt_serv_buf_size = 32 * 1024;lcrInfo.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT | LWS_SERVER_OPTION_VALIDATE_UTF8;lcrInfo.fd_limit_per_thread = 1 + 1 + 1;mContext = lws_create_context(&lcrInfo);if (!mContext) {lwsl_err("lws init failed\n");return false;}return true;
}bool WebsocketClient::Start(int argc, const char **argv)
{if (!init(argc, argv)){return false;}while (!lws_service(mContext, 0));lws_context_destroy(mContext);return true;
}

测试文件main.cpp如下:

#include "WebsocketClient.h"int main(int argc, const char **argv)
{WebsocketClient websocketClient;if (!websocketClient.Start(argc, argv)){return -1;}return 0;
}

编译命令如下:

g++ main.cpp WebsocketClient.cpp -o main -lwebsockets -Wall -std=c++11

<三>: 测试

打开一个websocket服务, 如果没有, 可以根据上篇文章编译一个echo服务. 本测试就用自己编译的echo服务, 结果如下:

结束:

如果有什么问题, 大家可以一起讨论解决.

使用linux c开源库libwebsockets编写的websocket客户端相关推荐

  1. Linux常用开源库

    前言 做应用开发的人,经常需要去封装一些接口,或者去实现一些复杂的功能.但是这些功能接口实现不仅费时,而且还不一定能保证稳定性.这也是导致很多人喜欢使用开源库的原因之一.如果满足需求,确实没必要&qu ...

  2. Linux/Centos: 开源库uthash第一弹uthash.h

    文章目录 一.简介 1.1 uthash介绍 1.2 uthash能做什么 1.3 uthash效率 1.4 源码获取 二.简单使用 2.1 定义hash数据结构 2.2 从hash表查找item 2 ...

  3. [连载 1] 如何将协议规范变成开源库系列文章之 WebSocket

    这是系列文章的第一篇,也是非常重要的一篇,希望大家能读懂我想要表达的意思. 系列文章开篇概述 相对于其他编程语言来说,Python 生态中最突出的就是第三方库.任何一个及格的 Python 开发者都使 ...

  4. Linux下开源库的使用(共享库文件头文件配置全局搜索)(WSL)

    关键词 g++; vscode; c++; pkg-config; include; lib; linux; .pc; tasks.json; launch.json; WSL 前言 需求及手动解决方 ...

  5. python 各种开源库

    测试开发 来源:https://www.jianshu.com/p/ea6f7fb69501 Web UI测试自动化 splinter - web UI测试工具,基于selnium封装. 链接 sel ...

  6. STM32HAL 移植 cJSON开源库 (裸机开发神器)

    目录 概述 一.使用方法 二.STM32CubeMx配置 三.Examples 四.运行结果 五.总结 概述 本篇文章介绍如何使用STM32引用 cJSON开源库,JSON格式在互联网数据交互时常用的 ...

  7. librdkafka开源库使用总结

    使用C/C++语言操作Kafka时,librdkafka是首选的开源库 使用librdkafka创建消费者客户端时,应配置如下属性 消费者会话组保持活动心跳间隔 自动提交偏移 自动重置偏移 自动重置偏 ...

  8. DICOM:DICOM3.0网络通信协议之“开源库实现剖析”

    背景: 日前,通过对比fo-dicom与dcm4che两种开源库(也是C#与Java两大语言体系)的不同实现来实战学习了DICOM的网络传输,博文中列举了两大开源库各自的实现特点,以及使用的语言特性. ...

  9. 30款Linux 高性能网络开发库开源软件

    转载至:https://blog.csdn.net/lifan_3a/article/details/38410081 Lua的epoll模块 lua-epoll Lua的epoll模块 更多细节,请 ...

最新文章

  1. 电源稳定性测试软件,电源测试:电源设计的稳定性测量
  2. 第二章:Android系统与嵌入式开发读书笔记
  3. python 和php的数据通信_python服务端 和 php客户端通信一
  4. boost::describe模块宏BOOST_DESCRIBE_PP_PARSE的测试程序
  5. T-SQL里数据库工程师都不知道的秘密之SQL Server自定义函数UDF
  6. 智能指针——unique_ptr
  7. 关于GestureDetector的onFling方法e1返回null问题
  8. Redis分布式快速入门
  9. php jpg转换为pdf文件,php使用ImageMagick将PDF文件转换为JPG文件的方法_PHP
  10. Soul网关源码阅读(八)路由匹配初探
  11. git学习笔记-(7-高层命令(1))
  12. idea java代码格式化_IDEA java 代码格式化统一
  13. RT-Thread在正点原子Apollo上使用MAX30102读取心率、血氧参数
  14. 开源项目_springboot的答题系统+spark大数据实时分析
  15. selenium-登录腾讯防水墙案例
  16. 专为风电筒叶片除冰研发的爬壁机器人
  17. Mac xmind 8完美破解教程
  18. Hawk-数据抓取工具:简明教程
  19. [置顶] 我奋斗了18年才和你坐在一起喝咖啡
  20. SVD求解旋转矩阵(Least-Squares Fitting of Two 3-D Point Sets论文)

热门文章

  1. 精品课程《微型计算机控制技术》电子教案ppt课件(全),《微型计算机控制技术》于海生第6章课件精品.ppt...
  2. web网站开发,web开发平台
  3. 疫苗接种预约挂号排队通知助手系统开发
  4. Node.js中exports、module.exports、require之间的关系
  5. 太阳能光伏发电系统的simulink仿真
  6. 微信小程序错误码40029 ——errcode: 40029, errmsg: invalid code, hints: [ req_id: gElDqRLnRa-744jra ]
  7. 2018年东北农业大学春季校赛 A-wyh的曲线
  8. Python获取股票数据并绘制相应K线图,看这个就够了!
  9. 破解ar9331路由器的boot密码
  10. 怎么用计算机拨号手机,怎么以以手机作为MODEM拨号上网,比如笔记本电脑在火车上...