libwebsockets 简介

文章目录

  • 1. 前言
  • 2. libwebsockets 的 编译 和 使用
    • 2.1 编译
    • 2.2 使用
      • 2.2.1 构建运行上下文
      • 2.2.2 事件处理循环
  • 3. websocket 协议

1. 前言

限于作者能力水平,本文可能存在谬误,因此而给读者带来的损失,作者不做任何承诺。

2. libwebsockets 的 编译 和 使用

2.1 编译

先到网站 libwebsockets 下载源码包,解压后,编译源码目录下的文件 cross-arm-linux-gnueabihf.cmake ,配置交叉编译环境(假定为 ARM 架构交叉编译 libwebsockets):

#
# CMake Toolchain file for crosscompiling on ARM.
#
# This can be used when running cmake in the following way:
#  cd build/
#  cmake .. -DCMAKE_TOOLCHAIN_FILE=../cross-arm-linux-gnueabihf.cmake
#set(CROSS_PATH /path/to/cross-compiler)# Target operating system name.
set(CMAKE_SYSTEM_NAME Linux)# Name of C compiler.
set(CMAKE_C_COMPILER "${CROSS_PATH}/bin/arm-linux-gnueabihf-gcc")
set(CMAKE_CXX_COMPILER "${CROSS_PATH}/bin/arm-linux-gnueabihf-g++")# Where to look for the target environment. (More paths can be added here)
set(CMAKE_FIND_ROOT_PATH "${CROSS_PATH}")# Adjust the default behavior of the FIND_XXX() commands:
# search programs in the host environment only.
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)# Search headers and libraries in the target environment only.
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)

其中,CROSS_PATH 设置为实际的交叉编译器目录。接下来编辑 CMakeLists.txt ,按需配置 libwebsockets 支持的功能特性(主要是配置 options)。如果要编译全部的调试代码,打开 _DEBUG 宏:

#if (LWS_MBED3)set(CMAKE_C_FLAGS "-D_DEBUG ${CMAKE_C_FLAGS}")
#endif()

笔者这里的源码版本,只有在 LWS_MBED3 开启时,才编译所有的调试信息代码,为了调试方便,这里注释掉 CMakeLists.txt 两行。注意,开启 _DEBUG 宏并不是意味着启用了所有调试信息,它只是将所有的调试代码编译进去了,要启用所有的调试信息,还需要通过接口 lws_set_log_level() 开启相应的调试信息。
接下来建立一个编译目录 build,然后进行编译:

$ mkdir build
$ cd build
$ cmake .. -DCMAKE_INSTALL_PREFIX=`pwd`/_install \
-DCMAKE_TOOLCHAIN_FILE=../cross-arm-linux-gnueabihf.cmake \
-DWITHOUT_EXTENSIONS=1 -DWITH_SSL=0
$ make -j8 # 按实际的处理器个数设定 -j 参数
$ make install

编译生成的所有文件都位于 build 目录下,供程序使用的文件安装在 build/_install 目录下:

$ cd _install
$ tree
.
├── bin
│   ├── libwebsockets-test-client
│   ├── libwebsockets-test-echo
│   ├── libwebsockets-test-fraggle
│   ├── libwebsockets-test-fuzxy
│   ├── libwebsockets-test-ping
│   ├── libwebsockets-test-server
│   ├── libwebsockets-test-server-extpoll
│   └── libwebsockets-test-server-pthreads
├── include
│   ├── libwebsockets.h
│   └── lws_config.h
├── lib
│   ├── cmake
│   │   └── libwebsockets
│   │       ├── LibwebsocketsConfig.cmake
│   │       ├── LibwebsocketsConfigVersion.cmake
│   │       ├── LibwebsocketsTargets.cmake
│   │       └── LibwebsocketsTargets-release.cmake
│   ├── libwebsockets.a
│   ├── libwebsockets.so -> libwebsockets.so.9
│   ├── libwebsockets.so.9
│   └── pkgconfig
│       └── libwebsockets.pc
└── share└── libwebsockets-test-server├── favicon.ico├── leaf.jpg├── libwebsockets.org-logo.png├── lws-common.js└── test.html8 directories, 23 files

2.2 使用

程序代码通过头文件 include/libwebsockets.h 导入 libwebsockets 的接口。下面通过 libwebsockets 来构建一个 webserver

2.2.1 构建运行上下文

