ESP32-Web-Server编程- WebSocket 编程

ESP32-Web-Server编程- WebSocket 编程

概述

在前述 ESP32-Web-Server 实战编程-通过网页控制设备的 GPIO 中,我们创建了一个基于 HTTP 协议的 ESP32 Web 服务器,每当浏览器向 Web 服务器发送请求,我们将 HTML/CSS 文件提供给浏览器。

在这里插入图片描述

使用 HTTP 服务器库的缺点是,**如果多个客户端连接到 Web 服务器,当 A 浏览器改变了网页的内容(比如点餐系统),它不会自动更新网页上的内容到所有客户端B、C。我们可以通过使用 WebSocket 通信协议来解决此问题。**例如,如果多个客户端连接到 Web 服务器,并且任何一个客户端更改了设备的 GPIO 引脚的状态,则它将自动向所有连接的客户端通知该更改状态。

在这里插入图片描述
相比 HTTP 协议,WebSocket 通信协议除了可以双向通信、并且向多个客户端同时发送通知信息外,还可以提供持久连接,并且由于没有为每个请求重新建立连接的开销,因此延迟较低。

在这里插入图片描述

需求及功能解析

本节演示如何在 ESP32 上实现一个WebSocket 服务器。示例仍旧以在网页控制一个 GPIO 为例子。

与前述不同的是,通过 WebSocket 服务器,当多个浏览器在访问该服务器时,不同的浏览器之间可以及时接收到网页更新的信息。

示例解析

前端代码

示例中的 ESP32 WebSocket 服务器前端代码在 data\index.html 文件中,其主要提供两个功能:

  • 显示 LED(对应设备的一个 GPIO)的状态,并为网页提供一个按钮用于切换 LED 的状态。

    <div class="topnav"><h1>ESP32 WebSocket Server</h1>
    </div>
    <div class="content"><div class="card"><h2>ONBOARD LED GPIO2</h2><p><button id="button" class="button">Toggle LED</button></p><p class="state">State: <span id="state">%s</span></p></div>
    </div>
    </div>
    
  • script中,每当 LED 的状态发生更新时,将 LED 状态作为 WebSocket 消息发送到所有连接的客户端。

    // 当网页加载时将自动调用该函数
    function onLoad(event) {initWebSocket();initButton();
    }
    // 此函数负责初始化页面上的按钮元素并向其附加事件侦听器。单击该按钮时,它会通过 WebSocket 协议向 ESP32 发送消息,以切换 LED 的状态。
    function initButton() {document.getElementById('button').addEventListener('click', toggle);
    }
    
  • 在该 JavaScript 代码中,其定义了一个变量“gateway”,即 WebSocket 的端点,其相当于 HTTP 中的 URL。

  • 点击“切换 LED”按钮后,会通过 WebSocket 向 ESP32 发送消息,切换 LED 的状态,并更新页面上的状态文本以反映 LED 的当前状态。

  • 该代码中还利用了 console.log() 函数,该函数会将消息输出到浏览器的开发人员控制台。这对于调试和理解代码流非常有用。

后端代码

通过 HTTP 建立 wrbsocket 握手

WebSocket 服务器通过 HTTP 协议握手,然后开始使用 WebSocket 通信协议进行数据通信。因此,我们需要设置一个HTTP GET请求处理程序来完成最初始的建立握手的环节。
get_req_handler()函数用于响应该握手阶段的 HTTP 请求。

esp_err_t get_req_handler(httpd_req_t *req)
{int response;if(led_state){sprintf(response_data, index_html, "ON");}else{sprintf(response_data, index_html, "OFF");}response = httpd_resp_send(req, response_data, HTTPD_RESP_USE_STRLEN);return response;
}

handle_ws_req(httpd_req_t *req) 负责处理从所有 Web 客户端发送到服务器的 WebSocket 请求。

WebSocket 接收客户端的数据

函数 handle_ws_req(httpd_req_t *req) 负责处理从所有 Web 客户端发送到服务器的 WebSocket 请求(包括打开、关闭和处理通过 websocket 连接发送的数据)。

static esp_err_t handle_ws_req(httpd_req_t *req)

