WebRTC是浏览器内置的实时通信标准,允许两个客户端之间直接建立连接传输音视频、任意数据,无需经过中间服务器转发数据,仅需要信令服务器辅助交换连接信息。下面我们将一步步实现纯JavaScript的WebRTC点对点通信应用。

核心概念说明
在开发前需要先了解几个WebRTC的核心概念:
- RTCPeerConnection:WebRTC的核心连接对象,负责管理点对点连接的建立、维护和数据传输。
- 信令服务器:用于两个客户端交换SDP(会话描述协议)和ICE候选信息,本身不参与数据传输,仅做信息中转。
- ICE候选:用于描述客户端的网络地址信息,包括公网IP、内网IP、端口等,帮助双方找到可连通的网络路径。
- SDP:描述连接的媒体能力、编码格式等信息,双方需要交换各自的SDP来协商通信参数。
前置准备
我们需要准备一个简单的信令服务器,这里使用Node.js和WebSocket实现,用于两个客户端之间转发SDP和ICE候选信息。首先初始化项目并安装依赖:
mkdir webrtc-demo cd webrtc-demo npm init -y npm install ws
信令服务器实现
信令服务器的逻辑很简单,接收客户端发送的WebSocket消息,然后转发给另一个连接的客户端:
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });
// 存储两个连接的客户端
let clients = [];
wss.on('connection', (ws) => {
clients.push(ws);
console.log('新客户端连接,当前连接数:', clients.length);
ws.on('message', (message) => {
// 把消息转发给另一个客户端
const otherClient = clients.find(client => client !== ws);
if (otherClient && otherClient.readyState === WebSocket.OPEN) {
otherClient.send(message);
}
});
ws.on('close', () => {
clients = clients.filter(client => client !== ws);
console.log('客户端断开,当前连接数:', clients.length);
});
});
console.log('信令服务器启动在 ws://127.0.0.1:8080');
客户端实现
客户端使用纯JavaScript编写,分为连接信令服务器、创建RTCPeerConnection、交换SDP和ICE候选、建立连接几个步骤。
HTML结构
简单的页面结构,包含两个按钮用于发起连接和发送消息,一个输入框输入消息内容,一个区域显示接收的消息:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WebRTC点对点通信</title>
</head>
<body>
<button id="startBtn">发起连接</button>
<div>
<input type="text" id="messageInput" placeholder="输入要发送的消息">
<button id="sendBtn">发送消息</button>
</div>
<div id="messageArea"></div>
<script src="client.js"></script>
</body>
</html>
客户端JavaScript逻辑
client.js的完整实现如下:
// 连接信令服务器
const ws = new WebSocket('ws://127.0.0.1:8080');
// 创建RTCPeerConnection实例,配置STUN服务器用于获取公网地址
const peerConnection = new RTCPeerConnection({
iceServers: [
{ urls: 'stun:stun.l.google.com:19302' } // 公共STUN服务器
]
});
// 用于发送数据的DataChannel
let dataChannel = null;
// 获取页面元素
const startBtn = document.getElementById('startBtn');
const sendBtn = document.getElementById('sendBtn');
const messageInput = document.getElementById('messageInput');
const messageArea = document.getElementById('messageArea');
// 显示消息到页面
function showMessage(text) {
const p = document.createElement('p');
p.textContent = text;
messageArea.appendChild(p);
}
// 处理ICE候选事件,将候选信息通过信令服务器发送给对方
peerConnection.onicecandidate = (event) => {
if (event.candidate) {
ws.send(JSON.stringify({
type: 'ice-candidate',
candidate: event.candidate
}));
}
};
// 处理DataChannel消息接收
peerConnection.ondatachannel = (event) => {
const receiveChannel = event.channel;
receiveChannel.onmessage = (e) => {
showMessage('收到消息:' + e.data);
};
receiveChannel.onopen = () => {
showMessage('数据通道已打开,可以发送消息');
sendBtn.disabled = false;
};
};
// WebSocket连接成功
ws.onopen = () => {
showMessage('信令服务器连接成功');
startBtn.disabled = false;
};
// 处理接收到的信令消息
ws.onmessage = async (event) => {
const message = JSON.parse(event.data);
if (message.type === 'offer') {
// 收到对方的offer,设置为远程描述,然后创建answer
await peerConnection.setRemoteDescription(new RTCSessionDescription(message.offer));
const answer = await peerConnection.createAnswer();
await peerConnection.setLocalDescription(answer);
ws.send(JSON.stringify({
type: 'answer',
answer: answer
}));
} else if (message.type === 'answer') {
// 收到对方的answer,设置为远程描述
await peerConnection.setRemoteDescription(new RTCSessionDescription(message.answer));
} else if (message.type === 'ice-candidate') {
// 收到对方的ICE候选,添加到连接中
await peerConnection.addIceCandidate(new RTCIceCandidate(message.candidate));
}
};
// 发起连接按钮点击事件
startBtn.addEventListener('click', async () => {
// 创建DataChannel用于传输数据
dataChannel = peerConnection.createDataChannel('chat');
dataChannel.onopen = () => {
showMessage('数据通道已打开,可以发送消息');
sendBtn.disabled = false;
};
dataChannel.onmessage = (e) => {
showMessage('收到消息:' + e.data);
};
// 创建offer
const offer = await peerConnection.createOffer();
await peerConnection.setLocalDescription(offer);
ws.send(JSON.stringify({
type: 'offer',
offer: offer
}));
showMessage('已发起连接请求,等待对方响应');
startBtn.disabled = true;
});
// 发送消息按钮点击事件
sendBtn.addEventListener('click', () => {
const message = messageInput.value.trim();
if (message && dataChannel && dataChannel.readyState === 'open') {
dataChannel.send(message);
showMessage('发送消息:' + message);
messageInput.value = '';
}
});
运行测试步骤
按照以下步骤测试应用:
- 启动信令服务器:在终端执行
node server.js,看到启动成功提示。 - 打开两个浏览器窗口,都访问客户端的HTML页面,两个窗口都会连接到信令服务器。
- 在其中一个窗口点击发起连接按钮,另一个窗口会自动收到offer并响应answer,双方交换ICE候选后建立连接。
- 连接建立后,两个窗口都可以在输入框输入消息,点击发送按钮,对方就能收到消息。
常见问题说明
- 如果两个客户端在同一内网,可能不需要STUN服务器也能连通,但跨网络场景必须配置STUN或TURN服务器。
- 信令服务器仅做信息中转,实际数据不会经过服务器,符合WebRTC点对点传输的特性。
- 如果需要传输音视频,只需要调用
getUserMedia获取媒体流,然后通过addTrack方法添加到RTCPeerConnection即可,流程和传输数据类似。
注意:实际生产环境中需要更完善的信令服务器逻辑,处理多用户、断线重连等场景,同时STUN/TURN服务器也需要自己部署或使用可靠的服务。
WebRTCJavaScript点对点通信RTCPeerConnection修改时间:2026-07-08 12:06:39