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

一种libuv实现websockets服务的解决方案

方法是libuv用多事件循环来驱动。说起来容易,做起来还是比下面的方法更容易:

在这里插入图片描述

上图是某位网友的方法代表子大部分网络资料。此方法对部署不友好,因为软件仓库提供的libwebsockets是不能用了。如何简化部署,利用好现有的软件仓库呢?

libwebsockets版本历史

libwebsockets曾经是无缝支持libuv的。不过,随着版本号的增大,它对libuv的支持越来越差了。首先快速回顾下libwebsockets的版本历史:

  • v0.1.0 - 2011年9月:这是libwebsockets的第一个版本,它提供了基本的WebSocket协议支持,包括创建WebSocket连接、发送和接收消息等基本功能。
  • v0.2.0 - 2012年1月:这个版本添加了对HTTP协议的支持,使得libwebsockets可以处理HTTP请求和响应。此外,还添加了对多线程的支持,使得你可以在多个线程中安全地使用libwebsockets。
  • v0.3.0 - 2012年7月:这个版本添加了对SSL/TLS加密的支持,使得你可以使用安全连接来传输WebSocket消息。此外,还添加了一些新的API和功能,比如定时器和回调函数等。
  • v0.4.0 - 2013年1月:这个版本添加了对HyBi-17协议的支持,这是WebSocket协议的一个扩展版本,提供了更多的功能和更好的性能。此外,还修复了一些已知的漏洞和错误。
  • v0.5.0 - 2014年2月:这个版本添加了对多路复用和支持,这意味着你可以同时处理多个WebSocket连接。此外,还添加了一些新的API和功能,比如获取连接信息、处理文件上传等。
  • v1.0.0 - 2015年9月:这是libwebsockets的一个重要版本,它标志着libwebsockets已经成熟并稳定。这个版本添加了对HTTP/2协议的支持,同时优化了性能和内存使用。此外,还修复了一些已知的漏洞和错误。
  • v1.1.0 - 2016年3月:这个版本主要修复了一些已知的漏洞和错误,同时添加了一些新的功能和优化,比如更好的日志记录和内存管理。
  • v2.0.0 - 2018年8月:这是一个具有里程碑意义的版本,它引入了许多新的功能和改变。其中包括更好的多线程支持、对更多协议的支持(如Raw WebSocket、HyBi-17、HTTP等),以及对更多操作系统的支持。此外,它还改进了API设计,并修复了许多已知问题。
  • v2.1.0 - 2019年4月:这个版本主要修复了一些已知的漏洞和错误,同时添加了一些新的功能和优化,比如更好的SSL/TLS支持和对更多操作系统的支持。
  • v3.0.0 - 2020年5月:这个版本标志着libwebsockets进入了一个新的阶段。它引入了更多新的功能和改变,包括更好的多线程支持、对更多协议的支持(如Raw WebSocket、HyBi-17、HTTP等),以及对更多操作系统的支持。此外,它还改进了API设计,并修复了许多已知问题。从这个版本开始,事件循环对libuv的支持逐渐边缘化。

关键问题

  1. 从v3.0.0开始,lws_uv_initloop函数不存在。
  2. lws_context_create_info.options增加LWS_SERVER_OPTION_LIBUV导致lws_service函数崩溃。

复现问题的常规操作:

  1. 编译时开启LIBUV支持:在编译libwebsockets库时,需要开启LIBUV支持并指定LIBUV的目录位置。这可以通过修改CMakeLists.txt文件来实现。具体来说,需要在CMakeLists.txt文件中添加以下行:

    option(WITH_LIBUV "Enable libuv support" ON)  
    find_package(libuv REQUIRED)  
    include_directories(${LIBUV_INCLUDE_DIRS})  
    link_directories(${LIBUV_LIBRARY_DIRS})  
    add_definitions(-DLIBUV_SUPPORT=1)
    

    这将启用LIBUV支持,并自动搜索和链接LIBUV库。

  2. 修改线程模型:libwebsockets默认使用自己的线程模型,但可以使用libuv的线程模型替代。这需要在初始化libwebsockets时指定使用libuv线程模型。具体来说,需要在调用lws_create_context函数时,将thread_mode参数设置为LWS_THREAD_MODE_UV。例如:

    struct lws_context_creation_info info;  
    memset(&info, 0, sizeof(info));  
    info.port = 8000;  
    info.protocols = protocols;  
    info.thread_mode = LWS_THREAD_MODE_UV; // 使用libuv线程模型  
    struct lws_context *context = lws_create_context(&info);
    

    这将告诉libwebsockets使用libuv的线程模型进行初始化。

  3. 使用libuv的API:在使用libwebsockets时,需要使用libuv提供的API来进行读写操作和事件处理。例如,可以使用uv_read_startuv_write_t等函数来进行读写操作,使用uv_run函数来进行事件循环。这些函数将在libuv库中提供。例如:

    uv_stream_t *stream;  
    uv_buf_t buf;  
    uv_read_start(stream, on_read, &buf); // 开始读取操作  
    uv_write(&req, stream, buf.base, nread, on_write); // 开始写入操作  
    uv_run(uv_default_loop(), UV_RUN_DEFAULT); // 运行事件循环
    