该函数首先检查请求的方法是否是 HTTP 协议的 HTTP_GET,如果是,它将打印一条消息,指示 WebSocket 握手阶段(因为 WebSocket Web 服务器通过 HTTP 握手开始初始通信,然后遵循 WebSocket 通信协议)已完成,WebSocket 连接已打开,函数返回。

if (req->method == HTTP_GET)
{ESP_LOGI(TAG, "Handshake done, the new connection was opened");return ESP_OK;
}

如果请求的方法不是HTTP_GET,则在该示例中表示客户端请求正在发送 WebSocket 数据帧。将调用函数 httpd_ws_recv_frame() 来接收 WebSocket 数据帧并将其存储在 ws_pkt 变量中。

esp_err_t ret = httpd_ws_recv_frame(req, &ws_pkt, 0);if (ret != ESP_OK){ESP_LOGE(TAG, "httpd_ws_recv_frame failed to get frame len with %d", ret);return ret;}

如果接收到的数据帧长度不为零,则该函数将调用 calloc(1, ws_pkt.len + 1) 为 buf 变量分配内存,该变量将用于存储数据帧的有效负载。然后,它再次调用 httpd_ws_recv_frame() 来检索数据框的有效负载并将其存储在 buf 变量中。该函数记录收到的消息和帧的长度。

 if (ws_pkt.len){buf = calloc(1, ws_pkt.len + 1);if (buf == NULL){ESP_LOGE(TAG, "Failed to calloc memory for buf");return ESP_ERR_NO_MEM;}ws_pkt.payload = buf;ret = httpd_ws_recv_frame(req, &ws_pkt, ws_pkt.len);if (ret != ESP_OK){ESP_LOGE(TAG, "httpd_ws_recv_frame failed with %d", ret);free(buf);return ret;}ESP_LOGI(TAG, "Got packet with message: %s", ws_pkt.payload);}ESP_LOGI(TAG, "frame len is %d", ws_pkt.len);

最后,该函数检查收到的消息是否为“切换”,如果是,则调用 trigger_async_send(req->handle, req) 函数来通知连接的其他客户端。

if (ws_pkt.type == HTTPD_WS_TYPE_TEXT &&strcmp((char *)ws_pkt.payload, "toggle") == 0){free(buf);return trigger_async_send(req->handle, req);}return ESP_OK

总之,此函数通过处理 WebSocket 数据帧、接收请求中发送的消息以及通过向所有连接的客户端发送异步消息来处理消息来处理 WebSocket 请求。

WebSocket 发送方函数

以下两个函数响应 WebSocket 并将帧发送到所有连接的客户端:

static void ws_async_send(void *arg)
static esp_err_t trigger_async_send(httpd_handle_t handle, httpd_req_t *req)

trigger_async_send()负责调用 ws_async_send(),通过使用 httpd_queue_work()**对ws_async_send()进行排队并传递服务器句柄:

static esp_err_t trigger_async_send(httpd_handle_t handle, httpd_req_t *req)
{struct async_resp_arg *resp_arg = malloc(sizeof(struct async_resp_arg));resp_arg->hd = req->handle;resp_arg->fd = httpd_req_to_sockfd(req);return httpd_queue_work(handle, ws_async_send, resp_arg);
}

ws_async_send() 执行以下功能:

  • 切换 led_state 变量的状态,该变量跟踪 LED 的状态。
  • 根据led_state更新指示灯的状态。
  • 使用当前 led_state 格式化要发送的字符串,以生成 Web 套接字数据包的有效负载。
  • 使用 httpd_ws_send_frame_async 函数将数据包发送到所有连接的客户端。
  • 最后,释放为 resp_arg 分配的内存。

示例效果

通过 WebSocket 实现多个浏览器客户端连接到 Web 服务器时可以同步更新同一个网页的内容:

在这里插入图片描述

讨论

1)HTTP 传输协议与 WebSocket 在使用场景上有哪些不同?

HTTP在处理静态数据且不定期更新的应用程序中更可取。

WebSocket在处理实时数据的应用程序中更为可取。比如使用动态数据并期望持续和频繁更新的应用程序,游戏应用程序社交软件必须与多个用户建立联系,这种类型的应用程序可以选择WebSocket来处理实时数据。

2)WebSocket 在前端中的 onload() 事件怎么理解?

