libwebsockets 简介

server/2024/9/24 11:28:08/

文章目录

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

1. 前言

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

websockets_____5">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;
}

websocket__311">3. websocket 协议

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


http://www.ppmy.cn/server/6998.html

相关文章

阿里云4核8G云服务器价格多少钱?700元1年

阿里云4核8G云服务器价格多少钱&#xff1f;700元1年。阿里云4核8G服务器租用优惠价格700元1年&#xff0c;配置为ECS通用算力型u1实例&#xff08;ecs.u1-c1m2.xlarge&#xff09;4核8G配置、1M到3M带宽可选、ESSD Entry系统盘20G到40G可选&#xff0c;CPU采用Intel(R) Xeon(R…

基于Springboot的社区疫情返乡管控系统(有报告)。Javaee项目,springboot项目。

演示视频&#xff1a; 基于Springboot的社区疫情返乡管控系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系…

基于unity+c#的随机点名系统(简单UI界面+列表+数组)

目录 一、功能界面显示 二、UI 1、视频的使用 &#xff08;1&#xff09;渲染纹理 &#xff08;2&#xff09; 视频铺全屏 &#xff08;3&#xff09;视频的调用 2、 下拉文本框的使用&#xff08;旧版&#xff09; 3、输入文本框的使用&#xff08;旧版&#xff09; …

java-Spring-入门学习-第二天(单例模式和多例模式)

目录 Bean作用域 单例模式(默认可以不写) Spring下的 AutoWired 依赖注入 JaveEE下的 Resource 依赖注入 多例模式 Bean作用域 ​在Spring框架中&#xff0c;Bean是按照作用域来创建的&#xff0c;常见的作用域有两种&#xff1a;Singleton 和 Prototype。Singleton (单例…

OpenAI、蚂蚁集团、谷歌、科大讯飞等联合编制大模型安全国际标准,已正式发布

4月15日-19日&#xff0c;第27届联合国科技大会在瑞士日内瓦召开。16日&#xff0c;在以“塑造AI的未来”为主题的AI边会上&#xff0c;世界数字技术院&#xff08;WDTA&#xff09;发布了一系列突破性成果&#xff0c;包括《生成式人工智能应用安全测试标准》和《大语言模型安…

4月21敲一篇猜数字游戏,封装函数,void,无限循环,快去体验体验

今天敲一篇猜数字游戏 目录 今天敲一篇猜数字游戏 1.打开先学goto语句&#xff1a; 2.开干&#xff1a; 首次我们学习随机数&#xff1a; 讲解一下&#xff1a; 改用srand; 加入时间变量&#xff1a; 获取时间&#xff1a;哈​编辑 3.我本来想已近够完美了&#xff0…

Spring MVC 中的适配器模式

文章目录 Spring MVC 中的适配器模式为什么不直接调用&#xff1f;解决方案一&#xff1a;统一 Controller解决方案二&#xff1a;使用适配器模式DispatcherServlet 对 Adpater 的使用 Spring MVC 中的适配器模式 为什么不直接调用&#xff1f; DispatcherServlet 为什么不直…

appium控制手机一直从下往上滑动

用于使用Appium和Selenium WebDriver在Android设备上滚动设置应用程序的界面。具体来说&#xff0c;它通过WebDriverWait和expected_conditions等待元素出现&#xff0c;然后使用ActionChains移动到该元素并执行滚动动作。在setUp中&#xff0c;它初始化了Appium的WebDriver和c…