工作目录和基本操作见博客《基于HTTP3的WebTransport实践》,在此仅展示服务器端和客户端代码。

服务器端


import { readFile } from "node:fs/promises";
import { createServer } from "node:https";
import {Server} from "socket.io";
import { Http3Server } from "@fails-components/webtransport";
import { randomBytes } from 'crypto';
import path from "path";
import fs from "fs";// const key = await readFile("./certs/localhost.key");
// const cert = await readFile("./certs/localhost.crt");
const key = await readFile("./certs/localhost+2-key.pem");
const cert = await readFile("./certs/localhost+2.pem");const httpsServer = createServer({key,cert
}, async (req, res) => {if (req.method === "GET" && req.url === "/") {const content = await readFile("./longvideo.html");res.writeHead(200, {"content-type": "text/html"});res.write(content);res.end();}else if (req.method === "GET" && req.url === "/longvideo.js") {const content = await readFile("./longvideo.js");res.writeHead(200, {"content-type": "application/javascript"});res.write(content);res.end();}else {res.writeHead(404).end();}
});const port = process.env.PORT || 3001;httpsServer.listen(port, () => {console.log(`server listening at https://localhost:${port}`);
});// const io = new Server(httpsServer);
const io = new Server(httpsServer, {transports: ["polling", "websocket", "webtransport"]
});io.on("connection", (socket) => {console.log(`connected with transport ${socket.conn.transport.name}`);socket.conn.on("upgrade", (transport) => {console.log(`transport upgraded to ${transport.name}`);});socket.on("disconnect", (reason) => {console.log(`disconnected due to ${reason}`);});
});const h3Server = new Http3Server({port,host: "0.0.0.0",secret: 'ChangeIt', //不换也行cert,privKey: key,
});h3Server.startServer();(async () => {const stream = await h3Server.sessionStream("/socket.io/");const sessionReader = stream.getReader();while (true) {const { done, value } = await sessionReader.read();if (done) {break;}io.engine.onWebTransportSession(value);}
})();

客户端

longvideo.html

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>WebTransport Client</title><style>#video-grid{display: grid;grid-template-columns: repeat(auto-fill,300px);grid-auto-rows: 300px;}video {width: 100%;height: 100%;object-fit: cover;}</style></head>
<body>
<p>Status: <span id="status">Disconnected</span></p>
<p>Transport: <span id="transport">N/A</span></p>
<script src="/socket.io/socket.io.js"></script>
<script>const $status = document.getElementById("status");const $transport = document.getElementById("transport");// const socket = io();const socket = io({rejectUnauthorized: false,transportOptions: {webtransport: {hostname: "127.0.0.1"}}});socket.on("connect", () => {console.log(`connected with transport ${socket.io.engine.transport.name}`);$status.innerText = "Connected";$transport.innerText = socket.io.engine.transport.name;socket.io.engine.on("upgrade", (transport) => {console.log(`transport upgraded to ${transport.name}`);$transport.innerText = transport.name;});});socket.on("connect_error", (err) => {console.log(`connect_error due to ${err.message}`);});socket.on("disconnect", (reason) => {console.log(`disconnect due to ${reason}`);$status.innerText = "Disconnected";$transport.innerText = "N/A";});
</script><div id="video-grid"></div>
<script src="longvideo.js"></script></body>
</html>

longvideo.js


const videoGrid = document.getElementById('video-grid')
if (!videoGrid) {console.error('Video grid element not found');
}
const myVideo = document.createElement('video')
myVideo.muted = truesocket.on('connect', () => {// socket.io.engine.on("upgrade", (transport) => {//     console.log(`transport upgraded to ${transport.name}`);// });navigator.mediaDevices.getUserMedia({ audio: true, video: true }).then((stream) => {addVideoStream(myVideo, stream)var madiaRecorder = new MediaRecorder(stream);var audioChunks = [];madiaRecorder.addEventListener("dataavailable", function (event) {audioChunks.push(event.data);});madiaRecorder.addEventListener("stop", function () {var audioBlob = new Blob(audioChunks);audioChunks = [];var fileReader = new FileReader();fileReader.readAsDataURL(audioBlob);fileReader.onloadend = function () {var base64String = fileReader.result;socket.emit("audioStream", base64String);};madiaRecorder.start();setTimeout(function () {madiaRecorder.stop();}, 500);});madiaRecorder.start();setTimeout(function () {madiaRecorder.stop();}, 500);}).catch((error) => {console.error('Error capturing audio.', error);});
});socket.on('audioStream', (audioData) => {var newData = audioData.split(";");newData[0] = "data:audio/ogg;";newData = newData[0] + newData[1];var audio = new Audio(newData);if (!audio || document.hidden) {return;}audio.play();
});function addVideoStream(video, stream) {video.srcObject = streamvideo.addEventListener('loadedmetadata', () => {video.play()})videoGrid.append(video)
}

