技術 約11分で読めます

ウェブ界隈の低レイテンシ・リアルタイム同期通信:技術比較と実装ガイド

別プロジェクトでリアルタイムゲーム作ってて、プレイヤー間の動きをリアルタイムに同期させる必要があった。前に書いた数万人同時視聴イベントの設計パターンでは「疑似同期」(サーバ時刻基準、一方向性が強い)について解説した。

でも今回は違う。クライアント間で直接、またはサーバ経由で「今この瞬間」の状態を共有して、双方向のやり取りが必要だった。

こういう「小規模だけど低レイテンシ・双方向リアルタイム同期」の話。

疑似同期 vs 真同期

大規模イベントの設計と低レイテンシ同期は根本から違う。

項目大規模イベント(疑似同期)リアルタイム双方向(真同期)
規模数万人数人~数千人
同期方向一方向(サーバ→全員)双方向
同期精度秒単位でOKms単位が必須
サーバ時刻の役割主役(絶対基準)補助的
ネットワーク負荷低頻度ポーリング常時通信
ユースケース放送、一斉視聴ゲーム、コラボ、チャット

疑似同期はサーバ負荷を最小化するための工夫。一方、真同期はクライアント間の状態同期そのものが目的で、どこまで遅延を減らせるかが勝負。

技術比較表

代表的な7つの通信技術を並べてみた。

技術平均遅延実装難度スケーラビリティブラウザサポート双方向P2P対応
WebSocket10-50ms★★☆中程度(スケールアウト複雑)★★★★★
WebRTC Data Channel5-30ms★★★★高い(P2P可)★★★★☆
SSE + HTTP/250-200ms★☆☆中程度★★★★★✗(一方向)
HTTP/3 + QUIC5-50ms★★★中程度★★★☆☆
WebTransport5-30ms★★★★中程度★★☆☆☆(実験段階)
Agora(マネージド)5-100ms★☆☆非常に高い
LiveKit(OSS)5-100ms★★☆非常に高い(自運用)

注:遅延は典型値で、ネットワーク状況に大きく依存。実装難度は「言語とライブラリの充実度」「学習コスト」を加味。

WebSocket

TCP上の双方向通信プロトコル。最も標準的で実装例が豊富。

特徴

  • ハンドシェイク後はコネクションを持続
  • テキスト・バイナリ両対応
  • ブラウザサポートほぼ100%

メリット

  • 実装がシンプル(ライブラリ充実)
  • レイテンシが安定している
  • デバッグ・監視ツールが豊富

デメリット

  • スケールアウト時、サーバ間の状態同期が複雑
  • 複数サーバ運用では「セッションアフィニティ」が必須
  • ロードバランサの設定が煩雑

実装例

サーバー(Node.js + ws):

import WebSocket from 'ws';

const wss = new WebSocket.Server({ port: 8080 });

wss.on('connection', (ws) => {
  console.log('Client connected');

  ws.on('message', (message) => {
    console.log('Received:', message);

    // 全クライアントにブロードキャスト
    wss.clients.forEach(client => {
      if (client.readyState === WebSocket.OPEN) {
        client.send(message);
      }
    });
  });

  ws.on('close', () => {
    console.log('Client disconnected');
  });
});

クライアント(JavaScript):

const ws = new WebSocket('ws://localhost:8080');

ws.onopen = () => {
  console.log('Connected');
  ws.send(JSON.stringify({ type: 'position', x: 100, y: 200 }));
};

ws.onmessage = (event) => {
  const data = JSON.parse(event.data);
  console.log('Received:', data);
  // ゲーム画面の他プレイヤー位置を更新など
};

ws.onerror = (error) => {
  console.error('WebSocket error:', error);
};

ユースケース

チャット、ボード共編、ゲームの初期段階、リアルタイム通知。


WebRTC Data Channel

P2P通信を実現。ネットワークの相手と直接やり取りできるのが最大の特徴。

特徴

  • UDP/SCTP上のデータ通信
  • P2P接続(シグナリングサーバが別途必須)
  • 複数クライアント対応(メッシュまたはスター型)

メリット

  • P2P時、サーバ経由しないので遅延が極小
  • NAT越えが標準で対応(STUN/TURN)
  • クライアント数が増えてもサーバ負荷が増えない

