Розробка WebRTC P2P-обміну даними на сайті

Наша компанія займається розробкою, підтримкою та обслуговуванням сайтів будь-якої складності. Від простих односторінкових сайтів до масштабних кластерних систем, побудованих на мікро сервісах. Досвід розробників підтверджено сертифікатами від вендорів.

Розробка та обслуговування будь-яких видів сайтів:

Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Послуги, які ми пропонуємо
Показано 1 з 1Усі 2062 послуг
Розробка WebRTC P2P-обміну даними на сайті
Складний
~5 днів
Часті запитання

Наші компетенції:

Етапи розробки

Останні роботи

  • image_website-b2b-advance_0.webp
    Розробка сайту компанії B2B ADVANCE
    1358
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1250
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    956
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1188
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    929
  • image_bitrix-bitrix-24-1c_fixper_448_0.webp
    Розробка веб-сайту для компанії ФІКСПЕР
    947

Уявіть: потрібно передати 2 ГБ відео між двома браузерами, але серверна інфраструктура не гумова. Стандартний підхід — завантажити файл на сервер, стиснути, віддати другому клієнту — впирається в пропускну здатність каналу та вартість трафіку. Рішення — RTCDataChannel. Цей протокол на SCTP/DTLS створює прямий канал між браузерами, минаючи сервер. Ми, команда з 5-річним досвідом у WebRTC, реалізували десятки таких рішень: від файлообміну до багатокористувацьких редакторів. Одного разу ми впровадили P2P-файлообмін для компанії, що передавала терабайти даних між офісами — економія на серверному трафіку склала 70%. За роки роботи ми накопичили понад 30 успішних проектів з WebRTC, включаючи спільні редактори, ігрові беклоги та системи синхронізації.

Чому RTCDataChannel — вибір для P2P-обміну?

WebRTC не обмежується відеодзвінками. RTCDataChannel — це низькорівневий канал даних між браузерами із затримками порядку 20–50 мс, що працює поверх SCTP/DTLS без серверного посередника. Файлообмінник, спільний редактор, ігровий беклог, mesh-синхронізація — все це будується на DataChannel. Головна перевага — дані не проходять через ваші сервери. Це критично для файлообміну, E2E-зашифрованих чатів, приватних ігрових сесій. Комерційно це означає економію на серверному трафіку та підвищення приватності.

