webrtc的RTCPeerConnection使用

背景:

平时我们很少会需要使用到点对点单独的通讯,即p2p,一般都是点对服务端通讯,但p2p也有自己的好处,即通讯不经过服务端,从服务端角度这个省了带宽和压力,从客户端角度,通讯是安全,且快速的,当然有些情况下可能速度并不一定快。那么如何实现p2p呢?

解决办法:

webrtc的RTCPeerConnection就实现了p2p的功能,使用RTCPeerConnection需要理解一些概念,什么是信令,信令交换的过程,信令服务器。

信令

2个设备需要通讯,就需要知道对方的在互联网上的公开地址,一般情况下2个设备都是不会直接拥有一个公网的ip地址,所以他们之间的通讯,就需要如何在公网找到自己的方式,路由信息告诉对方,通常这个信息都是临时的,并非永久,当对方获取到这个信息后,就可以通过网络找到彼此的实际路由路径,从而进行通讯,这个信息就是信令(位置信息)。

信令的交换过程:

假设2个设备,p1要和p2进行通讯

1.p1发起邀请阶段

const offer = await p1.createOffer();//创建邀请信令
await p1.setLocalDescription(offer);//设置为本地信令send(JSON.stringify(offer));//把邀请信令发送给对方,至于怎么发送,一般是需要一个第3方的信令服务器来转发这个信息

2.p2收到邀请阶段

 当收到p1发起的有邀请信令offer后

await p2.setRemoteDescription(new RTCSessionDescription(JSON.parse(offer)));//设置为远端的信令const answer = await p2.createAnswer();//创新一个应答信令,告诉p1我的位置
await pc.setLocalDescription(answer);//设置我的位置send(JSON.stringify(answer ));将位置信息发送给p1

3.p1收到应答信息阶段

await p2.setRemoteDescription(new RTCSessionDescription(JSON.parse(answer )));//设置为远端的信令
4.处理onicecandidate事件,确认要不要通讯
 await p2.addIceCandidate(new RTCIceCandidate(JSON.parse(candidate)));

完成上述几个阶段,正常来说就能开始通讯了

数据通讯DataChannel的使用

发送端

// 创建PeerConnection对象
const pc = new RTCPeerConnection();// 创建DataChannel
const dataChannel = pc.createDataChannel('myDataChannel');// 监听DataChannel的open事件
dataChannel.onopen = () => {console.log('DataChannel已打开');
};// 监听DataChannel的error事件
dataChannel.onerror = (error) => {console.error('DataChannel错误:', error);
};// 监听DataChannel的close事件
dataChannel.onclose = () => {console.log('DataChannel已关闭');
};// 发送文本消息
function sendMessage(message) {dataChannel.send(message);
}// 发起PeerConnection连接
// ...// 在某个事件触发时调用sendMessage()发送消息
// sendMessage('Hello, world!');

接收端:

// 创建PeerConnection对象
const pc = new RTCPeerConnection();// 监听DataChannel的open事件
pc.ondatachannel = (event) => {const dataChannel = event.channel;// 监听DataChannel的message事件dataChannel.onmessage = (event) => {const message = event.data;console.log('接收到消息:', message);};// 监听DataChannel的error事件dataChannel.onerror = (error) => {console.error('DataChannel错误:', error);};// 监听DataChannel的close事件dataChannel.onclose = () => {console.log('DataChannel已关闭');};
};

datachannel的用法发送端和接收端用法是一样的,只是接收端,需要通过 onicecandidate的事件才能获取到。

单页面完整demo

