<CodeLearn/>
WebSocket レッスン2

Socket.IO入門

イベント駆動のリアルタイム通信ライブラリを使いこなそう

なぜSocket.IOを使うのか?

ネイティブWebSocket APIはシンプルですが、実際のアプリケーション開発では 多くの課題があります。Socket.IOはそれらを解決してくれます。

自動再接続

接続が切れた場合に自動的に再接続を試みる。ネイティブWSにはこの機能がない。

イベントベース

カスタムイベント名でデータを送受信できる。メッセージの種類を簡単に区別できる。

フォールバック

WebSocketが使えない環境ではHTTPロングポーリングに自動的にフォールバックする。

ルーム・名前空間

グループ通信やチャネル分離の仕組みが組み込まれている。

サーバーのセットアップ

socket.ioパッケージをインストールして、 Express と組み合わせてサーバーを構築します。

// npm install express socket.io
import express from "express";
import { createServer } from "http";
import { Server } from "socket.io";

const app = express();
const httpServer = createServer(app);
const io = new Server(httpServer, {
  cors: {
    origin: "http://localhost:3000",
    methods: ["GET", "POST"],
  },
});

io.on("connection", (socket) => {
  console.log("ユーザー接続:", socket.id);

  // クライアントからのイベントを受信
  socket.on("chat:message", (data) => {
    console.log("メッセージ:", data);
  });

  // 切断時
  socket.on("disconnect", (reason) => {
    console.log("ユーザー切断:", socket.id, reason);
  });
});

httpServer.listen(3001, () => {
  console.log("Socket.IOサーバー起動: http://localhost:3001");
});

クライアントのセットアップ

socket.io-clientパッケージを使って、 ブラウザからSocket.IOサーバーに接続します。

// npm install socket.io-client
import { io } from "socket.io-client";

// サーバーに接続
const socket = io("http://localhost:3001", {
  autoConnect: true,          // 自動接続
  reconnection: true,         // 自動再接続
  reconnectionAttempts: 5,    // 再接続試行回数
  reconnectionDelay: 1000,    // 再接続間隔(ミリ秒)
});

// 接続成功
socket.on("connect", () => {
  console.log("接続成功:", socket.id);
});

// 接続エラー
socket.on("connect_error", (error) => {
  console.error("接続エラー:", error.message);
});

// 切断
socket.on("disconnect", (reason) => {
  console.log("切断:", reason);
});

emit / on でイベント通信

Socket.IOの通信はイベント駆動です。emitでイベントを送信し、onでイベントを受信します。

// ===== クライアント側 =====

// イベントを送信(文字列)
socket.emit("chat:message", "こんにちは!");

// イベントを送信(オブジェクト)
socket.emit("chat:message", {
  user: "田中",
  text: "こんにちは!",
  timestamp: Date.now(),
});

// イベントを受信
socket.on("chat:message", (data) => {
  console.log(`${data.user}: ${data.text}`);
});

// ===== サーバー側 =====

io.on("connection", (socket) => {
  // 特定のクライアントからのイベント受信
  socket.on("chat:message", (data) => {
    console.log("受信:", data);

    // 送信元を含む全員に送信
    io.emit("chat:message", data);
  });
});

ブロードキャスト

メッセージの送信先にはいくつかのパターンがあります。 用途に応じて使い分けましょう。

io.on("connection", (socket) => {

  // 1. 全員に送信(送信者を含む)
  io.emit("notification", "全員へのお知らせ");

  // 2. 送信者以外の全員に送信
  socket.broadcast.emit("notification", "他の全員へ");

  // 3. 送信者のみに送信
  socket.emit("notification", "あなただけに");

  // 4. 特定のソケットIDに送信
  io.to(targetSocketId).emit("private", "個別メッセージ");

  // 5. 複数の宛先に送信
  io.to(socketId1).to(socketId2).emit("group", "グループメッセージ");
});

Acknowledgments(確認応答)

Acknowledgmentを使うと、 イベントの送信後にサーバーからの応答を受け取ることができます。 HTTPのリクエスト/レスポンスに似た挙動を実現できます。

// ===== クライアント側 =====

// コールバックで応答を受け取る
socket.emit("chat:message", { text: "Hello!" }, (response) => {
  if (response.status === "ok") {
    console.log("メッセージ送信成功:", response.id);
  } else {
    console.error("送信失敗:", response.error);
  }
});

// async/await スタイル(Socket.IO v4.6+)
const response = await socket.emitWithAck("chat:message", {
  text: "Hello!",
});
console.log("応答:", response);

// ===== サーバー側 =====

socket.on("chat:message", (data, callback) => {
  try {
    // メッセージを保存
    const id = saveMessage(data);
    // 成功を応答
    callback({ status: "ok", id });
  } catch (error) {
    // エラーを応答
    callback({ status: "error", error: error.message });
  }
});

まとめ

  • Socket.IOは自動再接続、フォールバック、ルーム機能などを提供するライブラリ
  • サーバーはExpress + socket.io、クライアントはsocket.io-clientで構築
  • emit/onのイベント駆動モデルで直感的にリアルタイム通信が書ける
  • ブロードキャストのパターンを使い分けて適切な宛先にデータを送る
  • Acknowledgmentで送信の成功/失敗を確認できる