セットアップ
サーバーセットアップ(Node.js)
import { Server } from "socket.io"; const io = new Server(3000, { cors: { origin: "http://localhost:5173" } });
クライアントセットアップ
import { io } from "socket.io-client"; const socket = io("http://localhost:3000");
Express との組み合わせ
import express from "express"; import { createServer } from "http"; import { Server } from "socket.io"; const app = express(); const server = createServer(app); const io = new Server(server);
サーバーオプション
corsクロスオリジン用の CORS 設定
pathカスタムパス(デフォルト: /socket.io)
pingIntervalハートビート間隔(ミリ秒、デフォルト 25000)
pingTimeout切断までのタイムアウト(デフォルト 20000)
maxHttpBufferSize最大メッセージサイズ(バイト、デフォルト 1MB)
イベント
組み込みイベント(サーバー)
connectionクライアントが接続した
disconnectクライアントが切断した
disconnectingクライアントが切断中(まだルームに参加中)
errorエラーイベント
組み込みイベント(クライアント)
connectサーバーに接続した
disconnectサーバーから切断した
connect_error接続に失敗した
reconnect再接続に成功した
reconnect_attempt再接続を試みている
接続のライフサイクル
io.on("connection", (socket) => { console.log(`connected: ${socket.id}`); socket.on("disconnect", (reason) => { console.log(`disconnected: ${reason}`); }); });
送信
サーバーからの送信
socket.emit("hello", { msg: "world" }); socket.emit("data", arg1, arg2); io.emit("broadcast", data);
クライアントからの送信
socket.emit("chat:message", { text }); socket.emit("update", data, (res) => { console.log("ack:", res); });
送信パターン
socket.emit(ev, data)このソケットのみに送信
io.emit(ev, data)接続中のすべてのクライアントに送信
socket.broadcast.emit()送信者以外の全クライアントに送信
io.to(room).emit()ルーム内のすべてのクライアントに送信
socket.to(room).emit()送信者以外のルームメンバーに送信
ブロードキャスト
ブロードキャストメソッド
io.emit("msg", data); socket.broadcast.emit("msg", data); io.to("room1").emit("msg", data); io.except("room2").emit("msg", data);
揮発性と圧縮
socket.volatile.emit()クライアントが準備できていない場合は破棄(バッファなし)
socket.compress(true).emit()メッセージごとの圧縮を有効化
io.local.emit()ローカルサーバーのみにブロードキャスト(マルチノード)
socket.timeout(5000).emit()応答確認タイムアウト付きで送信
ルーム
ルーム操作
socket.join("room-1"); socket.join(["room-1", "room-2"]); socket.leave("room-1"); io.to("room-1").emit("msg", data);
ルームのプロパティ
socket.roomsこのソケットが参加しているルームのセット
socket.id各ソケットは自分の ID のルームに自動参加
io.sockets.adapter.roomsすべてのルームとメンバーのマップ
ルームパターン
socket.on("join:room", (room) => { socket.join(room); io.to(room).emit("user:joined", socket.id); }); socket.on("disconnecting", () => { for (const room of socket.rooms) { socket.to(room).emit("user:left", socket.id); } });
ネームスペース
ネームスペースの作成
const chat = io.of("/chat"); const admin = io.of("/admin"); chat.on("connection", (socket) => { chat.emit("user:online", socket.id); });
ネームスペースへのクライアント接続
const chat = io("http://localhost:3000/chat"); const admin = io("http://localhost:3000/admin");
動的ネームスペース
io.of(/^\/project-\d+$/).on("connection", (socket) => { const ns = socket.nsp.name; console.log(`joined namespace: ${ns}`); } );
ミドルウェア
サーバーミドルウェア
io.use((socket, next) => { const token = socket.handshake.auth.token; if (isValid(token)) return next(); next(new Error("authentication failed")); });
ネームスペースミドルウェア
const admin = io.of("/admin"); admin.use((socket, next) => { if (socket.handshake.auth.role === "admin") return next(); next(new Error("not authorized")); });
ミドルウェアのプロパティ
socket.handshake.authクライアントから送信された認証データ
socket.handshake.headers初期リクエストの HTTP ヘッダー
socket.handshake.query接続 URL のクエリパラメータ
socket.dataミドルウェアで付与した任意データ
エラーハンドリング
サーバー側のエラー
socket.on("action", (data, callback) => { try { const result = process(data); callback({ status: "ok", data: result }); } catch (err) { callback({ status: "error", msg: err.message }); } });
クライアント側のエラー
socket.on("connect_error", (err) => { console.log("connection error:", err.message); }); socket.io.on("reconnect_failed", () => { console.log("reconnection failed"); });
クライアントの再接続オプション
reconnection自動再接続を有効化(デフォルト true)
reconnectionAttempts最大試行回数(デフォルト Infinity)
reconnectionDelay初回遅延ミリ秒(デフォルト 1000)
reconnectionDelayMax最大遅延ミリ秒(デフォルト 5000)
確認応答
クライアント送信、サーバーが確認
// クライアント socket.emit("save", data, (response) => { console.log("server ack:", response); }); // サーバー socket.on("save", (data, callback) => { callback({ saved: true, id: 42 }); });
サーバー送信、クライアントが確認
// サーバー socket.emit("ping", (response) => { console.log("client ack:", response); }); // クライアント socket.on("ping", (callback) => { callback("pong"); });
タイムアウト付き
socket.timeout(5000).emit("save", data, (err, response) => { if (err) console.log("timeout!"); else console.log("ack:", response); } );
よく使うパターン
チャットルーム
io.on("connection", (socket) => { socket.on("chat:join", (room) => { socket.join(room); socket.to(room).emit("chat:joined", socket.id); }); socket.on("chat:message", ({ room, text }) => { io.to(room).emit("chat:message", { from: socket.id, text }); }); });
オンラインプレゼンス
const users = new Map(); io.on("connection", (socket) => { users.set(socket.id, socket.handshake.auth); io.emit("users:list", [...users.values()]); socket.on("disconnect", () => { users.delete(socket.id); io.emit("users:list", [...users.values()]); }); });
レートリミット
io.use((socket, next) => { const ip = socket.handshake.address; if (rateLimiter.consume(ip)) return next(); next(new Error("rate limit exceeded")); });