このレッスンのロックを解除するには、まず「イベント配信とREST APIとの統合」を完了してください。前のレッスンに戻る

はじめに

Socket.IOはWebSocketプロトコルの上に構築されたライブラリで、純粋なWebSocketにはない開発者向けの機能を追加します。チャットアプリ、共同編集ツール、ライブダッシュボードなどのリアルタイム機能を構築する場合、クライアントを論理的にグループ化し、通信チャネルを分離し、切断された接続を適切に処理する必要があります。Socket.IOはルーム・名前空間・自動再接続・トランスポートフォールバックを標準で提供するため、これらをゼロから構築する必要はありません。

このレッスンでは、これら4つの機能を具体的なコードで順に学びます。レッスンを終える頃には、サーバーを名前空間に分割し、ソケットを動的にルームにグループ化し、クライアントに自動再接続を許可し、WebSocketがブロックされた場合の安全網としてHTTP long-pollingを利用する方法が理解できます。

名前空間

名前空間は、同じ基盤となるTCP接続を共有する独立した通信チャネルです。デフォルトの名前空間は`/`ですが、カスタム名前空間を作成してロジック上の関心を分離できます。例えば、公開チャットと管理者用コントロールパネルを分ける場合などです。

サーバー側:

javascript
const io = require('socket.io')(3000);
const adminNamespace = io.of('/admin');
adminNamespace.on('connection', (socket) => {
console.log('Admin connected:', socket.id);
});

クライアント側:

javascript
const adminSocket = io('/admin');
adminSocket.emit('admin-login', { user: 'alice' });

`/admin`名前空間を通じて送信された内容は、同じサーバーとポートで動作していても、デフォルトの`/`名前空間からは完全に分離されます。

ルーム

ルームは名前空間内のサブチャネルです。ソケットはいつでもルームに参加したり退出したりでき、サーバーは1回の呼び出しでルーム内のすべてのソケットにブロードキャストできます。これは、チャットアプリでの会話ごとのメッセージングや、マルチプレイヤーゲームでのゲームルームごとのステート管理に適したパターンです。

javascript
io.on('connection', (socket) => {
socket.on('join-room', (roomId) => {
socket.join(roomId);
socket.to(roomId).emit('user-joined', socket.id);
});
socket.on('chat-message', ({ roomId, message }) => {
io.to(roomId).emit('chat-message', { from: socket.id, message });
});
socket.on('disconnect', () => {
console.log('Socket left:', socket.id);
});
});

`socket.join(roomId)`はソケットをSocket.IOが管理するインメモリのセットに追加します。そして`io.to(roomId).emit(...)`がそのルームの全メンバーにペイロードを配信します。参加したソケット自身は`socket.to(...)`で送信されたメッセージを受信しません。「他の全員」に通知したい場合に`socket.to(...)`を使用してください。

1つの名前空間に3つのルームがあり、各ルームに複数のクライアントが所属し、サーバーから各ルームへブロードキャストする矢印が描かれた図

自動再接続とトランスポートフォールバック

純粋なWebSocketは、ネットワークが切断した瞬間に永続的に動作を停止します。Socket.IOのクライアントは再接続を自動的に処理します。再試行戦略を設定し、イベントを通じてユーザーに状況を伝えることができます。

javascript
const socket = io({
reconnection: true,
reconnectionAttempts: 10,
reconnectionDelay: 1000,
reconnectionDelayMax: 5000,
});
socket.on('reconnect_attempt', (attempt) => {
console.log('Trying to reconnect, attempt', attempt);
});
socket.on('reconnect', (attemptNumber) => {
console.log('Reconnected after', attemptNumber, 'tries');
});
socket.on('connect_error', (err) => {
console.log('Connection failed:', err.message);
});

トランスポートフォールバックはレジリエンスのもう半分を担います。Socket.IOは最も低レイテンシなオプションであるため常にWebSocketを最初に試行しますが、ハンドシェイクが失敗した場合(企業ファイアウォール、古いプロキシ、制限のあるネットワーク)は、HTTP long-pollingに透過的にフォールバックします。アプリケーションコードは変わりません。`emit`と`on`の呼び出しはどちらのトランスポート上でも同じように動作します。

よくある落とし穴とまとめ

よく見られる2つの間違いがあります。第一に、ルームメンバーシップを独自の変数に保存しないでください。Socket.IOが内部的にjoinとleaveを管理するため、必ずSocket.IOが返す`socket.rooms`を信頼してください。さもないとキャッシュがずれていきます。第二に、`socket.to(roomId)`は送信者を除外し、`io.to(roomId)`は送信者を含めるため、ブロードキャストの意図に応じて適切な方を選んでください。

まとめると、名前空間はサーバーを分離されたチャネルに分割し、ルームは名前空間内でソケットをグループ化してターゲットを絞ったブロードキャストを可能にし、自動再接続はコードの変更なしにネットワーク切断から復旧し、トランスポートフォールバックはWebSocketがブロックされた場合でも接続を維持します。これら4つの機能が組み合わさることで、純粋なWebSocketの上に書くことになる定型コードを一掃できます。

レッスンの練習

レッスンのチェックポイント

学んだ内容を復習し、回答へのフィードバックを受けましょう。

0/1
回答済み · 0/1
Socket.IOのルーム・名前空間・自動再接続の活用