当前位置: 首页 > news >正文

webRTC:流程和socket搭建信令服务器

上一篇文章 webRTC初探:如何实现音视频的录制 实现了电脑上的摄像头、麦克风、屏幕分享桌面的操控,本文更进一步介绍流程。

简单提一句PeerConnection

既然是两个浏览器(端)之间通信,那自然需要一个“用于维护双方关系的载体”,PeerConnection就是WebRTC中的这个载体,也是webrtc的核心。

let PeerConnection = window.RTCPeerConnection ||window.mozRTCPeerConnection ||window.webkitRTCPeerConnection;

PeerConnection是一个对象,它里面有一些核心方法,后面实现完整流程的时候会解释,这里为了下面的流程图不至于难懂先提一嘴:

  • addTrack() :添加音频或者视频轨道
  • addIceCandidate(): 保存 ICE 候选信息,即双方协商信息,持续整个建立通信过程,直到没有更多候选信息
  • createAnswer() :创建应答信令
  • createDataChannel(): 创建消息通道,建立WebRTC通信之后,就可以 p2p 的直接发送文本消息,无需中转服务器
  • createOffer(): 创建初始信令
  • setLocalDescription() :保存自己端创建的信令
  • setLocalDescription() :保存自己端创建的信令

WebRTC的会话流程

下图就是通过载体的核心方法和事件完成两个浏览器的关联的过程:
在这里插入图片描述

其中“圈1”是websocket通道的搭建和双方初始化PeerConnection,紧跟着有关信令(SDP)的交换和监听就是通过socket完成的。

信令服务器的搭建

所谓信令服务器,其实完全可以说是通过websocket交换信令的即时通讯服务器。
在实现之前,需要思考一下所需的数据和数据结构:既然是会议,那就需要“用户userId”和“房间roomId”,存储的话就通过Redis就好了,Redis中有一种数据结构Hash,大概结构是这样的:
在这里插入图片描述

具体代码如下:

const httpServer = require('http').createServer();
const io = require('socket.io')(httpServer);var redis = require('redis')
const roomKey = "meeting-room::"
var redisClient = redis.createClient(6379, '127.0.0.1')
redisClient.on('error', function (err) {console.log('redisClient connect Error ' ,err);
});const userMap = new Map()
io.on('connection', async (socket) => {await onListener(socket)
});httpServer.listen(18080, async() => {console.log('服务器启动成功:18080');await redisClient.connect();
});function getMsg(type,msg,status=200,data=null){return {"type":type,"msg":msg,"status":status,"data":data}
}function getParams(url,queryName){let query = decodeURI(url.split('?')[1]);let vars = query.split("&");for (var i = 0; i <= vars.length; i++) {var pair = vars[i].split("=");if (pair[0] === queryName) {return pair[1];}}return null;
}/*** DB data* @param {Object} userId* @param {Object} roomId*/
async function getUserDetailByUid(userId,roomId){let res = JSON.stringify(({"userId":userId,"roomId":roomId}))return res
}/*** 监听*/
async function onListener(s){let url = s.client.request.urllet userId = getParams(url,'userId')let roomId = getParams(url,'roomId')console.log("client uid:"+userId+" roomId: "+roomId+" online ")//user mapuserMap.set(userId,s)//room cacheif(roomId){await redisClient.hSet(roomKey+roomId,userId, await getUserDetailByUid(userId,roomId))oneToRoomMany(roomId,getMsg('join',userId+ ' join then room'))}s.on('msg', async (data) => {console.log("msg",data)await oneToRoomMany(roomId,data)});s.on('disconnect', () => { console.log("client uid:"+userId+" roomId: "+roomId+" offline ")userMap.delete(userId)if(roomId){redisClient.hDel(roomKey+roomId,userId)oneToRoomMany(roomId,getMsg('leave',userId+' leave the room '))}});    s.on('roomUserList', async (data) => {// console.log("roomUserList msg",data)s.emit('roomUserList',await getRoomUser(data['roomId']))})s.on('call',(data) => {let targetUid = data['targetUid']if(userMap.get(targetUid)){oneToOne(targetUid,getMsg('call',"远程呼叫",200,data))}else{console.log(targetUid+ "不在线")}})s.on('candidate',(data) => {let targetUid = data['targetUid']if(userMap.get(targetUid)){oneToOne(targetUid,getMsg('candidate',"ice candidate",200,data))}else{console.log(targetUid+ "不在线")}})s.on('offer',(data) => {let targetUid = data['targetUid']if(userMap.get(targetUid)){oneToOne(targetUid,getMsg('offer',"rtc offer",200,data))}else{console.log(targetUid+ "不在线")}})s.on('answer',(data) => {let targetUid = data['targetUid']if(userMap.get(targetUid)){oneToOne(targetUid,getMsg('answer',"rtc answer",200,data))}else{console.log(targetUid+ "不在线")}})
}/*** 单独用户提示* @param {Object} uid* @param {Object} msg*/
function oneToOne(uid,msg){let s = userMap.get(uid)if(s){s.emit('msg',msg)}else{console.log(uid+"用户不在线")}
}/*** 获取房间用户列表* @param {Object} roomId*/
async function getRoomUser(roomId){return await redisClient.hGetAll(roomKey+roomId)
}/*** 广播* @param {Object} roomId* @param {Object} msg*/
async function oneToRoomMany(roomId,msg){let ulist = await redisClient.hGetAll(roomKey+roomId)for(const uid in ulist){oneToOne(uid,msg)}
}
http://www.dtcms.com/a/613299.html

相关文章:

  • PoA 如何把 CodexField 从“创作平台”推向“内容经济网络”
  • 厦门 外贸商城网站建设网站推广哪个好
  • 小米Java开发校园招聘面试题及参考答案
  • 哪个网站做头像比较好网片式防护围栏
  • LangChain Memory 使用示例
  • 【剑斩OFFER】算法的暴力美学——寻找数组的中心下标
  • APIs---Day01
  • 猪只行为状态识别与分类:基于YOLO13-C3k2-ESC模型的实现与优化_3
  • 宁波网站建设方案推广公司网站设计
  • [智能体设计模式] 第10章:模型上下文协议(MCP)
  • 使用docker-composer安装MySQL8、Redis7、minio脚本
  • linux的nginx版本升级
  • 支持selenium的chrome driver更新到142.0.7444.162
  • 【 Java八股文面试 | JVM篇 内存结构、类加载、垃圾回收与性能调优 】
  • 网站开发和前端是一样吗化妆品网站模板
  • Mujoco 机械臂进行 PBVS 基于位置的视觉伺服思路
  • 【玄机靶场】Crypto-常见编码
  • 360加固 APK 脱壳研究:安全工程师视角下的防护与还原原理解析
  • AI面试速记
  • ASC学习笔记0018:返回属性集实例的引用(如果此组件中存在)
  • SpringBoot中整合RabbitMQ(测试+部署上线 最完整)
  • 第15章 并发编程
  • 【高级机器学习】 13. 因果推断
  • Qt for HarmonyOS 验证码组件开源鸿蒙开发实战
  • 河北购物网站开发公司营销型网站优势
  • wordpress 判断用户郑州seo询搜点网络效果佳
  • 企业门户网站模板 企业网站模板源码下载 企业网站模板搭建网站
  • Q6: 如何计算以太坊交易的美元成本?
  • 整体设计 全面梳理复盘 之37 元级自动化引擎三体项目(Designer/Master/Transformer)划分确定 + 自用规划工具(增强版)
  • 从昆仑芯到千问:AI产业“倒金字塔”的落地革命