即在网页加载时,自动触发的函数,这是浏览器默认的行为,是一个标准。还有其他称为“onOpen()“,“onClose()”,“onMessage()" 等的函数,它们处理WebSocket 上可能发生的不同事件。

总结

1)本节主要是介绍在 ESP32 上实现 WebSocket 服务器。相比 HTTP 协议,WebSocket 通信协议除了可以双向通信、并且向多个客户端同时发送通知信息外,还可以提供持久连接,并且由于没有为每个请求重新建立连接的开销,因此延迟较低。

资源链接

1)ESP32-Web-Server ESP-IDF系列博客介绍
2)对应示例的 code 链接 (点击直达代码仓库)

3)下一篇:ESP32-Web-Server编程- 使用SSE 实时更新设备信息

(码字不易感谢点赞或收藏)

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

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

相关文章

智能手表上的音频(四):语音通话

上篇讲了智能手表上音频文件播放。本篇开始讲语音通话。同音频播放一样有两种case&#xff1a;内置codec和BT。先看这两种case下audio data path&#xff0c;分别如下图&#xff1a; 内置codec下的语音通话audio data path 蓝牙下的语音通话audio data path 从上面两张图可以看…

享元模式 (Flyweight Pattern)

定义&#xff1a; 享元模式&#xff08;Flyweight Pattern&#xff09;是一种结构型设计模式&#xff0c;用于优化性能和内存使用。它通过共享尽可能多的相似对象来减少内存占用&#xff0c;特别是在有大量对象时。这种模式通常用于减少应用程序中对象的数量&#xff0c;并在多…

Redis 实战缓存

本篇概要&#xff1a; 1. 设置、查询、获取过期时间&#xff1b;2. 缓存穿透&#xff1a;设置空键&#xff1b;3. 封杀单ip&#xff1b;4. 封杀ip段&#xff1b;5. 缓存预热&#xff1b;6. 使用 hash 数据类型保存新闻的缓存&#xff0c;增加点击量&#xff1b;7. Sorted set&a…

纯js实现录屏并保存视频到本地的尝试

前言&#xff1a;先了解下&#xff1a;navigator.mediaDevices&#xff0c;mediaDevices 是 Navigator 只读属性&#xff0c;返回一个 MediaDevices 对象&#xff0c;该对象可提供对相机和麦克风等媒体输入设备的连接访问&#xff0c;也包括屏幕共享。 const media navigator…

【刷题】DFS

DFS 递归&#xff1a; 1.判断是否失败终止 2.判断是否成功终止&#xff0c;如果成功的&#xff0c;记录一个成果 3.遍历各种选择&#xff0c;在这部分可以进行剪枝 4.在每种情况下进行DFS&#xff0c;并进行回退。 199. 二叉树的右视图 给定一个二叉树的 根节点 root&#x…

深度学习之十二(图像翻译AI算法--UNIT(Unified Neural Translation))

概念 UNIT(Unified Neural Translation)是一种用于图像翻译的 AI 模型。它是一种基于生成对抗网络(GAN)的框架,用于将图像从一个域转换到另一个域。在图像翻译中,这意味着将一个风格或内容的图像转换为另一个风格或内容的图像,而不改变图像的内容或语义。 UNIT 的核心…

IDEA2022 Git 回滚及回滚内容恢复

IDEA2022 Git 回滚 ①选择要回滚的地方&#xff0c;右键选择 ②选择要回滚的模式 ③开始回滚 ④soft模式回滚的内容会保留在暂存区 ⑤输入git push -f &#xff0c;然后重新提交&#xff0c;即可同步远程 注意观察IDEA右下角分支的标记&#xff0c;蓝色代表远程内容未同步到本…

数据结构 / day06 作业

1.下面的代码打印在屏幕上的值是多少? /下面的代码打印在屏幕上的值是多少?#include "stdio.h"int compute_data(int arr[], unsigned int len) {long long int result 0;if(result len)return arr[0];resultcompute_data(arr,--len);printf("len%d, res…

elementui中table进行表单验证

<el-form :model"ruleForm" ref"ruleForm" class"demo-ruleForm"><el-table :data"ruleForm.tableDataShou" border style"width: 100%;"><el-table-column type"index" label"序号" wi…

Android12源码分析

