WebRTC: основы
WebRTC (Web Real-Time Communication) — браузерный API для прямой peer-to-peer передачи аудио, видео и данных между клиентами без промежуточного сервера.
Зачем нужно
В отличие от WebSocket и SSE, которые проходят через сервер, WebRTC создаёт прямое соединение между браузерами. Это снижает задержку, экономит серверные ресурсы и позволяет передавать медиапотоки (видеозвонки, screensharing) с минимальным латентностью. Используется в Google Meet, Zoom Web, Discord.
Где используется
- Видеоконференции в браузере (без плагинов)
- Peer-to-peer передача файлов
- Совместная игра в реальном времени
- Screen sharing и remote desktop в браузере
- Голосовые чаты в WebApps
Процесс установки соединения (Signaling)
Клиент A Сигнальный сервер Клиент B
| (WebSocket/HTTP) |
|--- Offer (SDP) ------>| |
| |--- Offer (SDP) ------------>|
| |<--- Answer (SDP) -----------|
|<--- Answer (SDP) -----| |
|--- ICE candidate ---->|--- ICE candidate ---------->|
|<--- ICE candidate ----|<--- ICE candidate -----------|
|<=======================Прямое P2P соединение======= () => |
Базовый пример
Клиент A — создаёт предложение (Offer)
// Получаем медиапоток (камера + микрофон)
const localStream = await navigator.mediaDevices.getUserMedia({
video: true,
audio: true,
});
// Отображаем локальное видео
document.getElementById('localVideo').srcObject = localStream;
// Создаём RTCPeerConnection
const pc = new RTCPeerConnection({
iceServers: [
{ urls: 'stun:stun.l.google.com:19302' }, // STUN для обнаружения внешнего IP
],
});
// Добавляем локальные треки
localStream.getTracks.forEach(track => pc.addTrack(track, localStream));
// Получаем удалённый поток
pc.ontrack = event => {
document.getElementById('remoteVideo').srcObject = event.streams[0];
};
// ICE кандидаты — отправляем через сигнальный сервер
pc.onicecandidate = event => {
if (event.candidate) {
signalingChannel.send({ type: 'ice', candidate: event.candidate });
}
};
// Создаём Offer
const offer = await pc.createOffer;
await pc.setLocalDescription(offer);
signalingChannel.send({ type: 'offer', sdp: offer });
Клиент B — принимает и отвечает
const pc = new RTCPeerConnection({ iceServers: [{ urls: 'stun:stun.l.google.com:19302' }] });
signalingChannel.on('message', async ({ type, sdp, candidate }) => {
if (type === 'offer') {
await pc.setRemoteDescription(sdp);
const localStream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
localStream.getTracks.forEach(t => pc.addTrack(t, localStream));
const answer = await pc.createAnswer;
await pc.setLocalDescription(answer);
signalingChannel.send({ type: 'answer', sdp: answer });
}
if (type === 'answer') {
await pc.setRemoteDescription(sdp);
}
if (type === 'ice') {
await pc.addIceCandidate(candidate);
}
});
DataChannel — передача данных без медиа
// Создание канала данных (клиент A)
const channel = pc.createDataChannel('chat');
channel.onopen = () => channel.send('Привет!');
channel.onmessage = e => console.log('Сообщение:', e.data);
// Клиент B получает канал
pc.ondatachannel = event => {
const channel = event.channel;
channel.onmessage = e => console.log('Получено:', e.data);
};
Частые ошибки
- Путают WebRTC с WebSocket — WebRTC P2P, WebSocket через сервер
- Не учитывают NAT traversal — STUN/TURN серверы обязательны в продакшне
- Забывают освобождать медиапотоки при завершении:
stream.getTracks.forEach(t => t.stop()) - Реализуют signaling по HTTP вместо WebSocket — задержка при обмене ICE-кандидатами
Связанные темы
Ресурсы
🎓 Источник: WebRTC для создания P2P чата (Node.js STUN, сигнальный сервер)
- 📅 2025-12-12 · YouTube
- Тезисы:
- Два сервера: сигнальный (HTTP) для обмена адресами + STUN (UDP) для узнавания своего внешнего IP/порта.
- STUN-сервер раскрывает внешний адрес за NAT через датаграммный обмен.
inbox(очередь сообщений) на сигнальном сервере — если пир отключится, после переподключения получит накопленные сигналы.- Парсинг тела HTTP через
for await (chunk of req)— async iterable стрим. - Бинарный протокол STUN — компактный формат поверх UDP.
- Сигнальный код на HTTP не переносится на WebRTC автоматически — нужно отвязать бизнес-логику от транспорта.
- Цитата: «
joinRoomпривязан к HTTP, к кодам ответов 400/200 и readable stream — его не пересадить на WebRTC или WebSocket автоматически.»