デメリット

  • 実装の学習コストが高い
  • シグナリングサーバの設計が複雑
  • 複数人通信のメッシュ管理が煩雑

実装例

シグナリングサーバ(Node.js + Socket.IO):

import express from 'express';
import { Server } from 'socket.io';

const app = express();
const io = new Server(8080);

io.on('connection', (socket) => {
  socket.on('offer', (data) => {
    // AからのofferをBへ中継
    socket.broadcast.emit('offer', {
      from: socket.id,
      offer: data.offer
    });
  });

  socket.on('answer', (data) => {
    // BからのanswerをAへ送信
    io.to(data.to).emit('answer', {
      from: socket.id,
      answer: data.answer
    });
  });

  socket.on('ice-candidate', (data) => {
    // ICE candidateを相手に中継
    io.to(data.to).emit('ice-candidate', {
      from: socket.id,
      candidate: data.candidate
    });
  });
});

クライアント側(シンプル版):

const socket = io('http://localhost:8080');
let peerConnection;

socket.on('offer', async (data) => {
  peerConnection = new RTCPeerConnection();

  // データチャネルを受け取る
  peerConnection.ondatachannel = (event) => {
    const dc = event.channel;
    dc.onmessage = (e) => {
      console.log('受信:', e.data);
    };
  };

  await peerConnection.setRemoteDescription(
    new RTCSessionDescription(data.offer)
  );
  const answer = await peerConnection.createAnswer();
  await peerConnection.setLocalDescription(answer);

  socket.emit('answer', { to: data.from, answer });
});

// 自分からオファーを送信する場合
async function initiateConnection() {
  peerConnection = new RTCPeerConnection();
  const dc = peerConnection.createDataChannel('game-sync');

  dc.onopen = () => {
    dc.send(JSON.stringify({ type: 'init', playerId: 'me' }));
  };

  const offer = await peerConnection.createOffer();
  await peerConnection.setLocalDescription(offer);
  socket.emit('offer', { offer });
}

ユースケース

1対1マッチゲーム、P2Pファイル共有、複数人での低遅延データ交換。


SSE + HTTP/2

サーバからクライアントへの一方向ストリーム。双方向が不要な用途で最高にシンプル。

特徴

  • サーバ→クライアント一方向
  • HTTP/2で複数接続数制限を回避
  • テキストストリーム形式

メリット

  • 実装が超シンプル(EventSource使うだけ)
  • ファイアウォール・プロキシの互換性が高い
  • ブラウザサポート優秀

デメリット

  • 一方向のみ(クライアント→サーバはHTTP通常リクエスト)
  • サーバ・クライアント間でポーリング送受信する場合は複雑化

実装例

サーバー(Node.js + Express):

import express from 'express';

const app = express();
const clients = new Set();

app.get('/stream', (req, res) => {
  res.setHeader('Content-Type', 'text/event-stream');
  res.setHeader('Cache-Control', 'no-cache');
  res.setHeader('Connection', 'keep-alive');

  clients.add(res);

  res.on('close', () => {
    clients.delete(res);
  });
});

// ブロードキャスト関数
function broadcast(data) {
  clients.forEach(res => {
    res.write(`data: ${JSON.stringify(data)}\n\n`);
  });
}

app.post('/action', (req, res) => {
  const action = req.body;
  broadcast(action);
  res.sendStatus(200);
});

app.listen(8080);

クライアント(JavaScript):

const eventSource = new EventSource('/stream');

eventSource.onmessage = (event) => {
  const data = JSON.parse(event.data);
  console.log('ブロードキャスト:', data);
  // 画面更新など
};

eventSource.onerror = () => {
  console.error('Connection lost');
};

// クライアント側からのアクション送信(通常のHTTP)
async function sendAction(action) {
  await fetch('/action', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(action)
  });
}

ユースケース

株価・仮想通貨ティッカー、ライブスコアボード、通知システム、リーダーボード。


HTTP/3 + QUIC

UDP上のQUICプロトコル。次世代HTTPとして注目。

特徴

  • UDPベース(TCPじゃない)
  • HoLブロッキング(Head-of-Line blocking)を回避
  • 複数ストリーム並行可
  • 0-RTT接続再開

ブラウザサポート