解决思路

每个线程一个循环。可以在同一线程中使用多个事件循环。但这通常没有意义,因为一个循环的 uv_run() 调用将阻止并停止另一个循环的运行。通过仔细组合 uv_run(loop, UV_RUN_ONCE) 你可以做一些非常有趣的事情。您可以使用多个循环在程序中创建“模态”步骤,其中第二个事件循环“暂停”第一个事件循环,直到发生某些操作(用户按 Return 键或您收到新事件或其他事件)。

有一个非常具体的用例,可以使用两个事件循环作为同步机制来代替条件变量。当时 libuv 没有条件变量支持,现在我保持这种方式,以允许它与早期的节点版本一起使用。具体用例是:

  1. 主线程使用 uv_queue_work() 在工作线程中调用阻塞函数。
  2. 工作线程必须调用自定义函数。问题是自定义函数必须在主线程上运行。
  3. 工作线程必须等待该函数返回。

条件变量方法是:

  1. 工作线程不直接调用自定义函数。相反,它创建一个 uv_async_t 处理程序。此处理程序的回调调用自定义函数。
  2. 初始化条件变量。
  3. 它使用 uv_async_send() 来让主线程(事件循环运行的地方)代表它调用该函数。
  4. 等待条件变量。
  5. 回调调用自定义函数,然后向条件变量发出信号,让工作线程继续运行。

事件循环实现改为:

  1. 在工作线程中创建一个新的事件循环。
  2. uv_async_t 与这个新循环关联起来。
  3. 通过原始 uv_async_t 处理程序的数据字段将此处理程序传递到主线程。
  4. uv_run() 新的事件循环,现在会阻塞,因为异步处理程序已经增加了它的 refcount
  5. 主线程中的回调调用自定义函数,然后使用 uv_async_send() 向新循环上的异步处理程序发出信号。
  6. 该异步处理程序的回调只是关闭处理程序本身,新循环的引用计数降至零,uv_run() 返回并且工作线程可以继续。

解决办法

下面的示例由TCP服务和websockets服务组成。TCP服务只是一个echo服务端,websockets则是静态文件服务,仅两个网页:index.html和404.html。

TCP线程

void tcp_thread_cb(void* args)
{uv_loop_t loop;uv_loop_init(&loop);struct sockaddr_in addr;uv_tcp_t server;int ierr = uv_tcp_init(&loop, &server);VOID_RETURN(ierr);ierr = uv_ip4_addr("0.0.0.0", IPORT, &addr);VOID_RETURN(ierr);ierr = uv_tcp_bind(&server, (const struct sockaddr*)&addr, 0);VOID_RETURN(ierr);ierr = uv_listen((uv_stream_t*)&server, 128, on_new_connection);VOID_RETURN(ierr);printf("server started.\n");ierr = uv_run(&loop, UV_RUN_DEFAULT);VOID_RETURN(ierr);uv_stop(&loop);uv_barrier_wait((uv_barrier_t*)args);
}

websockets线程

void websockets_thread_cb(void* args)
{static struct lws_context* context;static const struct lws_http_mount mounts[] = { {/* .mount_next */		NULL,		/* linked-list "next" *//* .mountpoint */		"/",		/* mountpoint URL *//* .origin */			".", /* serve from dir *//* .def */			"index.html",	/* default filename *//* .protocol */			NULL,/* .cgienv */			NULL,/* .extra_mimetypes */		NULL,/* .interpret */		NULL,/* .cgi_timeout */		0,/* .cache_max_age */		0,/* .auth_mask */		0,/* .cache_reusable */		0,/* .cache_revalidate */		0,/* .cache_intermediaries */	0,/* .origin_protocol */		LWSMPRO_FILE,	/* files in a dir *//* .mountpoint_len */		1,		/* char count *//* .basic_auth_login_file */	NULL,},{0,} };struct lws_context_creation_info info = { 0, };lws_set_log_level(LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE, NULL);info.port = 20001;info.mounts = mounts;info.error_document_404 = "/404.html";info.options = LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE;context = lws_create_context(&info);assert(context);lwsl_user("websockets started\n");while (1)lws_service(context, 0);lws_context_destroy(context);uv_barrier_wait((uv_barrier_t*)args);
}