效果

(我的小熊猫是不是很可爱!)
在这里插入图片描述
用Wireshark抓包,看到抓到的都是QUIC包。
在这里插入图片描述

参考代码:https://www.nxrte.com/jishu/yinshipin/41948.html
该博客提供了使用 Socket.io 和 NodeJS 实现实时音频聊天,但是基于websocket(TCP)的实现。本文将其改成基于WebTransport(QUIC)的,实现更安全更高效的数据传输。

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

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

相关文章

Git 怎么仓库迁移?error: remote origin already exists.怎么解决

1. 确认本地已经有完整的 旧 仓库你本地应该有旧的项目&#xff0c;并且能看到 .git 文件夹。如果没有&#xff0c;可以先从旧仓库克隆下来&#xff1a;git clone 旧仓库git地址 cd 旧代码目录比如&#xff1a;git clone https://gitee.com/star-information/esflow-pro-api.gi…

【语法】【C+V】本身常用图表类型用法快查【CSDN不支持,VSCODE可用】

文章目录Mermaid 简介Mermaid 由三部分组成Mermaid 的使用方法复杂图表结构的技巧饼图简介饼图语法饼图示例雷达图简介雷达图语法雷达图语法细节标题轴曲线选项雷达图示例时间线简介时间线语法时间线示例树形图简介树形图语法树形图示例数据包图简介数据包图语法1&#xff1a;数…

Anaconda下载安装教程(附Anaconda安装包)

Anaconda下载安装教程&#xff08;附Anaconda安装包&#xff09;获取&#xff1a;python开发工具包丨夸克网盘-资源免费下载 有位小粉丝刚刚入门python&#xff0c;问我怎么安装Anaconda&#xff0c;于是我帮他找了一个详细的Anaconda安装教程和安装包。 Anaconda其实是一个p…

那我现在有3个输入 9层神经元 每层神经元数为 3 9 3 5 6 2 3 9 8 请给出我所有的权重矩阵

问题1 那我现在有3个输入 9层神经元 每层神经元数为 3 9 3 5 6 2 3 9 8 请给出我所有的权重矩阵 明白&#xff0c;我们来系统地列出你的 9层全连接网络 的权重矩阵。1️⃣ 网络结构 输入层&#xff1a;3 个神经元隐藏层/输出层&#xff1a;9 层神经元数分别为 [3,9,3,5,6,2,3,9…

广东省省考备考(第八十七天8.26)——判断推理(听课后强化训练)

判断推理&#xff1a;定义判断 错题解析 第一步&#xff1a;找出定义关键词。 “农村中各项经济活动及由此产生的经济关系”、“同农业有直接或间接的关系”。 第二步&#xff1a;逐一分析选项。 A项&#xff1a;该项指出具体的夏粮产量和增量&#xff0c;其中生产粮食属于种植…

读取 STM32H5 Data Flash 触发 NMI 的问题解析 LAT1544

关键字&#xff1a;STM32H5, data flash&#xff0c; high-cycle data, NMI问题描述客户反馈&#xff0c;使用 STM32H563 的 data flash(high-cycle data flash)&#xff0c;在还没有写入任何数据之前去读取 data flash, 会触发 hardfault 异常。1. 问题分析我们尝试在 NUCLEO-…

学云计算还是网络,选哪个好?

云计算工程师和网络工程师&#xff0c;都是IT界香饽饽&#xff0c;但方向差很大&#xff01;选错路后悔3年&#xff01;今天极限二选一&#xff0c;帮你彻底搞懂工作职责 网络工程师&#xff1a;网络世界的交警工程师&#xff01;主要管物理网络和逻辑连接。负责设计、搭建、维…

Matlab使用——开发上位机APP,通过串口显示来自单片机的电压电流曲线,实现光伏I-V特性监测的设计

预览此处的测试数据的采集频率和曲线变化是通过更换电阻来测试的&#xff0c;所以电压电流曲线显示并不是很平滑&#xff0c;图中可以看到每一个采集点的数值。这个设计是福州大学第三十期SRTP的一个校级的项目&#xff0c;打算通过分布式的在线扫描电路低成本的单片机&#xf…

云原生 JVM 必杀技:3 招让容器性能飞跃 90%