<!DOCTYPE html>
<html>
<head><title>WebRTC Demo</title>
</head>
<body><h1>WebRTC Demo</h1><button onclick="start()">Start</button><button onclick="call()">Call</button><button onclick="hangup()">Hang Up</button><button onclick="sendMessage()">send</button><br><br><textarea id="localDesc"></textarea><br><br><textarea id="remoteDesc"></textarea><br><br><textarea id="message"></textarea><br><br><textarea id="received"></textarea><script>let localConnection, remoteConnection,dataChannel,receiveChannel;function start() {localConnection = new RTCPeerConnection();remoteConnection = new RTCPeerConnection();localConnection.onicecandidate = e => {if (e.candidate) {console.log("localConnection.onicecandidate")remoteConnection.addIceCandidate(e.candidate);}};remoteConnection.onicecandidate = e => {if (e.candidate) {console.log("remoteConnection.onicecandidate")localConnection.addIceCandidate(e.candidate);}};localConnection.oniceconnectionstatechange = e => {console.log('Local ICE connection state change:', localConnection.iceConnectionState);};remoteConnection.oniceconnectionstatechange = e => {console.log('Remote ICE connection state change:', remoteConnection.iceConnectionState);};remoteConnection.ondatachannel = e => {console.log("ondatachannel",e)receiveChannel = e.channel;receiveChannel.onmessage = e => {console.log("onmessage",e.data)document.getElementById('received').value += e.data + '\n';};};dataChannel = localConnection.createDataChannel('dataChannel');dataChannel.onopen = e => {console.log("onopen")console.log('Data channel opened');};dataChannel.onclose = e => {console.log("onclose")console.log('Data channel closed');};dataChannel.onmessage = event => {console.log("onmessage",event.data)};
}async function call() {console.log("createOffer")const offer = await localConnection.createOffer();await localConnection.setLocalDescription(offer);await remoteConnection.setRemoteDescription(offer);console.log("createAnswer")const answer = await remoteConnection.createAnswer();await remoteConnection.setLocalDescription(answer);await localConnection.setRemoteDescription(answer);document.getElementById('localDesc').value = localConnection.localDescription.sdp;document.getElementById('remoteDesc').value = remoteConnection.localDescription.sdp;}async function hangup() {await localConnection.close();await remoteConnection.close();localConnection = null;remoteConnection = null;}function sendMessage() {const message = document.getElementById('message').value;//const dataChannel = localConnection.createDataChannel('dataChannel');dataChannel.send(message);console.log("send",message)}</script>
</body>
</html>

不同页面demo,信令交换过程手动操作

<!DOCTYPE html>
<html>
<head><title>WebRTC 文本消息发送</title>
</head>
<body><textarea id="xx"></textarea><textarea id="xx2"></textarea><textarea id="xx3"></textarea><div><label for="message">消息:</label><input type="text" id="message" /><button onclick="sendMessage()">发送</button><button onclick="sendOffer()">邀请</button><button onclick="handleAnswer()">接收远程信令</button><button onclick="handleCandidate()">接收candidate</button><br><button onclick="handleOffer()">接收邀请</button></div><div id="chat"></div><script>let pc;let dataChannel;// 创建本地PeerConnection对象function createPeerConnection() {pc = new RTCPeerConnection();// 创建数据通道dataChannel = pc.createDataChannel('chat');// 监听收到消息事件dataChannel.onmessage = event => {console.log(event.data)const message = event.data;displayMessage(message);};// 监听连接状态变化事件dataChannel.onopen = () => {displayMessage('连接已建立');};dataChannel.onclose = () => {displayMessage('连接已关闭');};pc.ondatachannel = (e)=>{console.log("ondatachannel",e)};// 监听ICE候选事件pc.onicecandidate = e => {if (e.candidate) {document.getElementById("xx3").value = JSON.stringify(e.candidate);}};pc.oniceconnectionstatechange = e => {console.log('Local ICE connection state change:', pc.iceConnectionState);};}createPeerConnection()// 处理信令function handleSignal(signal) {switch (signal.type) {case 'offer':handleOffer(signal.offer);break;case 'answer':handleAnswer(signal.answer);break;case 'candidate':handleCandidate(signal.candidate);break;}}async function sendOffer(){let desc = await pc.createOffer()pc.setLocalDescription(desc); document.getElementById("xx").value = JSON.stringify(desc)console.log(desc)}// 处理Offer信令async function handleOffer() {await pc.setRemoteDescription(new RTCSessionDescription(JSON.parse(document.getElementById("xx2").value)));let answer = await pc.createAnswer()await pc.setLocalDescription(answer);document.getElementById("xx").value = JSON.stringify(answer)}// 处理Answer信令async function handleAnswer() {// 设置远端描述let answer = new RTCSessionDescription(JSON.parse(document.getElementById("xx2").value))await pc.setRemoteDescription(answer);}// 处理ICE候选信令async function handleCandidate() {try {await pc.addIceCandidate(new RTCIceCandidate(JSON.parse(document.getElementById("xx2").value)));} catch (error) {console.error('添加ICE候选失败:', error);}}// 发送消息function sendMessage() {const messageInput = document.getElementById('message');const message = messageInput.value;dataChannel.send(message);displayMessage('我:' + message);messageInput.value = '';}// 显示消息function displayMessage(message) {const chatDiv = document.getElementById('chat');const messageP = document.createElement('p');messageP.textContent = message;chatDiv.appendChild(messageP);}</script>
</body>
</html>

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

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

相关文章

Javaweb之前端工程化的详细解析

3 前端工程化 3.1 前端工程化介绍 我们目前的前端开发中&#xff0c;当我们需要使用一些资源时&#xff0c;例如&#xff1a;vue.js&#xff0c;和axios.js文件&#xff0c;都是直接再工程中导入的&#xff0c;如下图所示&#xff1a; 但是上述开发模式存在如下问题&#xff…

git的使用:本地git下载、sshkey的添加、github仓库创建及文件上传

一、github创建账号 即github注册账号&#xff0c;登录github官网&#xff0c;根据提示注册即可 github官网 二、git客户端下载安装 已有很多git下载安装的博文了&#xff0c;在此就不赘述 三、sshkey的生成与添加 1、sshkey的生成以及查看 // sshkey的生成命令&#xff…

OSS+CDN的资费和安全

文章目录 花费OSSCDNOSS CDN 安全OSS防盗链跨域设置CORS数据加密 CDN防盗链URL鉴权Cookie鉴权远程鉴权IP黑白名单UA黑白名单 回源服务自定义私有参数IP黑白名单数据加密 花费 OSS 存储费用 &#xff1a;0.12元/GB/月下行流量费用 &#xff1a;0.5元/GB请求费用 &#xff1a;…

java全局异常处理(springboot)

介绍&#xff1a; 在日常项目开发中&#xff0c;异常是常见的&#xff0c;但是如何更高效的处理好异常信息&#xff0c;让我们能快速定位到BUG&#xff0c;是很重要的&#xff0c;不仅能够提高我们的开发效率&#xff0c;还能让你代码看上去更舒服&#xff0c;SpringBoot的项目…

C语言你爱我么?(ZZULIOJ 1205:你爱我么?)

题目描述 LCY买个n束花准备送给她暗恋的女生&#xff0c;但是他不知道这个女生是否喜欢他。这时候一个算命先生告诉他让他查花瓣数&#xff0c;第一个花瓣表示"爱"&#xff0c;第二个花瓣表示"不爱"&#xff0c;第三个花瓣表示"爱"..... 为了使最…

某60区块链安全之未初始化的存储指针实战二学习记录

系列文章目录 文章目录 系列文章目录未初始化的存储指针实战二实验目的实验环境实验工具实验原理实验内容实验过程EXP利用 未初始化的存储指针实战二 实验目的 学会使用python3的web3模块 学会分析以太坊智能合约未初始化的存储指针漏洞 找到合约漏洞进行分析并形成利用 实验…

机器学习之自监督学习(四)MoCo系列翻译与总结(二)

MoCo中相关工作的对比分析 去噪自动编码器&#xff08;Denoising Autoencoder&#xff09;是一种用于学习数据表示的神经网络模型。它的主要目标是通过去除输入数据中的噪声&#xff0c;学习到输入数据的有用表示&#xff0c;从而提高模型对干净数据的鲁棒性。下面是对去噪自动…

Flink 常用物理分区算子(Physical Partitioning)

Flink 物理分区算子(Physical Partitioning) 在Flink中&#xff0c;常见的物理分区策略有&#xff1a;随机分配(Random)、轮询分配(Round-Robin)、重缩放(Rescale)和广播(Broadcast)。 接下来&#xff0c;我们通过源码和Demo分别了解每种物理分区算子的作用和区别。 (1) 随机…

win10安装pytorch(py39)

cuda≤11.6&#xff0c;观察控制面板 观察torch对应cuda版本 https://download.pytorch.org/whl/torch/ 安装cuda11.6.0 CUDA Toolkit Archive | NVIDIA Developer cmd输入nvcc -V 编辑国内镜像源 .condarc anaconda prompt输入 查看环境 conda env list 安装py3.9…

uniapp视频倍速播放插件,uniapp视频试看插件——sunny-video使用文档

sunny-video视频倍速播放器 组件名&#xff1a;sunny-video 效果图 img1img2img3img4 平台差异说明 目前已应用到APP&#xff08;安卓、iOS&#xff09;、微信&#xff08;小程序、H5&#xff09;其它平台未测试 安装方式 本组件符合easycom规范&#xff0c;HBuilderX 2.5…

emoji

图标的网址&#xff1a; webfx emojipedia 1.可以直接复制粘贴 2.按照其格式文本表示&#xff08;Shortcodes&#xff09; &#x1f680; &#x1f604; &#x1f92b; ✍️ &#x1f480; 还有关于通过链接引用shield.io中的图标&#xff0c;没有深究&#xff0c;不…

第六十三周周报

学习目标&#xff1a; 项目 实验和论文 学习时间&#xff1a; 2023.11.18-2023.11.24 学习产出&#xff1a; 论文 对论文进行了润色和修改 实验 1、上周DiffusionRelative的结果无法再次复现&#xff0c;新跑的FID与以前实验跑的结果相差不大&#xff0c;上周的结果应…

点大商城V2.5.3分包小程序端+小程序上传提示限制分包制作教程

这几天很多播播资源会员反馈点大商城V2.5.3小程序端上传时提示大小超限&#xff0c;官方默认单个包都不能超过2M&#xff0c;总分包不能超20M。如下图提示超了93KB&#xff0c;如果出现超的不多情况下可采用手动删除一些images目录下不使用的图片&#xff0c;只要删除超过100KB…

鸿蒙4.0开发笔记之DevEco Studio如何使用低代码开发模板进行开发的详细流程(六)

鸿蒙低代码开发 一、什么是低代码二、如何进行鸿蒙低代码开发1、 创建低代码开发工程&#xff08;方式壹&#xff09;2、已有工程则创建Visual文件&#xff08;方拾贰&#xff09; 三、低代码开发界面介绍四、低代码实现页面跳转五、低代码开发建议 一、什么是低代码 所谓低代码…

Qt+xml解析

文章目录 一、xml文件介绍1.1 XML 文件结构和基本概念1.2 XML 文件示例二、Qt读取xml文件2.1 Qt读取xml 步骤2.2 基本操作和函数 QXmlStreamReader2.3 错误处理errorString和hasError2.4 Qt读取xml实例三、实际项目一、xml文件介绍 1.1 XML 文件结构和基本概念 XML(可扩展标…

三、ts高级笔记,

文章目录 18、d.ts声明文件19、Mixin混入20、Decorator装饰器的使用21、-高级proxy拦截_Reflect元储存22、-高级写法Partial-Pick23、Readonly只读_Record套对象24、高阶写法Infer占位符25、Inter实现提取类型和倒叙递归26、object、Object、{}的区别27、localStorage封装28、协…

基于 STM32F7 和神经网络的实时人脸特征提取与匹配算法实现

本文讨论了如何使用 STM32F7 和神经网络模型来实现实时人脸特征提取与匹配算法。首先介绍了 STM32F7 的硬件和软件特点&#xff0c;然后讨论了人脸特征提取和匹配算法的基本原理。接下来&#xff0c;我们将重点讨论如何在 STM32F7 上实现基于神经网络的人脸特征提取与匹配算法&…

微机原理_3

一、单项选择题(本大题共15小题,每小题3分,共45分。在每小题给出的四个备选项中,选出一个正确的答案,请将选定的答案填涂在答题纸的相应位置上。) 在 8086 微机系统中&#xff0c;完成对指令译码操作功能的部件是&#xff08;)。 A. EU B. BIU C. SRAM D. DRAM 使计算机执行某…

【机器学习】聚类(一):原型聚类:K-means聚类

文章目录 一、实验介绍1. 算法流程2. 算法解释3. 算法特点4. 应用场景5. 注意事项 二、实验环境1. 配置虚拟环境2. 库版本介绍 三、实验内容0. 导入必要的库1. Kmeans类a. 构造函数b. 闵可夫斯基距离c. 初始化簇心d. K-means聚类e. 聚类结果可视化 2. 辅助函数3. 主函数a. 命令…

ElasticSearch之虚拟内存

查看当前Linux系统中vm.max_map_count变量的值&#xff0c;命令如下&#xff1a; sysctl vm.max_map_count执行结果的样例&#xff0c;如下&#xff1a; vm.max_map_count 65530修改参数vm.max_map_count的值&#xff0c;命令如下&#xff1a; sysctl -w vm.max_map_count2…