主函数

int main(int argc, char** argv)
{uv_loop_t* loop = uv_default_loop();uv_thread_t t[2];uv_barrier_t b;uv_barrier_init(&b, 3);int ierr = uv_thread_create(&t[0], tcp_thread_cb, &b);RAISE_RETURN(ierr);ierr = uv_thread_create(&t[1], websockets_thread_cb, &b);RAISE_RETURN(ierr);ierr = uv_run(loop, UV_RUN_DEFAULT);RAISE_RETURN(ierr);uv_barrier_wait(&b);return 0;
}

通用部分

#include <uv.h>
#include <libwebsockets.h>
#include <stdio.h>
#include <malloc.h>
#include <assert.h>#define RAISE_RETURN(x) \if((x)) \{ \fprintf(stderr, "error {%s} code %ld %s\n", __func__, (long int)(x), uv_strerror(x)); \printf("error {%s} code %ld %s\n", __func__, (long int)(x), uv_strerror(x)); \raise(x); \return (x); \}
#define VOID_RETURN(x) \if((x)) \{ \fprintf(stderr, "error {%s} code %ld %s\n", __func__, (long int)(x), uv_strerror(x)); \printf("error {%s} code %ld %s\n", __func__, (long int)(x), uv_strerror(x)); \return; \}

验证

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在这里插入图片描述

在这里插入图片描述

作者:岬淢箫声
日期:2023年11月7日
版本:1.0
链接:http://caowei.blog.csdn.net

相关文章:

一种libuv实现websockets服务的解决方案

方法是libuv用多事件循环来驱动。说起来容易&#xff0c;做起来还是比下面的方法更容易&#xff1a; 上图是某位网友的方法代表子大部分网络资料。此方法对部署不友好&#xff0c;因为软件仓库提供的libwebsockets是不能用了。如何简化部署&#xff0c;利用好现有的软件仓库呢&…...

MobaXterm配置SSHTunnel

本地与远程服务器之间存在防火墙&#xff0c;防火墙只允许SSH端口通过&#xff0c;为访问远程服务器&#xff0c;我们可以借助MobaXterm来与SSH服务器建立隧道&#xff0c;使得防火墙外的用户能够访问远程服务器 配置 打开SSHTunnel 新建SSH tunnel 点击开启就生效了&…...

MySQL中的datetime和timestamp有什么区别

相同点: 存储格式相同 datetime和timestamp两者的时间格式都是YYYY-MM-DD HH:MM:SS 不同点: 存储范围不同. datetime的范围是1000-01-01到9999-12-31. 而timestamp是从1970-01-01到2038-01-19, 即后者的时间范围很小. 与时区关系. datetime是存储服务器当前的时区. 而timesta…...

如何开发一个求职招聘小程序?详细步骤解析与教程

一、确定需求和功能 在开发求职招聘小程序之前&#xff0c;需要明确需求和功能。通过对市场和用户需求的调研和分析&#xff0c;确定小程序需要具备哪些功能&#xff0c;如职位发布、简历投递、在线沟通、面试安排等。 二、选择开发方式 求职招聘小程序的开发方式有多种选择…...

安克创新音频算法工程师(应届生)招聘

职位描述&#xff1a; 负责音频处理算法的研发和优化&#xff0c;包括但不限于噪声抑制、回声消除、声反馈抑制、音效、声纹、唤醒、指令词识别等。 持续跟进国际前沿技术方向&#xff0c;预研端侧可落地的音频技术&#xff0c;打造技术影响力。 对音频处理系统进行模拟和实验…...

Ubuntu 22.04.3 LTS中安装singularity

文章目录 概要背景知识什么是singularity ? 安装流程1. 安装Go2. 下载Singularity3. 编译Singularity源代码 4. 验证安装是否成功singularity的使用安装open structure 小结 概要 这里主要记录singularity的安装和使用&#xff0c;安装过程中会出现相关的错误&#xff0c;所以…...

NVM安装node后提示没有对应npm包(即:无法将“npm”项识别为 cmdlet、函数、脚本文件)