Chrome/Edge: ✓ Safari: ✓(最近対応) Firefox: 実験段階 全体採用率: 約26%(2024年時点)

メリット

  • TCPより低遅延
  • ネットワーク切り替え時(WiFi→LTE)の耐性

デメリット

  • サーバ実装の標準化がまだ途上
  • ファイアウォール・プロキシでブロックされることあり
  • デバッグが複雑

トーン

HTTP/3対応のCDNやエッジサーバを使えば自動で恩恵を受けられる。Cloudflareなどは既に標準提供。ただし、WebSocketで十分な場面では無理に HTTP/3 に切り替える必要はない。


WebTransport(注目の新技術)

現時点ではプロダクション採用を推奨しません。実験段階です。

QUICとHTTP/3をベースに、ストリーム(信頼性重視)とデータグラム(速度重視)を柔軟に選択できる次世代標準。

特徴

  • ストリーム:信頼性あり(WebSocketの後継候補)
  • データグラム:信頼性なし、超低遅延(ゲーム向け)
  • ブラウザネイティブAPI

ブラウザサポート

Chrome 86+のみ。Safariは実装予定。Firefoxは未定。

メリット

  • 最新最高のレイテンシ性能
  • ユースケース別の選択肢

デメリット

  • ブラウザサポート限定的
  • 実装例・解説記事が少ない
  • 仕様がまだ流動的

参考実装

// Chrome実験段階
async function connectWebTransport() {
  try {
    const transport = await WebTransport.connect(
      'https://example.com:4433/game'
    );

    // ストリームベース(信頼性重視)
    const writer = transport.datagrams.writable.getWriter();
    writer.write(new Uint8Array([1, 2, 3]));

    // データグラムベース(速度重視)
    transport.incomingUnidirectionalStreams.getReader().read()
      .then(({ value }) => {
        const reader = value.getReader();
        reader.read().then(({ value, done }) => {
          console.log('受信:', new TextDecoder().decode(value));
        });
      });
  } catch (e) {
    console.error('WebTransport error:', e);
  }
}

マネージド型プラットフォーム(Agora の例)

インフラ管理を完全に外部に委譲。スケーリングの心配がない。

特徴

  • SaaS型、インフラ管理不要
  • グローバルエッジサーバネットワーク
  • 標準化されたAPI

メリット

  • 運用・スケーリングの手間がゼロ
  • グローバル配信時の遅延最小化
  • サポート充実

デメリット

  • ベンダーロックイン
  • カスタマイズの制限
  • 月額費用発生

コード例

import AgoraRTC from "agora-rtc-sdk-ng";

const agoraEngine = AgoraRTC.createClient({
  mode: "rtc",
  codec: "vp8"
});

agoraEngine.on("user-published", async (user, mediaType) => {
  await agoraEngine.subscribe(user, mediaType);
});

agoraEngine.on("user-unpublished", async (user) => {
  await agoraEngine.unsubscribe(user);
});

// 参加
await agoraEngine.join(appId, channelName, token, uid);

オープンソース型(LiveKit の例)

自前運用で完全コントロール。カスタマイズ自由度が高い。

特徴

  • GitHub公開
  • Kubernetes対応
  • 自前インフラで展開可能

メリット

  • カスタマイズ自由度高い
  • ロックインなし
  • 大規模運用時のコスト削減可能

デメリット

  • インフラ構築・運用コストが必要
  • トラブル対応は自力

導入例

import {
  connect,
  Room,
  RoomOptions
} from 'livekit-client';

const url = 'ws://your-livekit-server:7880';
const token = await generateToken(userName, roomName);

const room = new Room();
await room.connect(url, token);

room.on('participantConnected', (participant) => {
  console.log('参加:', participant.name);
});

ユースケース別ガイド

リアルタイムゲーム

要件:遅延50ms以下、複数プレイヤー(2~100人)、双方向

推奨技術

  1. WebSocket(標準選択肢)
  2. WebRTC Data Channel(P2P時)

実装ポイント

  • プレイヤー位置データはサーバ中継(全員に見せるため)
  • 入力は即座に送信(予測入力も併用)
  • フレームレート同期(60fps)とサーバチック同期を分離

マルチプレイヤーホワイトボード

要件:遅延100ms以下、複数ユーザー(2~20人)、同期精度中程度

