思茅网站建设,企石镇网站仿做,四川建设安全监督管理局网站,网站在线支付接口ESP32-Web-Server编程- WebSocket 编程
概述
在前述 ESP32-Web-Server 实战编程-通过网页控制设备的 GPIO 中#xff0c;我们创建了一个基于 HTTP 协议的 ESP32 Web 服务器#xff0c;每当浏览器向 Web 服务器发送请求#xff0c;我们将 HTML/CSS 文件提供给浏览器。 使用…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 classtopnavh1ESP32 WebSocket Server/h1
/div
div classcontentdiv classcardh2ONBOARD LED GPIO2/h2pbutton idbutton classbuttonToggle LED/button/pp classstateState: span idstate%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_reqhttpd_req_t *req 负责处理从所有 Web 客户端发送到服务器的 WebSocket 请求。
WebSocket 接收客户端的数据
函数 handle_ws_reqhttpd_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;}如果接收到的数据帧长度不为零则该函数将调用 calloc1 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_sendreq-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 服务器时可以同步更新同一个网页的内容 讨论
1HTTP 传输协议与 WebSocket 在使用场景上有哪些不同
HTTP在处理静态数据且不定期更新的应用程序中更可取。
WebSocket在处理实时数据的应用程序中更为可取。比如使用动态数据并期望持续和频繁更新的应用程序游戏应用程序、社交软件必须与多个用户建立联系这种类型的应用程序可以选择WebSocket来处理实时数据。
2WebSocket 在前端中的 onload() 事件怎么理解
即在网页加载时自动触发的函数这是浏览器默认的行为是一个标准。还有其他称为“onOpen()““onClose()”“onMessage() 等的函数它们处理WebSocket 上可能发生的不同事件。
总结
1本节主要是介绍在 ESP32 上实现 WebSocket 服务器。相比 HTTP 协议WebSocket 通信协议除了可以双向通信、并且向多个客户端同时发送通知信息外还可以提供持久连接并且由于没有为每个请求重新建立连接的开销因此延迟较低。
资源链接
1ESP32-Web-Server ESP-IDF系列博客介绍 2对应示例的 code 链接 点击直达代码仓库
3下一篇ESP32-Web-Server编程- 使用SSE 实时更新设备信息
(码字不易感谢点赞或收藏)