背景 windows11 node版本降低到v12.22.12后&#xff0c;执行&#xff1a;nvm -v npm -v npm : 无法将“npm”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。请检查名称的拼写&#xff0c;如果 包括路径&#xff0c;请确保路径正确&#xff0c;然后再试一次。 所在位置 …...

路由器基础(十一):ACL 配置

访问控制列表 (Access Control List,ACL) 是目前使用最多的访问控制实现技术。访问控制列表是路由器接口的指令列表&#xff0c;用来控制端口进出的数据包。ACL适用于所有的被路由协议&#xff0c;如IP、IPX、AppleTalk 等。访问控制列表可以分为基本访问控制列表和高级访问控制…...

【今日文章】:如何用css 实现星空效果

【今日文章】&#xff1a;如何用css 实现星空效果 需求实现tips: 需求 用CSS 实现星空效果的需求&#xff1a; 屏幕上有“星星”&#xff0c;且向上移动。移动的时候&#xff0c;动画效果要连贯&#xff0c;不能出现闪一下的样子。 实现 这里我们需要知道&#xff0c;“星星”是…...

HackTheBox-Starting Point--Tier 1---Three

文章目录 一 题目二 实验过程 一 题目 Tags Web、Cloud、Custom Applications、AWS、AWS、Reconnaissance、Web Site Structure Discovery、Bucket Enumeration、Arbitrary File Upload、Anonymous/Guest Access译文&#xff1a;Web、云、定制应用程序、AWS、AWS、侦察、网站…...

Linux Alsa声卡驱动(2):Machine驱动

一:Simple Card Simple Card是ASoC通用的machine driver,可支持大部分标准声卡。 驱动:kernel/sound/soc/generic/simple-card.c compatible = "simple-audio-card"; 1、设备树属性 (1)协议 属性协议格式描述simple-audio-card,format i2si2s标准格式right_j…...

某综合性能源集团绩效考核设计项目纪实

——设置分层分类的考核指标、建立多维度评价体系&#xff0c;增加考核结果信服力 【客户行业】能源行业 【问题类型】薪酬管理 【客户背景】 某综合性能源跨国集团是一家专注于能源加工行业的民营跨国企业&#xff0c;业务覆盖能源工程建设、高端装备制造、能源勘探开发、专…...

ubuntu18.04 通过创建服务实现开机自启, 启动指定脚本

下面是具体的操作过程 切换目录到root cd /root/ 新建一个脚本 vi myscript.sh 添加内容&#xff1a;设置指定网卡的ip地址 (这里根据自己需要修改) #!/bin/bash ifconfig enp0s3 10.20.60.113 赋予执行权限 chmod x myscript.sh 新建一个服务 vi /etc/systemd/system/myscr…...

Tomcat 9.0.x 源码编译

文章目录 一、克隆源码二、构建 Maven1&#xff09;在项目根目录中新建 pom.xml 文件2&#xff09;然后 Add Maven Projects 三、在目录中增加 home 目录四、增加启动配置五、其它问题1&#xff09;控制台乱码解决 2&#xff09;启动后访问 localhost:8080 报错解决 一、克隆源…...

基于SSM的旅游管理系统的设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…...

多目标优化中的“latent action”是什么?

2020 NeurIPS 中的“latent action”&#xff1a; Our model defines latent action as a boundary that splits the region represented by a node into a high-performing and a low performing region. 这里的latent action代表一个边界&#xff08;分类器&#xff09;&…...

上海亚商投顾:三大指数小幅下跌 CPO、算力板块集体爆发

上海亚商投顾前言&#xff1a;无惧大盘涨跌&#xff0c;解密龙虎榜资金&#xff0c;跟踪一线游资和机构资金动向&#xff0c;识别短期热点和强势个股。 一.市场情绪 沪指早间低开后震荡&#xff0c;午后一度拉升翻红&#xff0c;创业板指盘中跌近1%&#xff0c;随后探底回升跌…...

【C语法学习】19 -关闭和刷新文件

文章目录 1 关闭文件1.1 fclose()函数1.1.1 函数原型1.1.2 参数1.1.3 返回值 1.2 fcloseall()函数1.2.1 函数原型1.2.2 参数1.2.3 返回值 2 刷新文件2.1 缓冲区的概念2.2 缓冲区的刷新2.2.1 fflush()函数2.2.1.1 函数原型2.2.1.2 参数2.2.1.3 返回值 2.2.2 flushall()函数2.2.2…...

制作吉他谱软件Guitar Pro8中文版本

