libwebsockets实现异步websocket客户端,服务端异常断开可重连
libwebsockets
websocket客户端基本流程网上都有,我只额外优化了重连机制。
在服务器异常断开时不触发LWS_CALLBACK_CLOSED或LWS_CALLBACK_CLIENT_CONNECTION_ERROR,导致无法自动重连
通过定时检查链接是否可写入判断链接是否有效
// 判断wsi是否可用if ((std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count() -last_time) > detect_dup){if (lws_callback_on_writable(wsi) <= 0){std::cerr<< "[WebSocket] Connection failed, retrying in 3s..." << std::endl;wsi = nullptr;// continue;}last_time = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();}
完整代码
#ifndef MYWSCLIENT_H
#define MYWSCLIENT_H#pragma once#include <iostream>
#include <thread>#include <libwebsockets.h>
#include <atomic>
#include <functional>
#include <mutex>
#include <condition_variable>/*
异步启动WebSocket
自动重连
*/
class MyWSClient
{
public:using MessageCallback = std::function<void(const std::string &)>;MyWSClient(const std::string &url, int port, const std::string &path, MessageCallback onMessage = nullptr);~MyWSClient();void start();void stop();void sendMessage(const std::string &message);private:std::string url;std::string path;int port;int detect_dup = 3; // sMessageCallback onMessageCallback;struct lws_context *context;struct lws *wsi;std::thread wsThread;std::atomic<bool> running;std::mutex sendMutex;std::vector<std::string> sendQueue;void run();void reconnect();static int callback(struct lws *wsi, enum lws_callback_reasons reason,void *user, void *in, size_t len);static struct lws_protocols protocols[];
};#endif
#include "MyWSClient.h"#include "spdlog/spdlog.h"using namespace std;MyWSClient::MyWSClient(const std::string &url, int port, const std::string &path, MessageCallback onMessage): url(url), port(port), path(path), onMessageCallback(onMessage), context(nullptr), wsi(nullptr), running(false) {}MyWSClient::~MyWSClient() { stop(); }void MyWSClient::start()
{running = true;wsThread = std::thread(&MyWSClient::run, this);
}void MyWSClient::stop()
{running = false;if (context){lws_context_destroy(context);context = nullptr;}if (wsThread.joinable()){wsThread.join();}
}void MyWSClient::sendMessage(const string &message)
{if (!wsi){std::cout << __func__ << " error send, ws server not connected" << std::endl;return;}std::lock_guard<std::mutex> lock(sendMutex);sendQueue.push_back(message);if (wsi){int res = lws_callback_on_writable(wsi);std::cout << __func__ << " send :" << message << ", res:" << res << std::endl;}
}
void MyWSClient::run()
{struct lws_context_creation_info ctx_info = {};struct lws_client_connect_info conn_info = {};ctx_info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;ctx_info.port = CONTEXT_PORT_NO_LISTEN;ctx_info.protocols = protocols;context = lws_create_context(&ctx_info);if (!context){std::cerr << "[WebSocket] Failed to create context" << std::endl;return;}conn_info.context = context;conn_info.address = url.c_str();conn_info.port = port;conn_info.path = path.c_str();conn_info.host = url.c_str();conn_info.origin = url.c_str();conn_info.protocol = "ws";conn_info.ssl_connection = LCCSCF_USE_SSL |LCCSCF_ALLOW_SELFSIGNED |LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK |LCCSCF_ALLOW_EXPIRED;conn_info.userdata = this;long last_time = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();while (running){if (!wsi){std::this_thread::sleep_for(std::chrono::seconds(3));std::cout << "[WebSocket] Attempting to connect..." << std::endl;wsi = lws_client_connect_via_info(&conn_info);}// 判断wsi是否可用if ((std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count() -last_time) > detect_dup){if (lws_callback_on_writable(wsi) <= 0){std::cerr<< "[WebSocket] Connection failed, retrying in 3s..." << std::endl;wsi = nullptr;// continue;}last_time = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();}if (lws_service(context, 0) < 0){std::cerr<< "[WebSocket] lws_service failed" << std::endl;}}lws_context_destroy(context);
}void MyWSClient::reconnect()
{stop();std::cerr<< "[WebSocket] reconnect, retrying in 3s..." << std::endl;run();
}int MyWSClient::callback(lws *wsi, lws_callback_reasons reason, void *user, void *in, size_t len)
{MyWSClient *client = (MyWSClient *)lws_wsi_user(wsi);switch (reason){case LWS_CALLBACK_CLIENT_ESTABLISHED:{std::cout << "[WebSocket] Connected to server" << std::endl;lws_callback_on_writable(wsi); // 请求写入break;}case LWS_CALLBACK_CLIENT_RECEIVE:{if (client->onMessageCallback){client->onMessageCallback(std::string((char *)in, len));}break;}case LWS_CALLBACK_CLIENT_WRITEABLE:{std::lock_guard<std::mutex> lock(client->sendMutex);if (!client->sendQueue.empty()){std::string message = client->sendQueue.front();client->sendQueue.erase(client->sendQueue.begin());std::cout << __func__ << " send:" << message << std::endl;// 确保 LWS_PRE 字节已预留unsigned char buf[LWS_PRE + 1024] = {0};int msgLen = message.size();memcpy(buf + LWS_PRE, message.c_str(), msgLen);int sent = lws_write(wsi, buf + LWS_PRE, msgLen, LWS_WRITE_TEXT);if (sent < 0){std::cerr << __func__ << "lws_write failed!" << std::endl;}// 如果还有数据,继续请求写入if (!client->sendQueue.empty()){lws_callback_on_writable(wsi);}}break;}case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:{std::cerr << "[WebSocket] Connection error: " << (in ? (char *)in : "unknown") << std::endl;client->wsi = nullptr;// client->reconnect();break;}case LWS_CALLBACK_CLOSED:{std::cout << "[WebSocket] Connection closed" << std::endl;client->wsi = nullptr;// client->reconnect();break;}default:break;}return 0;
}// 定义协议数组
struct lws_protocols MyWSClient::protocols[] = {{"ws", MyWSClient::callback, 0, 4096},{nullptr, nullptr, 0, 0}};相关文章:
libwebsockets实现异步websocket客户端,服务端异常断开可重连
libwebsockets websocket客户端基本流程网上都有,我只额外优化了重连机制。 在服务器异常断开时不触发LWS_CALLBACK_CLOSED或LWS_CALLBACK_CLIENT_CONNECTION_ERROR,导致无法自动重连 通过定时检查链接是否可写入判断链接是否有效 // 判断wsi是否可用if …...
轻量级模块化前端框架:快速构建强大的Web界面
轻量级模块化前端框架:快速构建强大的Web界面 在当今快节奏的Web开发环境中,选择一个高效且灵活的前端框架至关重要。UIkit 是一个轻量级的模块化前端框架,旨在帮助开发者快速构建功能强大且响应迅速的Web界面。 UIkit提供了丰富的组件和工…...
qt+opengl 播放yuv视频
一、实现效果 二、pro文件 Qt widgets opengl 三、主要代码 #include "glwidget.h"GLWidget::GLWidget(QWidget *parent) : QOpenGLWidget(parent) {connect(&m_timer, &QTimer::timeout, this,[&](){this->update();});m_timer.start(1000/33); }v…...
UI自动化:poium测试库
以下是关于 poium 测试库 的详细介绍,涵盖其核心功能、使用方法及与原生 Selenium 的对比,帮助快速掌握这一工具: 1. poium 简介 定位:基于 Selenium 的 Page Object 模式增强库,专注于简化元素定位和页面操作。 核心…...
树莓集团落子海南,如何重构数字产业生态体系
树莓集团在海南的布局,是其整体商业战略中的关键一环。这背后,是对政策机遇、产业协同、以及区域优势的深度考量。 政策机遇 海南自贸港建设带来前所未有的政策红利,包括贸易、投资、资金等方面的自由便利。树莓集团紧抓这一机遇࿰…...
5G基本概念
作者:私语茶馆 1. 5G应用场景概述 1.1.5G应用场景 ITU域2015年定义了三大应用场景:eMBB(增强型移动宽带)、uRLLC(低时延高可靠通信)、mMTC(海量物联网通信); emBB:Enhanced Mobile Broadband ,移动互联网应用,是4G MBB(移动宽带)的升级,主要侧重于网络速率、带…...
PH热榜 | 2025-03-12
1. Fluently 标语:开始说英语,就像说你的母语一样流利。 介绍:想象一下,有一个像人类一样的英语教练,全天候在线、价格却便宜15倍。这就是 Fluently 🚀 纠正你的错误,提升你的词汇量、发音和语…...
Python Web项目的服务器部署
一.部署运行 1.虚拟环境的安装:(一行一行运行) wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh -O miniconda.sh bash miniconda.sh -b -p /opt/miniconda3 echo export PATH"/opt/miniconda3/bin:$PAT…...
[项目]基于FreeRTOS的STM32四轴飞行器: 八.遥控器摇杆
基于FreeRTOS的STM32四轴飞行器: 八.遥控器摇杆 一.摇杆数据的扫描二.处理摇杆数据三.微调按键处理 一.摇杆数据的扫描 下面摇杆初始化时,启动了ADC-DMA进行了采集,已经开始转换直接将数据通过DMA存入buff数组中: static uint16_t buff[4] …...
附下载 | 2024 OWASP Top 10 基础设施安全风险.pdf
《2024 OWASP Top 10 基础设施安全风险》报告,由OWASP(开放网络应用安全项目)发布,旨在提升企业和组织对基础设施安全风险、威胁与漏洞的意识,并提供高质量的信息和最佳实践建议。报告列出了2024年最重要的10大基础设施…...
Pytorch的一小步,昇腾芯片的一大步
Pytorch的一小步,昇腾芯片的一大步 相信在AI圈的人多多少少都看到了最近的信息:PyTorch最新2.1版本宣布支持华为昇腾芯片! 1、 发生了什么事儿? 在2023年10月4日PyTorch 2.1版本的发布博客上,PyTorch介绍的beta版本…...
C语言操作MySQL从入门到精通
大家好,我是 V 哥。今天给大家整理的内容是关于使用 C 语言操作 MySQL 数据库的详细介绍,从入门到精通,并配有案例代码和注释,帮助小白快速上手。 基本操作 1. 环境准备 在开始之前,你需要安装 MySQL 数据库和 MySQ…...
【从零开始学习计算机科学】编译原理(五)语法制导翻译
【从零开始学习计算机科学】编译原理(五)语法制导翻译 语法制导翻译语法制导定义SDDSDD的求值顺序两类重要的SDD语法制导的翻译方案SDTSDT的实现L属性定义的SDT左递归翻译方案语法制导翻译 语法表述的是语言的形式,或者说是语言的样子和结构。而程序设计语言中另一方面,是…...
uniapp uview 1.0 跨域h5配置多个代理、如何请求接口
参考文章:uniapp uView1.0跨域h5配置多个代理 官方手册:http 请求 项目中使用: 参考其他博主的文章是在manifest.json中配置代理,但在官方的手册中是直接在script请求的,我尝试请求了下没问题,上线后也不…...
化工厂防爆气象站:为石油化工、天然气等领域提供安全保障
【TH-FB02】在石油化工、天然气等高危行业中,安全生产是至关重要的。这些行业常常面临着易燃易爆、有毒有害等潜在风险,因此,对气象条件的监测和预警显得尤为重要。化工厂防爆气象站作为一种专门设计用于这些特殊环境的气象监测设备ÿ…...
Android Glide 缓存模块源码深度解析
一、引言 在 Android 开发领域,图片加载是一个极为常见且关键的功能。Glide 作为一款被广泛使用的图片加载库,其缓存模块是提升图片加载效率和性能的核心组件。合理的缓存机制能够显著减少网络请求,降低流量消耗,同时加快图片显示…...
Mac安装Neo4j图数据库
通过Homebrew 安装(推荐) 打开mac终端: 1. 安装 Homebrew(如果尚未安装) /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"2. 安装 Neo4j brew insta…...
《A Gentle Introduction to Graph Neural Networks》-GNN的综述性论文
目录 一、什么数据可以表示成一张图 (1)什么是图? (2)如何表示图的属性 (3)images as graphs(将图片表示为图) (4)text as graphs(…...
[023-01-40].第40节:组件应用 - OpenFeign与 Sentinel 集成实现fallback服务降级
SpringCloud学习大纲 一、需求说明: 需求1:通过fallback属性进行统一配置 a.问题分析: 1.需要实现cloudalibaba-consumer-nacos-order83模块通过OpenFeign调用cloudalibaba-provider-payment9001 83服务通过OpenFeign调用 9001微服务&…...
设计模式-结构型模式-装饰器模式
概述 装饰器模式 : Decorator Pattern : 是一种结构型设计模式. 作用 : 允许你动态地给对象添加功能或职责,而无需修改其原始类的代码,非常的符合 开闭原则。 实现思路 :通过创建一个包装对象(即装饰器),来…...
RK3588 编译 openssl
在编译 OpenSSL 时,你需要确保你的系统环境已经配置好了所有必要的依赖和编译工具。下面是一般步骤和一些常见问题的解决方案,特别是在使用 RK3588 这类的 ARM 处理器上。 1. 安装依赖 首先,你需要安装编译 OpenSSL 所需的依赖。这通常包括编译器(如 GCC)、make 工具、Per…...
Git前言(版本控制)
1.Git 目前世界上最先进的分布式版本控制系统。 git官网:https://git-scm.com/ 2.版本控制 2.1什么是版本控制 版本控制(Revision control)是一种在开发的过程中用于管理我们对文件、目录或工程等内容修改历史,方便查看更改历史记录备份以便恢复以前…...
visual studio配置opencv
文章目录 step1 下载opencvstep2 配置包含目录step 3 配置链接器step4 配置环境变量并重启vs2022step5 检查代码 step1 下载opencv 下载 opencv-4.8.0-windows.exe https://cloud.189.cn/web/share?codefUnqEb7naUra step2 配置包含目录 step 3 配置链接器 step4 配置环境变…...
docker修改daemon.json文件后无法启动
1.问题描述 使用阿里云docker镜像安装的docker,安装成功后默认可以启动。但是修改daemon.json配置后docker服务无法启动,提示如下错误: 从上图发现,docker服务默认使用阿里docker镜像仓库 2.解决方法 根据提示找到docker服务目…...
Linux网络:网络与操作系统1
本文是介绍网络的基本结构,以及和OS之间有什么关系 OSI七层模型 引入 使用网络是为了解决信息的长距离传送,那就需要解决四个问题: 接收方如何使用数据传输的可靠性主机如何定位数据包在局域网如何转发 人们选择用网络协议(t…...
Manus(一种AI代理或自动化工具)与DeepSeek(一种强大的语言模型或AI能力)结合使用任务自动化和智能决策
一、Manus与DeepSeek差异 十分好奇DeepSeek和Manus究竟谁更厉害些,DeepSeek是知识型大脑,Manus则是全能型执行者。即DeepSeek专注于语言处理、知识整合与专业文本生成。其核心优势在于海量参数支持的深度学习和知识推理能力,例如撰写论文、润…...
Python个人学习笔记(14):函数(匿名函数、内置函数(下)、三元表达式)
九、匿名函数 lambda表达式 语法规则: 变量 lambda 参数1,参数2,…:返回值 例:用lambda简化下述操作 def func(a,b):return ab ret func(1, 2) print(ret)代码: fn lambda a,b:ab print(fn) print(fn(12,13))结果: <fun…...
姚安娜新剧瘦了一圈,《仁心俱乐部》急诊医生顾诗宜在线上岗
《仁心俱乐部》在芒果 TV 播出,湖南卫视金鹰独播剧场也随之播出,这一剧集受到了不少观众的关注。姚安娜在剧中饰演的急诊科医生顾诗宜,她为患者检查身体时动作娴熟,与患者沟通时展现出的耐心和专注,都展现出很高的专业…...
【PyCharm】Python和PyCharm的相互关系和使用联动介绍
李升伟 整理 Python 是一种广泛使用的编程语言,而 PyCharm 是 JetBrains 开发的专门用于 Python 开发的集成开发环境(IDE)。以下是它们的相互关系和使用联动的介绍: 1. Python 和 PyCharm 的关系 Python:一种解释型、…...
【ES6】基础特性总结
概述 仅个人使用,复习ES6的笔记,比较粗糙,仅适用于浏览器端。 数据类型 ES6(ECMAScript 2015)引入了一些新的数据类型和对现有数据类型的扩展。以下是ES6中数据类型的一个简要总结表格: 数据类型描述Nu…...