推奨技術

  1. WebSocket
  2. SSE(背景変更など一方向部分)

実装ポイント

  • ドローイングストロークはまとめて送信(毎フレーム送信は重い)
  • CRDT(Conflict-free Replicated Data Type)で競合解決(複雑化するので検討段階)

ライブコラボレーション(Figma風)

要件:遅延50ms以下、多数同時接続(10~100人)、Operational Transformation必須

推奨技術

  1. WebSocket(OT実装サーバ)
  2. マネージド型(YjsやPartyKit統合)

実装ポイント

  • 各ユーザーの操作をサーバで正規化(Operational Transformation)
  • クライアント側でも楽観的更新で遅延感を消す

チャット・メッセージング

要件:遅延500ms程度で許容、ユーザー数多数、一度配信したら履歴は不要

推奨技術

  1. WebSocket(リアルタイム)
  2. HTTP polling(フォールバック)

実装ポイント

  • メッセージ履歴はDB保存し、接続時のみ取得
  • 接続喪失時は再接続で新規メッセージ取得

株価・仮想通貨ティッカー

要件:遅延100~500ms、ブロードキャスト(一方向)、多数のクライアント接続

推奨技術

  1. SSE + HTTP/2(シンプル、スケール優秀)
  2. WebSocket(カスタマイズ必要な場合)

実装ポイント

  • 更新があるたびにSSEでプッシュ
  • クライアント側ではローカルストレージに最新値をキャッシュ

リアルタイム音声・ビデオ

要件:遅延10~100ms(コデック依存)、複数人同時接続、高スループット

推奨技術

  1. WebRTC(標準、P2P可能)
  2. マネージド型(Agora、Twilio)

実装ポイント

  • オーディオコーデック(opus)とビデオコーデック(VP8/H.264)の選定
  • ネットワーク負荷に応じた品質調整(解像度・フレームレート低下)

YouTube Live風コメントシステム

要件:遅延数秒OK、配信者と視聴者が時刻同期

推奨技術

  1. WebSocket + 時刻同期(疑似同期記事の仕組みを活用)
  2. SSE(一方向配信部分)

実装ポイント

  • コメント配信とビデオ再生の同期には、疑似同期記事で紹介したタイムシートを組み合わせ
  • サーバ時刻取得でクライアント間の時刻ズレを補正

技術選択フロー

START

【Q1】遅延は50ms以下である必要があるか?
  ├─ YES → Q2へ
  └─ NO → Q3へ

【Q2】クライアント間の直接通信(P2P)が必須か?
  ├─ YES → WebRTC Data Channel を検討
  └─ NO → WebSocket or HTTP/3 を検討

【Q3】サーバ管理の手間を最小化したいか?
  ├─ YES → Agora などマネージド型を検討
  └─ NO(自運用OK) → Q4へ

【Q4】ブロードキャスト(一方向)で十分か?
  ├─ YES → SSE + HTTP/2 を検討
  └─ NO(双方向必須) → WebSocket へ

採用実績・参考リソース

WebSocket:Discord, Slack(実装のコア)、Socket.IO(npm 週間DL 1.5M+)

WebRTC:Google Meet, Zoom(ビデオ/音声)、対戦ゲーム各種

SSE:Twitter X(リアルタイムフィード部分)

Agora:ウェビナープラットフォーム、ライブコマース

LiveKit:ビデオチャットプラットフォーム、エンタープライズ向け


まとめ

疑似同期(大規模、一方向)と真同期(小規模、双方向)は設計思想が根本的に違う。

選択基準:

  1. 遅延要件が50ms以下なら WebSocket か WebRTC Data Channel
  2. P2P必須なら WebRTC Data Channel
  3. ブロードキャストだけなら SSE + HTTP/2
  4. 運用コストを最小化したいなら Agora などマネージド型
  5. カスタマイズ重視なら WebSocket か LiveKit

複合利用パターンも多い。例えば、YouTube Live風なら「SSE でビデオ配信」「WebSocket でコメント」「サーバ時刻同期で演出タイミング」と、複数技術を組み合わせるのが現実的。

前回の疑似同期記事で「大規模イベント」のスケーリング方法を解説した。今回は「小規模だけど低遅延」の実装パターンをカバーした。自分の要件に合わせて、両者を使い分けられるといい。