Android 12的源码结构与之前的版本类似&#xff0c;但也有一些新的变化和特性。以下是对Android 12源码结构的简要解析&#xff1a; 1. 系统源代码&#xff1a;这部分包含了整个Android操作系统的核心代码&#xff0c;包括Linux内核、系统库、运行时环境&#xff08;ART&#…

Flink源码解析零之重要名词的理解

名词解释 1)StreamGraph 根据用户通过 Stream API 编写的代码生成的最初的图。 (1)StreamNode 用来代表 operator 的类,并具有所有相关的属性,如并发度、入边和出边等。 (2)StreamEdge 表示连接两个StreamNode的边。 2)JobGraph StreamGraph经过优化后生成了 J…

【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能

文章目录 ⭐⭐⭐Spring核心源码分析自定义Spring框架⭐⭐⭐一、Spring使用回顾二、Spring核心功能结构1、Spring核心功能2、bean概述 三、Spring IOC相关接口分析1、BeanFactory解析2、BeanDefinition解析3、BeanDefinitionReader解析4、BeanDefinitionRegistry解析5、创建容器…

伪集群配置

编辑core-site 配置core-site 配置hdfs-site 将以下的文件配置进去 启动一下hadoop产生tmp文件 产生这个叫namenode的文件并格式化 回到~目录 再配置以下信息 配置以下信息 重启文件 再重新格式化配置namenode 再启动一下&#xff0c;然后jps看看&#xff0c;出现这样就…

java后端自学总结

自学总结 MessageSource国际化接口总结 MessageSource国际化接口 今天第一次使用MessageSource接口,比较意外遇到了一些坑 messageSource是spring中的转换消息接口&#xff0c;提供了国际化信息的能力。MessageSource用于解析 消息&#xff0c;并支持消息的参数化和国际化。 S…

运维知识点-openResty

openResty 企业级实战——畅购商城SpringCloud-网站首页高可用解决方案-openRestynginxlua——实现广告缓存测试企业级实战——畅购商城SpringCloud-网站首页高可用解决方案-openRestynginxlua——OpenResty 企业级实战——畅购商城SpringCloud-网站首页高可用解决方案-openRes…

机器学习中参数优化或交叉验证评估指标含义

在Scikit-Learn中&#xff0c;cross_val_score函数支持多种不同的评分标准&#xff08;scoring参数&#xff09;。以下是一些常见的评分标准及其应用场景&#xff1a; 参考链接&#xff1a; https://blog.csdn.net/worther/article/details/126909270 https://zhuanlan.zhihu.c…

优化器原理——权重衰减(weight_decay)

优化器原理——权重衰减&#xff08;weight_decay&#xff09; weight_decay的作用 原理解析 实验观察 在深度学习中&#xff0c;优化器的 weight_decay 参数扮演着至关重要的角色。它主要用于实现正则化&#xff0c;以防止模型过拟合。过拟合是指模型在训练数据上表现优异&…

【科技素养】蓝桥杯STEMA 科技素养组模拟练习试卷12

单选题 1、电子邮件地址中一定会出现的字符&#xff08;&#xff09; A、- B、 C、&#xff01; D、# 答案&#xff1a;B 2、以下常见的电脑硬件中&#xff0c;&#xff08;&#xff09;不是一种输入设备 A、鼠标 B、键盘 C、触摸板 D、显示器 答案&#xff1a;D 3、…

python | 简易版: pdf 转换为 word 方法

一、 前言 本文利用python将pdf转换为word方法&#xff0c;需要用到两个第三方模块&#xff0c;分别是&#xff1a; &#xff08;1&#xff09;pdfplumber&#xff0c;用来解析pdf文档&#xff0c;包括pdf的基本信息&#xff08;作者、创建时间、修改时间…&#xff09;及表格…

从意义中恢复,而不是从数据包中恢复

从书报&#xff0c;录放机&#xff0c;电视机到智能手机&#xff0c;vr 眼镜&#xff0c;所有学习的&#xff0c;娱乐的工具或玩具&#xff0c;几乎都以光声诉诸视听&#xff0c;一块屏幕和一个喇叭。 视觉和听觉对任何动物都是收发信息的核心&#xff0c;诉诸视觉和听觉的光和…