Параметр WebSocket RTCDataChannel
Маршрут даних Клієнт → Сервер → Клієнт Клієнт → Клієнт (P2P)
Затримка 50–200 мс (через сервер) 20–60 мс (прямий канал)
Надійність TCP (ordered, reliable) Налаштовувана
Шифрування TLS DTLS (обов'язково)
Навантаження на сервер Весь трафік Тільки сигналізація

Як реалізувати P2P-обмін даними без сервера?

Як налаштувати DataChannel?

  1. Створіть RTCPeerConnection з ICE-серверами.
  2. Викличте createDataChannel() з ім'ям та конфігурацією.
  3. Встановіть обробники подій onopen, onclose, onmessage.
  4. На стороні отримувача обробіть подію ondatachannel.
const pc = new RTCPeerConnection({ iceServers: [...] });

const channel = pc.createDataChannel('files', {
  ordered: true,
});

channel.binaryType = 'arraybuffer';
channel.bufferedAmountLowThreshold = 65536;

channel.onopen = () => console.log('DataChannel open');
channel.onclose = () => console.log('DataChannel closed');
channel.onmessage = (e) => handleMessage(e.data);

pc.ondatachannel = (e) => {
  const remoteChannel = e.channel;
  remoteChannel.onmessage = (e) => handleMessage(e.data);
};

Режими надійності

SCTP під DataChannel дозволяє налаштовувати семантику доставки:

Режим Конфігурація Застосування
ordered + reliable за замовчуванням Файли, повідомлення чату
unordered + unreliable maxRetransmits: 0 Ігрові позиції, курсори
ordered + maxPacketLifeTime maxPacketLifeTime: 100 мс Голосові команди, введення з клавіатури

Як організувати передачу файлів?

Браузерний DataChannel обмежений розміром повідомлення ~256 KB. Файли потрібно чанкувати:

const CHUNK_SIZE = 64 * 1024;

async function sendFile(channel, file) {
  const metadata = JSON.stringify({
    name: file.name,
    size: file.size,
    type: file.type,
    chunks: Math.ceil(file.size / CHUNK_SIZE),
  });

  channel.send(metadata);

  const buffer = await file.arrayBuffer();
  let offset = 0;

  function sendNextChunk() {
    while (offset < buffer.byteLength) {
      if (channel.bufferedAmount > channel.bufferedAmountLowThreshold * 2) {
        channel.onbufferedamountlow = () => {
          channel.onbufferedamountlow = null;
          sendNextChunk();
        };
        return;
      }

      const chunk = buffer.slice(offset, offset + CHUNK_SIZE);
      channel.send(chunk);
      offset += CHUNK_SIZE;
    }
    channel.send(JSON.stringify({ type: 'transfer-complete' }));
  }

  sendNextChunk();
}

Отримувач збирає частини:

let receivedSize = 0;
let receivedChunks = [];
let fileMetadata = null;

channel.onmessage = (e) => {
  if (typeof e.data === 'string') {
    const msg = JSON.parse(e.data);
    if (msg.name) {
      fileMetadata = msg;
    } else if (msg.type === 'transfer-complete') {
      const blob = new Blob(receivedChunks);
      triggerDownload(blob, fileMetadata.name);
    }
  } else {
    receivedChunks.push(e.data);
    receivedSize += e.data.byteLength;
  }
};

Як реалізувати E2E-шифрування?

DataChannel вже зашифрований DTLS. Для додаткового E2E-шифрування використовуємо Web Crypto API:

const keyPair = await crypto.subtle.generateKey(
  { name: 'ECDH', namedCurve: 'P-256' },
  false, ['deriveKey']
);

const publicKeyExported = await crypto.subtle.exportKey('raw', keyPair.publicKey);

const sharedKey = await crypto.subtle.deriveKey(
  { name: 'ECDH', public: partnerPublicKey },
  keyPair.privateKey,
  { name: 'AES-GCM', length: 256 },
  false, ['encrypt', 'decrypt']
);

Як побудувати mesh-мережу для кількох користувачів?

До 4–6 учасників допустима full-mesh топологія:

class MeshNetwork {
  constructor(signalSocket) {
    this.peers = new Map();
    this.channels = new Map();
    this.signal = signalSocket;
  }

  async connectTo(userId) {
    const pc = new RTCPeerConnection(ICE_CONFIG);
    this.peers.set(userId, pc);

    const channel = pc.createDataChannel('mesh');
    this.channels.set(userId, channel);
    channel.onmessage = (e) => this.onData(userId, e.data);

    const offer = await pc.createOffer();
    await pc.setLocalDescription(offer);
    this.signal.emit('offer', { to: userId, offer });
  }

  broadcast(data) {
    const message = JSON.stringify(data);
    this.channels.forEach(ch => {
      if (ch.readyState === 'open') ch.send(message);
    });
  }
}

Які обмеження у DataChannel?

  • Safari не підтримує bufferedAmountLowThreshold у старих версіях — потрібен polling.
  • Firefox має максимальний розмір повідомлення 256 KB; Chrome — аналогічно.
  • Мобільні браузери можуть закривати DataChannel при переході у фон — потрібна логіка перепідключення.
  • При втраті з'єднання iceConnectionState === 'failed' — потрібен явний reconnect.
Технічні обмеженняКрім зазначених, варто враховувати обмеження браузерів на кількість одночасних DataChannel (зазвичай до 255) та пропускну здатність до 10-20 Мбіт/с залежно від мережі.

Як уникнути типових помилок при роботі з DataChannel?

  • Ігнорування bufferedAmount. Якщо надсилати дані швидше, ніж їх обробляє SCTP, буфер переповнюється — браузер закриває канал. Завжди контролюйте bufferedAmountLowThreshold.
  • Відсутність повторного з'єднання. При тимчасовій втраті мережі (наприклад, перемикання Wi-Fi) DataChannel не відновлюється автоматично. Реалізуйте логіку restart ICE або повний reconnect через signaling.
  • Передача метаданих як простого тексту. Якщо порядок повідомлень не гарантовано, метадані файлу можуть прийти після початку чанків — використовуйте прапорець типу повідомлення.
  • Забувати про E2E-шифрування. DTLS шифрує лише канал, але якщо ваш сигнальний сервер скомпрометовано, зловмисник може вбудуватися в P2P-сесію. Додавайте додаткову аутентифікацію через Web Crypto.

Що входить в роботу?

Ми пропонуємо повний цикл розробки під ключ:

  • Проектування архітектури (вибір топології — full-mesh/star/hybrid, налаштування сигналізації на WebSocket або Firebase)
  • Реалізація клієнтської та серверної частин: стек React/TypeScript, Node.js для signaling, Docker для розгортання
  • Налаштування ICE-серверів (STUN/TURN) для обходу NAT
  • Інтеграція E2E-шифрування з ключовим обміном за протоколом ECDH
  • Написання документації та розгортання з CI/CD (GitHub Actions, Cloudflare)
  • Навчальна сесія для вашої команди — 2 години з демонстрацією коду
  • Гарантія на код 12 місяців та безкоштовна підтримка перший місяць

Сроки:

  • Базовий файлообмінник P2P (2 учасники) — від 3 до 4 днів
  • Багатокористувацький mesh з кількома каналами — 1–2 тижні
  • E2E-шифрування + ключовий обмін — плюс 3–4 дні
  • Спільний редактор / whiteboard поверх DataChannel — 2–4 тижні

Зв'яжіться з нами для оцінки вашого проекту — ми розрахуємо точну вартість та терміни. Замовте розробку P2P-рішення під ключ та отримайте консультацію безкоштовно. Ми гарантуємо, що ваш проект буде реалізований з урахуванням усіх обмежень браузерів та оптимальної продуктивності.

Розробка систем реального часу: WebRTC, SSE, WebSocket

Ми знаємо, як боляче, коли полінг вбиває сервер. Один наш проєкт — платформа для онлайн-аукціонів — використовував полінг кожні 2 секунди. Під навантаженням у 400 учасників сервер отримував 12 000 HTTP-запитів на хвилину заради однієї ставки. 90% відповідей — пусті. Після переходу на WebSocket навантаження впало в 15 разів, економія серверних ресурсів — значна сума. Замовте розробку real-time функцій під ключ — отримайте готове рішення з гарантією стабільності.

Реалізація real-time на продакшні — не просто бібліотека. Ми проектуємо архітектуру під навантаження, сценарії та бюджет. Нижче — розбір ключових рішень з прикладами.

Три транспорти реального часу: коли що вибирати

Server-Sent Events працюють поверх звичайного HTTP/1.1 або HTTP/2. Браузер відкриває з'єднання, сервер тримає його відкритим і пушить події у форматі text/event-stream. Автоматичне перепідключення вбудоване — reconnect-логіка не потрібна. Обмеження: тільки сервер → клієнт. Ідеально для нотифікацій, прогресу довгих завдань, live-фідів.

WebSocket — повнодуплексний канал після HTTP Upgrade-рукопотискання. Браузер і сервер обмінюються фреймами в обидві сторони. Підходить для чатів, спільного редагування, ігор, торгових терміналів. Вимагає окремої обробки reconnect-логіки та heartbeat (ping/pong кожні 30 секунд, інакше NAT-таблиці закривають з'єднання).

WebRTC — peer-to-peer аудіо/відео та дані між браузерами напряму, минаючи сервер. Сервер потрібен лише для сигналізації (STUN/TURN для обходу NAT). TURN-сервер потрібен у 20–30% випадків (корпоративні мережі, симетричний NAT). Для сервісу телемедицини ми впровадили WebRTC: затримка звуку впала з 800 мс (через релей) до 50 мс (P2P). TURN-сервер знадобився лише 15% сесій, що зекономило значні кошти на трафіку.

WebSocket (Wikipedia) WebRTC (Wikipedia)

Як правильно вибрати транспорт: покрокова інструкція

  1. Визначте сценарій обміну даними: однонаправлений (сервер → клієнт) — SSE; двонаправлений з низькою затримкою — WebSocket; аудіо/відео — WebRTC.
  2. Оцініть вимоги до затримки. Якщо прийнятно <500 мс — підійде SSE; для <100 мс і двонаправленості — WebSocket; для <50 мс і P2P — WebRTC.
  3. Перевірте бюджет на інфраструктуру. SSE використовує звичайні HTTP-сервери, WebSocket вимагає тримати з'єднання в пам'яті, WebRTC може потребувати TURN-сервер (додаткові витрати).
  4. Врахуйте масштабування: для 100k+ з'єднань розгляньте WebSocket-gateway (Centrifugo, Pushpin).
Транспорт Напрямок Затримка Складність реалізації Типові сценарії
WebSocket Повний дуплекс < 100 мс Середня Чати, ігри, торгівля
SSE Тільки сервер → клієнт < 500 мс Низька Нотифікації, стрічки прогресу
WebRTC P2P аудіо/відео/дані < 50 мс Висока Відеодзвінки, передача файлів

Що таке CRDT і чим він кращий за Operational Transformation?

Спільне редагування — не просто «хто останній записав, той і правий». Без алгоритму злиття колізій два користувачі вставляють текст у позицію 45, перший зберігає — позиція зсувається, другий зберігає поверх — операція застосовується до застарілого стану. Текст дублюється або втрачається.

OT (Operational Transformation) потребує сервера для вирішення конфліктів, CRDT (Conflict-free Replicated Data Types) працює без централізованого координатора. Yjs — найбільш зріла CRDT-бібліотека для браузера. Інтегрується з ProseMirror, TipTap, CodeMirror, Monaco Editor.

Порівняння бібліотек для спільного редагування

Бібліотека Алгоритм Підтримка редакторів Складність Продуктивність
Yjs CRDT ProseMirror, TipTap, CodeMirror, Monaco Середня Висока (<10 мс при 100 операціях)
ShareDB OT ProseMirror, Quill Середня Середня (потрібен сервер для злиття)
Automerge CRDT Будь-який (RichText) Висока Хороша (але пам'ять зростає швидше за Yjs)

Проблема: розмір Yjs-документа зростає через історію операцій. Потрібне періодичне збирання сміття — snapshot документа + очищення старих операцій. Без цього документ, над яким працювали рік, може важити 50 МБ.

Приклад heartbeat на WebSocket (Node.js)
const ws = new WebSocket('wss://example.com');
let pingInterval;

ws.on('open', () => {
  pingInterval = setInterval(() => {
    ws.ping();
    setTimeout(() => {
      if (ws.readyState === WebSocket.OPEN) ws.terminate();
    }, 5000);
  }, 25000);
});

ws.on('close', () => clearInterval(pingInterval));

Типові помилки при впровадженні real-time

Memory leak на сервері — забули видалити обробник події при закритті з'єднання. На Node.js heap зростає ~1 МБ/год. EventEmitter попереджає про 10+ слухачів, але не завжди це помічають.

Thundering herd при реконнекті. Сервер упав на 30 секунд, піднявся — 10 000 клієнтів намагаються перепідключитися одночасно. Exponential backoff з jitter обов'язковий: delay = Math.min(baseDelay * 2^attempt + random(0, 1000), maxDelay).

Відсутність індикації втрати з'єднання. WebSocket не завжди сповіщає про розрив (наприклад, телефон пішов у тунель). Heartbeat вирішує проблему.

Процес роботи

Починаємо з вибору транспорту під сценарії — іноді в одному проєкті потрібні всі три: SSE для системних нотифікацій, WebSocket для чату, WebRTC для відеодзвінків. Проектуємо протокол повідомлень (JSON з type і payload, рідше бінарний через MessagePack). Розробляємо з тестуванням race conditions — це не покривається юніт-тестами.

Навантажувальне тестування з k6 + k6/experimental/websockets: моделюємо 5 000 одночасних з'єднань з реальним патерном. Інженери мають сертифікати з WebSocket і WebRTC, гарантуємо стабільність 99.9%.

Що входить

  • Архітектура real-time шару (вибір транспорту, протокол повідомлень)
  • Реалізація з навантажувальним тестуванням (k6, сценарії race conditions)
  • Інтеграція з бекендом через Redis Pub/Sub або аналогічну шину
  • Документація з протоколу та схем даних
  • Навчання вашої команди
  • Технічна підтримка 2 тижні після запуску

Чому Centrifugo може бути вигіднішим за Socket.io?

Socket.io простіше в налаштуванні (1–2 дні), але центрифуга на Go тримає 1M+ з'єднань на одній ноді. Для 100k+ одночасних клієнтів Centrifugo економить до 40% витрат на інфраструктуру. Отримайте консультацію — ми допоможемо вибрати стек під ваше навантаження.

Строки

  • Базовий WebSocket-чат або нотифікації поверх існуючого API: 1–3 тижні.
  • Коллаборативний редактор з Yjs і persistence: 4–8 тижнів.
  • WebRTC відеодзвінки з записом: 6–12 тижнів (значна частина — інтеграція з медіасервером mediasoup або Janus).

Зв'яжіться з нами для оцінки вашого проєкту. Обговоріть завдання з інженером — оцінимо складність і строки індивідуально.