前面提到了使用Guitar Pro制作吉他谱的步骤&#xff0c;除此以外&#xff0c;在最新的Guitar Pro 8版本中&#xff0c;还新增了制作简谱的功能。 在窗口右侧的乐谱中&#xff0c;选择简谱按钮&#xff0c;可以打开乐谱的简谱编辑模式。 Guitar Pro-Guitar Pro 8 win-安装包ht…...

SpringBoot整合JUnit

1.创建新项目 说明&#xff1a;创建springboot_04_junit项目&#xff0c;选择对应的版本。 2.接口类 说明&#xff1a;新建BookDao接口。 package com.forever.dao;public interface BookDao {public void save(); }3.实现类 说明&#xff1a; 新建BookDaoImpl实现类。 pa…...

测试微信模版消息推送

进入“开发接口管理”--“公众平台测试账号”&#xff0c;无需申请公众账号、可在测试账号中体验并测试微信公众平台所有高级接口。 获取access_token: 自定义模版消息&#xff1a; 关注测试号&#xff1a;扫二维码关注测试号。 发送模版消息&#xff1a; import requests da…...

【入坑系列】TiDB 强制索引在不同库下不生效问题

文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...

从零开始打造 OpenSTLinux 6.6 Yocto 系统(基于STM32CubeMX)(九)

设备树移植 和uboot设备树修改的内容同步到kernel将设备树stm32mp157d-stm32mp157daa1-mx.dts复制到内核源码目录下 源码修改及编译 修改arch/arm/boot/dts/st/Makefile&#xff0c;新增设备树编译 stm32mp157f-ev1-m4-examples.dtb \stm32mp157d-stm32mp157daa1-mx.dtb修改…...

【服务器压力测试】本地PC电脑作为服务器运行时出现卡顿和资源紧张(Windows/Linux)

要让本地PC电脑作为服务器运行时出现卡顿和资源紧张的情况&#xff0c;可以通过以下几种方式模拟或触发&#xff1a; 1. 增加CPU负载 运行大量计算密集型任务&#xff0c;例如&#xff1a; 使用多线程循环执行复杂计算&#xff08;如数学运算、加密解密等&#xff09;。运行图…...

AI编程--插件对比分析:CodeRider、GitHub Copilot及其他

AI编程插件对比分析&#xff1a;CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展&#xff0c;AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者&#xff0c;分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...

让回归模型不再被异常值“带跑偏“,MSE和Cauchy损失函数在噪声数据环境下的实战对比

在机器学习的回归分析中&#xff0c;损失函数的选择对模型性能具有决定性影响。均方误差&#xff08;MSE&#xff09;作为经典的损失函数&#xff0c;在处理干净数据时表现优异&#xff0c;但在面对包含异常值的噪声数据时&#xff0c;其对大误差的二次惩罚机制往往导致模型参数…...

基于SpringBoot在线拍卖系统的设计和实现

摘 要 随着社会的发展&#xff0c;社会的各行各业都在利用信息化时代的优势。计算机的优势和普及使得各种信息系统的开发成为必需。 在线拍卖系统&#xff0c;主要的模块包括管理员&#xff1b;首页、个人中心、用户管理、商品类型管理、拍卖商品管理、历史竞拍管理、竞拍订单…...

三分算法与DeepSeek辅助证明是单峰函数

前置 单峰函数有唯一的最大值&#xff0c;最大值左侧的数值严格单调递增&#xff0c;最大值右侧的数值严格单调递减。 单谷函数有唯一的最小值&#xff0c;最小值左侧的数值严格单调递减&#xff0c;最小值右侧的数值严格单调递增。 三分的本质 三分和二分一样都是通过不断缩…...

Vite中定义@软链接

在webpack中可以直接通过符号表示src路径&#xff0c;但是vite中默认不可以。 如何实现&#xff1a; vite中提供了resolve.alias&#xff1a;通过别名在指向一个具体的路径 在vite.config.js中 import { join } from pathexport default defineConfig({plugins: [vue()],//…...

Scrapy-Redis分布式爬虫架构的可扩展性与容错性增强:基于微服务与容器化的解决方案

在大数据时代&#xff0c;海量数据的采集与处理成为企业和研究机构获取信息的关键环节。Scrapy-Redis作为一种经典的分布式爬虫架构&#xff0c;在处理大规模数据抓取任务时展现出强大的能力。然而&#xff0c;随着业务规模的不断扩大和数据抓取需求的日益复杂&#xff0c;传统…...