主要构建 server 监听套接字:

#include <libwebsockets.h>// 按需自定义的 web server 数据
struct web_server_data {...
};struct lws_protocols lws_protos[] = {{ "ws", web_server_callback, sizeof(struct web_server_data), 3 * 1024 * 1024 },{ "http", lws_callback_http_dummy, 0, 0 },{ NULL, NULL,   0 }
};struct lws_http_mount http_mount = {/* .mount_next */               NULL,           /* linked-list "next" *//* .mountpoint */               "/",            /* mountpoint URL *//* .origin */                   "web",            /* serve from dir *//* .def */                      "test.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 */
};struct lws_context *context;
struct lws_context_creation_info ctx_info = { 0 };ctx_info.port = 8000;
ctx_info.iface = NULL; 
ctx_info.protocols = lws_protos;
ctx_info.gid = -1; 
ctx_info.uid = -1; 
ctx_info.options = LWS_SERVER_OPTION_DISABLE_IPV6;
ctx_info.mounts = &http_mount;
context = lws_create_context(&ctx_info);
lws_create_context()struct lws_context *context = NULL;...context = lws_zalloc(sizeof(struct lws_context));...if (lws_plat_init(context, info))goto bail;...if (!lws_check_opt(info->options, LWS_SERVER_OPTION_EXPLICIT_VHOSTS))// 创建 libwebsocket server [套接字 + lws]if (!lws_create_vhost(context, info)) {lwsl_err("Failed to create default vhost\n");return NULL;}...return context;

2.2.2 事件处理循环

// 事件处理循环:
// . 客户端连接建立、断开
// . 和客户端通信
while (1) {lws_service(context, 1000);msleep(1);
}

libwebsockets 预定义的了一些通知信息,在事件处理循环里传递给 web_server_callback() 处理:

