3.x から 4.0 への移行
4.0.0 リリースでは、下記に詳述されている多くの新機能が追加されましたが、いくつかのAPIの破壊的変更も含まれています(そのためメジャーバージョンアップとなっています)。
これらの破壊的変更は、サーバー側のAPIのみに影響することにご注意ください。Socket.IOプロトコル自体は更新されていないため、v3クライアントはv4サーバーに接続でき、その逆も可能です。さらに、互換性モード(allowEIO3: true
)は、Socket.IO v2クライアントとSocket.IO v4サーバー間でも引き続き利用できます。
変更点の完全なリストを以下に示します。
破壊的変更
io.to()
は immutable になりました
以前は、特定のルームへのブロードキャスト(io.to()
を呼び出すことによって)は io インスタンスを変更していました。これにより、次のような予期しない動作が発生する可能性がありました。
io.to("room1");
io.to("room2").emit(/* ... */); // also sent to room1
// or with async/await
io.to("room3").emit("details", await fetchDetails()); // random behavior: maybe in room3, maybe to all clients
io.to()
(またはその他のブロードキャスト修飾子)を呼び出すと、immutable なインスタンスが返されるようになります。
サンプル
const operator1 = io.to("room1");
const operator2 = operator1.to("room2");
const operator3 = socket.broadcast;
const operator4 = socket.to("room3").to("room4");
operator1.emit(/* ... */); // only to clients in "room1"
operator2.emit(/* ... */); // to clients in "room1" or in "room2"
operator3.emit(/* ... */); // to all clients but the sender
operator4.emit(/* ... */); // to clients in "room3" or in "room4" but the sender
wsEngine
オプション
wsEngine
オプションのフォーマットが更新され、次のエラーが解消されました。
クリティカルな依存関係:依存関係のリクエストが式です
webpack を使用してサーバーをバンドルする場合。
変更前
const io = require("socket.io")(httpServer, {
wsEngine: "eiows"
});
変更後
const io = require("socket.io")(httpServer, {
wsEngine: require("eiows").Server
});
設定
Swift v15 クライアントとの互換性の確保
バージョン 16.0.0 より前では、Swift クライアントは HTTP リクエストに EIO
クエリパラメーターを含めておらず、Socket.IO v3 サーバーはデフォルトで EIO=4
を推測していました。
そのため、互換性モードが有効になっている場合でも(allowEIO3: true
)、クエリパラメーターを明示的に指定しない限り、Swift クライアント v15 はサーバーに接続できませんでした。
let manager = SocketManager(socketURL: URL(string: "http://localhost:8080")!, config: [
.log(true),
.connectParams(["EIO": "3"])
])
let socket = manager.defaultSocket
Socket.IO v4 サーバーは、EIO
クエリパラメーターが含まれていない場合、EIO=3
を推測するようになりました。
pingTimeout
のデフォルト値が増加しました
pingTimeout
(ハートビートメカニズムで使用)のデフォルト値は、socket.io@2.1.0
(2018年3月)で 60000 から 5000 に更新されました。
当時の理由
一部のユーザーは、サーバー側とクライアント側の切断間に長い遅延が発生していました。「disconnect」イベントがブラウザで発火するのに長い時間がかかることがありました(おそらくタイマーが遅延しているためです)。そのため変更されました。
とはいえ、現在の値(5秒)は、大きなペイロードが低速なネットワーク経由で送信された場合に予期しない切断を引き起こしました。これは、クライアントとサーバー間で ping-pong パケットの交換が行われるのを妨げるためです。これは、同期タスクがサーバーを5秒以上ブロックする場合にも発生する可能性があります。
したがって、新しい値(20秒)は、迅速な切断検出とさまざまな遅延に対する許容度のバランスが良いと考えられます。
新機能
ブロードキャスト時に特定のルームを除外できるようにしました
Sebastiaan Marynissen さんの素晴らしい作業のおかげで、ブロードキャスト時に特定のルームを除外できるようになりました。
io.except("room1").emit(/* ... */); // to all clients except the ones in "room1"
io.to("room2").except("room3").emit(/* ... */); // to all clients in "room2" except the ones in "room3"
socket.broadcast.except("room1").emit(/* ... */); // to all clients except the ones in "room1" and the sender
socket.except("room1").emit(/* ... */); // same as above
socket.to("room4").except("room5").emit(/* ... */); // to all clients in "room4" except the ones in "room5" and the sender
io.to()
に配列を渡せるようにしました
to()
メソッドは、ルームの配列を受け入れるようになりました。
変更前
const rooms = ["room1", "room2", "room3"];
for (const room of rooms) {
io.to(room);
}
// broadcast to clients in "room1", "room2" or "room3"
// WARNING !!! this does not work anymore in v4, see the breaking change above
io.emit(/* ... */);
変更後
io.to(["room1", "room2", "room3"]).emit(/* ... */);
socket.to(["room1", "room2", "room3"]).emit(/* ... */);
追加のユーティリティメソッド
いくつかの(待望の)メソッドが追加されました。
socketsJoin
:一致するソケットインスタンスに指定されたルームへの参加をさせます。
// make all Socket instances join the "room1" room
io.socketsJoin("room1");
// make all Socket instances of the "admin" namespace in the "room1" room join the "room2" room
io.of("/admin").in("room1").socketsJoin("room2");
socketsLeave
:一致するソケットインスタンスに指定されたルームからの退出をさせます。
// make all Socket instances leave the "room1" room
io.socketsLeave("room1");
// make all Socket instances of the "admin" namespace in the "room1" room leave the "room2" room
io.of("/admin").in("room1").socketsLeave("room2");
disconnectSockets
:一致するソケットインスタンスを切断します。
// make all Socket instances disconnect
io.disconnectSockets();
// make all Socket instances of the "admin" namespace in the "room1" room disconnect
io.of("/admin").in("room1").disconnectSockets();
// this also works with a single socket ID
io.of("/admin").in(theSocketId).disconnectSockets();
fetchSockets
:一致するソケットインスタンスを返します。
// return all Socket instances of the main namespace
const sockets = await io.fetchSockets();
// return all Socket instances of the "admin" namespace in the "room1" room
const sockets = await io.of("/admin").in("room1").fetchSockets();
// this also works with a single socket ID
const sockets = await io.in(theSocketId).fetchSockets();
上記の例における sockets
変数は、通常の Socket クラスのサブセットを公開するオブジェクトの配列です。
for (const socket of sockets) {
console.log(socket.id);
console.log(socket.handshake);
console.log(socket.rooms);
socket.emit(/* ... */);
socket.join(/* ... */);
socket.leave(/* ... */);
socket.disconnect(/* ... */);
}
これらのメソッドはブロードキャストと同じセマンティクスを共有し、同じフィルターが適用されます。
io.of("/admin").in("room1").except("room2").local.disconnectSockets();
"admin" 名前空間のすべての Socket インスタンス
- "room1" ルーム内(
in("room1")
またはto("room1")
) - "room2" 内のものを除く(
except("room2")
) - 現在の Socket.IO サーバーのみ(
local
)で
切断します。
型付きイベント
Maxime Kjaer さんの素晴らしい作業のおかげで、TypeScript ユーザーはクライアントとサーバー間で送受信されるイベントの型を指定できるようになりました。
まず、各イベントのシグネチャを宣言します。
interface ClientToServerEvents {
noArg: () => void;
basicEmit: (a: number, b: string, c: number[]) => void;
}
interface ServerToClientEvents {
withAck: (d: string, cb: (e: number) => void) => void;
}
そして、クライアント側で使用できるようになりました。
import { io, Socket } from "socket.io-client";
const socket: Socket<ServerToClientEvents, ClientToServerEvents> = io();
socket.emit("noArg");
socket.emit("basicEmit", 1, "2", [3]);
socket.on("withAck", (d, cb) => {
cb(4);
});
IDE は各引数の型を適切に推論するようになりました。
同様にサーバー側でも(ServerToClientEvents
と ClientToServerEvents
は逆になります)。
import { Server } from "socket.io";
const io = new Server<ClientToServerEvents, ServerToClientEvents>(3000);
io.on("connection", (socket) => {
socket.on("noArg", () => {
// ...
});
socket.on("basicEmit", (a, b, c) => {
// ...
});
socket.emit("withAck", "42", (e) => {
console.log(e);
});
});
デフォルトでは、イベントは型付けされておらず、引数は any
として推論されます。
autoUnref
オプション
KC Erb さんの素晴らしい作業のおかげで、autoUnref
オプションが追加されました。
autoUnref
を true に設定すると(デフォルト: false)、Socket.IO クライアントは、イベントシステムに他のアクティブなタイマー/TCPソケットがない場合(クライアントが接続されている場合でも)、プログラムの終了を許可します。
const socket = io({
autoUnref: true
});
注:このオプションは Node.js クライアントのみに適用されます。
既知の移行上の問題
undefined の emit を取得できません
次の式
socket.to("room1").broadcast.emit(/* ... */);
は Socket.IO v3 では機能していましたが、現在は無効と見なされます。これは、to("room1")
メソッドが既に Socket インスタンスをブロードキャストモードにしているため、broadcast
フラグが無意味であるためです。
// VALID
socket.broadcast.emit(/* ... */); // to all clients but the sender
socket.to("room1").emit(/* ... */); // to clients in "room1" but the sender
// VALID (but useless 'broadcast' flag)
socket.broadcast.to("room1").emit(/* ... */);
// INVALID
socket.to("room1").broadcast.emit(/* ... */);