最近佳作推荐&#xff1a; Java 大厂面试题 – JVM 与分布式系统的深度融合&#xff1a;实现技术突破&#xff08;34&#xff09;&#xff08;New&#xff09; Java 大厂面试题 – JVM 新特性深度解读&#xff1a;紧跟技术前沿&#xff08;33&#xff09;&#xff08;New&#…

你真的了解操作系统吗?

文章目录操作系统是什么&#xff1f;操作系统核心功能为什么需要操作系统&#xff08;目的&#xff09;&#xff1f;操作系统的下层是什么&#xff1f;上层又是什么&#xff1f;如何理解“管理”&#xff1f;——“先描述&#xff0c;再组织”操作系统是什么&#xff1f; 任何…

从0到1详解requests接口自动化测试

前言 接口测试是测试系统组件间接口的一种测试。接口测试主要用于检测外部系统与系统之间以及内部各个子系统之间的交互点。测试的重点是要检查数据的交换&#xff0c;传递和控制管理过程&#xff0c;以及系统间的相互逻辑依赖关系等。 1、理解什么是接口 接口一般来说有两种…

Linux系统操作编程——http

万维网www万维网是一个大规模的、联机式的信息储藏所 &#xff0c;实现从一个站点链接到另一个站点万维网服务器后台标记万维网数据方式&#xff1a;url&#xff1a;统一资源定位符万维网客户端与万维网服务器的通信方式&#xff1a;HTTP&#xff1a;超文本传输协议万维网客户端…

Langchian-chatchat私有化部署和踩坑问题以及解决方案[v0.3.1]

文章目录一 langchain-chatchat项目二 本地私有部署2.1 源码下载2.2 创建虚拟环境2.3 安装Poetry2.4 安装项目依赖2.5 初始化项目2.6 修改配置信息2.7 初始化知识库2.8 启动服务三 问题和解决方法3.1 poetry和packaging版本兼容性3.2 Langchain-chatchatPDF加载错误分析[win平台…

Day3--HOT100--42. 接雨水,3. 无重复字符的最长子串,438. 找到字符串中所有字母异位词

Day3–HOT100–42. 接雨水&#xff0c;3. 无重复字符的最长子串&#xff0c;438. 找到字符串中所有字母异位词 每日刷题系列。今天的题目是力扣HOT100题单。 双指针和滑动窗口题目。其中438题踩了坑&#xff0c;很值得看一下。 42. 接雨水 思路&#xff1a; 每个位置i&#x…

Kafka Broker 核心原理全解析:存储、高可用与数据同步

Kafka Broker 核心原理全解析&#xff1a;存储、高可用与数据同步 思维导图正文&#xff1a;Kafka Broker 核心原理深度剖析 Kafka 作为高性能的分布式消息队列&#xff0c;其 Broker 节点的设计是支撑高吞吐、高可用的核心。本文将从存储结构、消息清理、高可用选举、数据同步…

RTTR反射机制示例

1. Person类型头文件 #ifndef PERSON_H …

计数组合学7.21(有界部分大小的平面分拆)

7.21 有界部分大小的平面分拆 本节的主要目标是在 q1q 1q1 的情况下细化定理 7.20.1&#xff0c;通过限制平面分拆 π∈P(r,c)\pi \in P(r, c)π∈P(r,c) 的最大部分的大小。例如&#xff0c;考虑特殊情况 r1r 1r1&#xff0c;此时 π\piπ 只是一个不超过 ccc 个部分的普通分…

Product Hunt 每日热榜 | 2025-08-26

1. Trace 标语&#xff1a;人类与人工智能的工作流程自动化 &#x1f47e; 介绍&#xff1a;一个工作流程自动化平台&#xff0c;将任务分配给合适的处理者——无论是人类还是人工智能。通过连接像Slack、Jira和Notion这样的工具&#xff0c;Trace能够拆解现有工作流程&#…

llama.cpp reranking源码分析

大模型时代&#xff0c;reranker一直是提高RAG有效性的重要工具。相对于初筛阶段向量检索&#xff0c;精排阶段的reranker需要query和每个候选document做相关计算。初筛已经将候选documents限制在一个相对较小范围&#xff0c;但依然要进行大量的相关性计算。 llama.cpp是广泛…

ruoyi-vue(十二)——定时任务,缓存监控,服务监控以及系统接口

一 定时任务 1、 定时任务使用 1.1 概述 在实际项目开发中Web应用有一类不可缺少的&#xff0c;那就是定时任务。 定时任务的场景可以说非常广泛&#xff0c;比如某些视频网站&#xff0c;购买会员后&#xff0c;每天会给会员送成长值&#xff0c;每月会给会员送一些电影券&…