// 建立客户端连接上下文
lws_service()lws_plat_service()lws_plat_service_tsi()...// 读取 server 监听套接字状态, 看 是否有连接进来// 读取 client 套接字状态, 看 是否有数据可读n = poll(pt->fds, pt->fds_count, timeout_ms);.../* any socket with events to service? */for (n = 0; n < pt->fds_count && c; n++) { // 处理所有 poll fd 事件...m = lws_service_fd_tsi(context, &pt->fds[n], tsi);...// 超时处理if (context->last_timeout_check_s != now) { // 秒级精度context->last_timeout_check_s = now; // poll fd 时间时候, 更新 context 时间...// 超时监测: // lws_set_timeout(wsi, reason, secs) 添加的超时监测列表wsi = context->pt[tsi].timeout_list;while (wsi) {/* we have to take copies, because he may be deleted */wsi1 = wsi->timeout_list;tmp_fd = wsi->sock;if (lws_service_timeout_check(wsi, (unsigned int)now)) { // 如果监测到超时对象, 关闭它并释放资源/* he did time out... */if (tmp_fd == our_fd)/* it was the guy we came to service! */timed_out = 1; // 标记监测到超时/* he's gone, no need to mark as handled */}wsi = wsi1;}}...switch (wsi->mode) {...case LWSCM_SERVER_LISTENER: // server 监听套接字......n = lws_server_socket_service(context, wsi, pollfd); // 客户端连接上下文的建立...switch (wsi->mode) {...case LWSCM_SERVER_LISTENER:#if LWS_POSIX/* pollin means a client has connected to us then */do {.../* listen socket got an unencrypted connection... */clilen = sizeof(cli_addr);...accept_fd  = accept(pollfd->fd, (struct sockaddr *)&cli_addr, &clilen); // 获取客户端连接套接字句柄...} ;#endif...}...goto handled;// 其它事件处理,这里不细表}...}
// 完成客户端握手
lws_service()...lws_plat_service_tsi()...n = poll(pt->fds, pt->fds_count, timeout_ms);...for (n = 0; n < pt->fds_count && c; n++) { // 处理所有 poll fd 事件...m = lws_service_fd_tsi(context, &pt->fds[n], tsi);...switch (wsi->mode) {...case LWSCM_WS_SERVING:......n = lws_read(wsi, (unsigned char *)eff_buf.token, eff_buf.token_len);...switch (wsi->state) {...case LWSS_HTTP_HEADERS:...last_char = buf;if (lws_handshake_server(wsi, &buf, len))/* Handshake indicates this session is done. */goto bail;}...}...}
lws_handshake_server(wsi, &buf, len)...while (len--) { // 解析 http 信息,完成和客户端的握手...if (wsi->protocol->callback)// 给回调 web_server_callback() 发 LWS_CALLBACK_ESTABLISHED 通知if (wsi->protocol->callback(wsi, LWS_CALLBACK_ESTABLISHED,wsi->user_space,...}
int web_server_callback(struct lws *wsi, enum lws_callback_reasons reason,void* user, void* in, size_t len)
{...switch (reason) { // 不需要处理所有类型的通知,只处理必要的、需要的通知类型case LWS_CALLBACK_ESTABLISHED:...break;case LWS_CALLBACK_RECEIVE:...break;case LWS_CALLBACK_SERVER_WRITEABLE:...break;case LWS_CALLBACK_CLOSED:...break;default:break;}return 0;
}

3. websocket 协议

websocket 协议工作在应用层,基于 TCP 套接字实现。websocket 涉及的协议包括 RFC6455 和 RFC7936 。知乎博文 WebSocket 协议完整解析 是一个不错的参考。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/357.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

EASERVER管理密码忘记的处理方式

一个多年的老客户&#xff0c;EASERVER的管理员密码忘记了&#xff0c;查了一些资料&#xff0c;把它重置了&#xff0c;做个记录 This is a really quick tip on how to reset password for the administrator id “jagadmin” used in EA Server. We use this id for everyt…

Java 笔记 01:Java 概述,MarkDown 常用语法整理

一、前言 记录时间 [2024-04-18] 昨天整理完 Docker 基础后略微思索了一下&#xff0c;还是决定把 Java 捡起来&#xff0c;系统地学习一遍&#xff0c;参考的学习课程是狂神说 Java 零基础&#xff0c;真诚感激此系列视频对笔者的帮助。 零基础可以学 Java 吗&#xff1f;只要…

JVS低代码平台表单引擎:字符串拼接与逻辑函数的完美结合

字符串拼接使用逻辑函数配置 示例场景&#xff1a;通过按钮触发逻辑使用函数将两个日期字段组合为范围时间类型。 选择开始日期和结束日期后&#xff0c;点击【合并】按钮自动处理回显至起止日期字段。在【合并】按钮上设置逻辑。 注意&#xff1a;这里【起止日期】组件是数组…

【web开发02】后端开发Maven

后端开发Maven 1 Maven是什么&#xff1f;1.1 Maven基础概念1.1.2 仓库1.1.3 坐标 2 配置maven环境3 创建maven项目4 导入maven项目4 依赖管理4.1 依赖配置4.2 依赖传递4.2.1 排除依赖 4.3 依赖范围4.4 生命周期4.4.1 运行生命周期 1 Maven是什么&#xff1f; Maven本质是项目…

无梯度计算模式

无梯度计算模式是指在进行优化、求解或模型训练时&#xff0c;不依赖于目标函数或其相关组件&#xff08;如损失函数、约束函数等&#xff09;的梯度信息来指导搜索或更新过程的方法。这种模式适用于以下几种情况&#xff1a; 梯度不可用或难以计算&#xff1a; 目标函数可能包…

android和java 线程Tread

1。线程的生命周期。 可以分为创建,就绪,运行,阻塞,死亡 5个状态。 1.1 创建 new :当程序new了一个线程后,线程就处于新建状态,这时候他和其他 java对象一样,被java虚拟机分配了内存,但没有线程的特性。 …

idea 切换分支后 Project目录树小消失

idea 切换分支后 Project目录树小消失 生气了 生气了 若代码已经保存提交&#xff0c;直接删除本地项目&#xff0c;重新拉取。

二次元AI绘画生成器免费:教你生成精美图片

二次元AI绘画生成器&#xff0c;无疑是现代技术与艺术完美结合的典范。这些工具不仅将复杂的绘画过程简化&#xff0c;更让每一个艺术爱好者的创意得以充分展现。这些生成器能够精准捕捉大家的创意精髓&#xff0c;将其转化为细腻、独特的二次元画作。无论是角色设计、场景描绘…

SpringMVC(五)【拦截器】

前言 今天来把 SpringMVC 最后一部分学完&#xff0c;虽然课时很短&#xff0c;但是学起来还是很慢的&#xff0c;不过确收获很大。不得不感慨学大数据确实有必要把 SSM、SpringBoot 等各种 JavaEE 技术好好学一学&#xff0c;收获很大&#xff0c;尽管我们到现在 Java 代码写了…

Redis几种常见的应用方式

1.登录认证 redis最常见的应用就是&#xff0c;登录认证把。再首次登录返回给前端token&#xff0c;把用户名和登录状态缓存到redis一段时间&#xff0c;每次其他请求进来过滤器那这token解析出来的用户名或其他关键的key值&#xff0c;再redis里面查询缓存&#xff0c;有则直…

【算法】二分查找

快乐的流畅&#xff1a;个人主页 个人专栏&#xff1a;《算法神殿》《数据结构世界》《进击的C》 远方有一堆篝火&#xff0c;在为久候之人燃烧&#xff01; 文章目录 引言一、二分查找二、查找元素的第一个和最后一个位置三、x的平方根四、搜索插入位置五、山脉数组的峰顶索引…

【Leetcode每日一题】 分治 - 排序数组(难度⭐⭐)(60)

1. 题目解析 题目链接&#xff1a;912. 排序数组 这个问题的理解其实相当简单&#xff0c;只需看一下示例&#xff0c;基本就能明白其含义了。 2.算法原理 算法思路&#xff1a; 快速排序作为一种经典的排序算法&#xff0c;其核心思想在于通过“分而治之”的策略&#xff…

eCongnition 根据栅格类别图分类分割结果

目录 1、导入标签文件 2、根据栅格类别计算对象类别 3、导出栅格计算的类别 1、导入标签文件 导入栅格类别文件Label.tif 参考&#xff1a;eCongnition 对图像进行多尺度分割-CSDN博客 2、根据栅格类别计算对象类别 对类别栅格创建 mode[Maximum] 特征&#xff0c;该特征…

SQL Serve---嵌套查询

定义 嵌套查询&#xff1a;主要用于复杂的查询中。在SQL语言中&#xff0c;一个Select From Where语句称为一个查询块&#xff0c;将一个查询块嵌套在另一个查询的Where子句或Having短语中的查询称为嵌套查询。 子查询的类型 使用别名的子查询 使用IN和NOT IN的子查询 使用比较…

Java基础之循环控制语句、函数、数组

Java基础 1.循环控制语句: break、continue public static void main(String[] args) {/*TODO 循环控制语句 - break需要和 for 以及 while 循环搭配使用 当在循环中执行了 break语句 那么就可以退出当前循环*/int sum 0;for (int i 1; i < 1000; i) {sum i;if (i >…

基于SSM的列车订票管理系统(含源码+sql+视频导入教程+文档+PPT)

&#x1f449;文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1 、功能描述 基于SSM的列车订票管理系统3拥有两种角色&#xff1b;管理员、用户 管理员&#xff1a;用户管理、车票管理、购票指南管理、系统管理等 用户&#xff1a;发布帖子、登录注册、购票等 1.…

数据结构速成--串

由于是速成专题&#xff0c;因此内容不会十分全面&#xff0c;只会涵盖考试重点&#xff0c;各学校课程要求不同 &#xff0c;大家可以按照考纲复习&#xff0c;不全面的内容&#xff0c;可以看一下小编主页数据结构初阶的内容&#xff0c;找到对应专题详细学习一下。 目录 …

【Linux冯诺依曼体系结构】

目录 1.冯诺依曼体系结构原理 1.冯诺依曼体系结构 我们常见的计算机&#xff0c;如笔记本。我们不常见的计算机&#xff0c;如服务器&#xff0c;大部分都遵守冯诺依曼体系。 截至目前&#xff0c;我们所认识的计算机&#xff0c;都是有一个个的硬件组件组成 输入单元&#…

最新SpringBoot项目财务管理系统

采用技术 最新SpringBoot项目财务管理系统的设计与实现~ 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringBootMyBatis 工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 页面展示效果 系统登录页面 管理员功能 管理员功能页面 员工管理页面 部…

SpringBoot多数据源(五)

SpringBoot多数据源-集成多个mybatis框架 1.基本框架2.使用2.1项目结构2.2 依赖导入2.3 application.yml配置2.4 创建读与写的SqlSessionFactoryBean 3.总结 1.基本框架 通过启动多个SqlSessionFactoryBean&#xff0c;每个SqlSessionFactoryBean对应一个datasource和指定位置的…