Web实时通信(Web Real-Time Communication),简称 WebRTC,是一种革命性的开源技术,它大大简化了万维网上的实时通信,实现了网络浏览器之间的直接交互,而无需额外安装插件或软件。WebRTC 起源于科技巨头谷歌和爱立信在 2011 年的一次合作,已得到主要浏览器供应商的广泛接受和认可,多年来呈现出显著的上升趋势。
WebRTC 的精髓在于它能促进网络浏览器之间毫不费力的点对点通信,从而使用户能够参与大量的实时通信应用。其范围包括视频会议、语音通话、文件共享、实时流媒体、远程桌面和屏幕共享、物联网(IoT)应用和在线游戏,体现了 WebRTC 的多功能性。
深入了解 WebRTC
WebRTC 的核心在于其对点对点通信的精心设计,它使浏览器之间能够直接对话,而无需中间服务器的干预。这种去中心化的框架带来了大量好处,包括降低延迟、加强隐私保护和最大限度地减少服务器负载,从而为各种实时通信环境提供了理想的解决方案。
WebRTC 的支柱
媒体流:WebRTC 的架构在很大程度上依赖于媒体流,媒体流是浏览器之间进行实时音频和视频通信的通道。
数据通道:作为对媒体流的补充,WebRTC 扩展了对数据通道的支持,实现了实时点对点数据通信。这一功能为游戏、文件共享和协作编辑等应用注入了活力。
网络组件:WebRTC 利用一系列网络协议建立和维护点对点连接,实现用户之间的直接通信。这一框架的核心是三个关键网络组件: ICE、STUN 和 TURN,它们在穿越网络地址转换(NAT)和防火墙(网络通信中常见的障碍)方面发挥着至关重要的作用。
- ICE(交互式连接建立):
ICE 是一种协议,用于为两个对等节点找到最有效的通信路径,即使在存在 NAT 和防火墙的情况下也是如此。它从本地计算机以及 STUN 和 TURN 服务器收集一组候选 IP 地址。然后对这些地址对进行测试,以确定最有效的路径,确保数据能穿越 NAT 和防火墙。
- STUN(用于 NAT 的会话遍历实用程序):
STUN 是一种协议,有助于发现 NAT 为特定通信会话分配的公共 IP 地址和端口。当设备位于 NAT 后面时,其私有 IP 地址和外界看到的公共 IP 地址是不同的。STUN 可以帮助识别这个公共地址,从而促进直接通信通道的建立。
- TURN(使用中继穿透 NAT):
当点对点直接通信受到限制性 NAT 或防火墙的阻碍时,TURN 就会发挥作用。它通过提供公共中继站,在对等网络之间临时传输数据。TURN 服务器拥有公共 IP 地址,通过中继数据,它们可以帮助克服 NAT 和防火墙带来的限制,确保通信路径保持开放和正常运行。
深入探讨通过 NAT 穿越技术应对 WebRTC 中的连接挑战
网络地址转换(NAT)是一种重要机制,可使专用网络中的多个设备共享一个公共 IP 地址,从而节省有限的公共 IP 地址资源。然而,这一基本功能对在不同专用网络的设备之间建立直接连接提出了挑战,而这正是 WebRTC 实时通信的关键要求。本节将深入探讨 STUN 和 TURN 服务器的作用以及 WebRTC 中的 ICE 框架,它们共同应对了 NAT 带来的连接挑战。
STUN 服务器: 跨越 NAT 障碍
STUN(用于 NAT 的会话穿越实用程序)服务器在 WebRTC 中至关重要,它可以帮助设备发现其公共 IP 地址和 NAT 路由器分配的端口映射。这种发现对于绕过 NAT 引起的障碍,建立直接的点对点连接至关重要。
- 请求启动: WebRTC 设备向 STUN 服务器发出请求,以确定其公共 IP 地址和端口映射。
- 服务器响应: STUN 服务器响应从请求中观察到的所需信息。
- 信息提取: WebRTC 设备提取公共 IP 地址和端口信息。
- IP/Port 信息交换: 两台设备交换各自的公共 IP 和端口详细信息。
- 建立直接连接: 有了对方的公网 IP 和端口信息,设备就可以建立直接连接,而无需考虑 NAT 路由器。
ICE 框架: 确保高效的连接路径
交互式连接建立(ICE)对 WebRTC 至关重要,它能在对等方之间启动高效、直接的连接路径,尤其是在 NAT 和防火墙带来的挑战中。它精心收集候选网络路径并确定其优先级,确保为实时数据传输建立最佳连接。
- 收集候选路径: 收集可能建立连接的各种传输地址。
- 候选路径优先排序: 根据速度、可靠性和潜在的防火墙穿越能力等因素对候选路径进行优先排序。
- 连接性检查: 按照优先顺序进行一系列检查,确定候选配对的连接性,最终建立最有效的连接。
利用 STUN 和 TURN 发现连接
ICE 采用 STUN 和 TURN(使用 NAT 附近的中继进行遍历)来发现可行的连接路径。STUN 有助于识别设备的公共 IP 地址和端口,而 TURN 服务器则在对称 NAT 或限制性防火墙阻碍直接连接的情况下充当中继器。
TURN 服务器: 在限制条件下确保可靠通信
在 WebRTC 中,当点对点直接连接因限制性网络条件而受阻时,TURN 服务器将充当中继媒介,确保实时通信的连续性。
- 数据包中继请求: 当无法建立直接连接时,对等方会向 TURN 服务器发送数据包中继请求。
- 分配: TURN 服务器为特定会话中继分配临时 IP 地址和端口。
- 媒体和数据流量中继: TURN 服务器在对等设备之间中继媒体和数据包,模拟直接连接。
- 安全性: 加密可确保对等网络和 TURN 服务器之间交换数据的隐私和安全。
利用 WebRTC API 进行媒体处理
这项技术的核心是几个 API,每个 API 在建立和管理实时连接时都有不同的作用。
- getUserMedia() API 是访问用户媒体设备(如摄像头和麦克风)的网关。一旦获得许可,开发人员就可以捕获媒体流,并在其应用程序中加以利用,无论是用于显示、处理还是传输到其他对等设备。
- RTCPeerConnection 是 WebRTC 点对点通信的关键。该 API 负责管理对等点之间的连接、处理建立通信通道的协商过程、管理带宽以及处理网络变化。通过 RTCPeerConnection,可以将 getUserMedia() 捕捉到的媒体流发送到其他对等设备,从而实现实时视频和音频通信。
- RTCDataChannel 允许在对等设备之间共享任意数据。无论您是要创建实时聊天应用程序、共享文件还是传输游戏数据,RTCDataChannel 都能安全高效地促进此类数据通信。
跨浏览器兼容性
WebRTC 在 Chrome、Firefox 和 Safari 等主要浏览器上的采用证明了其有效性和潜力。然而,通往无缝用户体验的道路并非没有障碍。不同浏览器在实现 WebRTC API 方面的细微差别会导致兼容性问题,如果不加以解决,就会影响用户体验。
解决这些差异的策略之一是通过功能检测。通过识别浏览器支持的功能,开发人员可以为不支持的功能提供替代解决方案或优美降级,从而确保应用程序的可用性。
Polyfill 和 shim是防止跨浏览器不兼容的另一道防线。通过弥合浏览器本机实现与标准化 WebRTC API 之间的差距,它们为开发人员提供了统一的界面,无论使用的浏览器是什么。
此外,在服务器端对媒体流进行转码也是确保不同平台用户体验一致性的可行策略。通过改变媒体流的格式或质量以匹配接收端的能力,转码有助于克服不同浏览器实施带来的限制。
演示项目设置
步骤 1:HTML 和客户端脚本
在本节中,我们将创建 HTML 结构并包含两个视频元素,一个用于显示本地视频流,另一个用于显示远程视频流。我们还包括 Socket.IO 库和客户端 JavaScript 代码。
<!DOCTYPE html>
<html>
<head>
<title>WebRTC with Socket.IO</title>
</head>
<body>
<!-- Video elements for local and remote video streams -->
<video id="localVideo" autoplay muted></video>
<video id="remoteVideo" autoplay></video>
<!-- Include Socket.IO library -->
<script src="/socket.io/socket.io.js"></script>
<script>
// Client-side JavaScript code starts here
// ...
</script>
</body>
</html>
步骤 2:服务器端脚本(app.js)
在本节中,我们将使用 Express 和 Socket.IO 创建一个服务器,以处理对等程序之间的信号。
const express = require("express");
const app = express();
const server = require("http").createServer(app);
const io = require("socket.io")(server);
// Set up a route to serve the index.html file
app.get("/", (_, res) => {
res.sendFile(__dirname + "/index.html");
});
// Socket.IO event handling
io.on("connection", (socket) => {
// Handle "offer" event from the client
socket.on("offer", (data) => {
socket.broadcast.emit("offer", data);
});
// Handle "answer" event from the client
socket.on("answer", (data) => {
socket.broadcast.emit("answer", data);
});
// Handle "candidate" event from the client
socket.on("candidate", (data) => {
socket.broadcast.emit("candidate", data);
});
});
// Start the server and listen on port 3000
server.listen(3000, () => {
console.log("listening on *:3000");
});
步骤 3:客户端脚本(index.html)
<script>
const socket = io();
// Get references to the local and remote video elements
const localVideo = document.getElementById("localVideo");
const remoteVideo = document.getElementById("remoteVideo");
// Configuration for the RTCPeerConnection
const configuration = {
iceServers: [
{
urls: [
"stun:stun1.l.google.com:19302",
"stun:stun2.l.google.com:19302",
],
},
],
iceCandidatePoolSize: 10,
};
// Create a new RTCPeerConnection object with the configuration
const pc = new RTCPeerConnection(configuration);
// Get user media (camera and microphone)
navigator.mediaDevices
.getUserMedia({ video: true, audio: true })
.then((stream) => {
// Display the local stream in the "localVideo" element
localVideo.srcObject = stream;
// Add tracks from the local stream to the RTCPeerConnection
stream.getTracks().forEach((track) => pc.addTrack(track, stream));
// Set up the onnegotiationneeded event for the RTCPeerConnection
pc.onnegotiationneeded = () => {
pc.createOffer()
.then((offer) => pc.setLocalDescription(offer))
.then(() => socket.emit("offer", pc.localDescription));
};
});
// Send ICE candidates to the server via Socket.IO
pc.onicecandidate = ({ candidate }) => {
if (candidate) {
socket.emit("candidate", candidate);
}
};
// Set the remote video stream when it becomes available
pc.ontrack = ({ streams: [stream] }) => {
remoteVideo.srcObject = stream;
};
// Handle incoming "offer" from the server
socket.on("offer", (description) => {
pc.setRemoteDescription(description)
.then(() => pc.createAnswer())
.then((answer) => pc.setLocalDescription(answer))
.then(() => socket.emit("answer", pc.localDescription));
});
// Handle incoming "answer" from the server
socket.on("answer", (description) => {
pc.setRemoteDescription(description);
});
// Handle incoming "candidate" from the server
socket.on("candidate", (candidate) => {
pc.addIceCandidate(new RTCIceCandidate(candidate));
});
</script>
逐步说明:
- 客户端 JavaScript 代码使用 Socket.IO 与服务器建立连接,并获取本地和远程视频元素的引用。
- 使用 ICE 服务器的配置建立 RTCPeerConnection,以实现点对点通信。
- getUserMedia 函数用于请求访问用户的摄像头和麦克风。一旦获准,本地视频流就会显示在本地视频元素中,其轨迹也会添加到 RTCPeerConnection 中。
- 设置 onnegotiationneeded 事件是为了在需要协商时创建要约。然后将此要约设置为 RTCPeerConnection 的本地描述,并作为要约事件通过 Socket.IO 发送给服务器。
- 每当收集到一个 ICE 候选人时,就会触发 onicecandidate 事件。当这种情况发生时,客户端会通过 Socket.IO 将候选 ICE 作为候选事件发送给服务器。
- ontrack 事件用于在远程视频流可用时对其进行设置。
- 客户端会监听服务器传入的要约、应答和候选事件。收到要约时,客户端会将其设置为 RTCPeerConnection 的远程描述,创建应答,将其设置为本地描述,并将应答作为应答事件发送回服务器。
- 收到应答事件后,客户端将其设置为 RTCPeerConnection 的远程描述。
- 收到候选事件时,客户端会将收到的 ICE 候选事件添加到 RTCPeerConnection。
完整示例代码:
https://github.com/emmayusufu/webrtc-demo
结论
WebRTC 深刻影响了我们通过互联网进行通信和协作的方式,为实时通信应用程序提供了强大且可扩展的解决方案。通过了解其核心组件、应对网络挑战并利用其 API,开发人员可以释放大量创建交互式实时 Web 应用程序的机会,从而塑造数字交互的新时代。
作者:Kimaswaemma
本文来自作者投稿,版权归原作者所有。如需转载,请注明出处:https://www.nxrte.com/jishu/webrtc/35334.html