Spec-Zone.ru › Socket.IO 4

Миграция с 3.x на 4.0

Выпуск 4.0.0 добавляет довольно много новых функций, которые подробно описаны ниже, но также содержит несколько изменений API, нарушающих совместимость (отсюда и значительная переработка).

Обратите внимание, что эти изменения, нарушающие совместимость, влияют только на API на стороне сервера. Протокол Socket.IO сам по себе не обновлялся, поэтому клиент v3 сможет подключиться к серверу v4 и наоборот. Кроме того, режим совместимости (allowEIO3: true) по-прежнему доступен между клиентом Socket.IO v2 и сервером Socket.IO v4.

Вот полный список изменений:

  • Изменения, нарушающие совместимость
    • io.to() теперь неизменяемый
    • wsEngine параметр
  • Конфигурация
    • Обеспечение совместимости с клиентами Swift v15
    • Значение по умолчанию параметра pingTimeout было увеличено
  • Новые функции
    • Возможность исключения определенных комнат при трансляции
    • Возможность передачи массива в io.to()
    • Дополнительные служебные методы
    • Типизированные события
    • autoUnref параметр
  • Известные проблемы миграции

Изменения, нарушающие совместимость

io.to() теперь неизменяемый

Ранее трансляция в заданную комнату (вызовом 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() (или любого другого модификатора трансляции) теперь возвращает неизменяемый экземпляр.

Примеры:

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 был обновлен для устранения следующей ошибки:

Critical dependency: the request of a dependency is an expression

при сборке сервера с помощью 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 не включал параметр запроса EIO, и сервер Socket.IO v3 по умолчанию выводил EIO=4.

Поэтому клиент Swift v15 не мог подключиться к серверу, даже при включенном режиме совместимости (allowEIO3: true), если явно не был указан параметр запроса:

let manager = SocketManager(socketURL: URL(string: "http://localhost:8080")!, config: [
  .log(true),
  .connectParams(["EIO": "3"])
])
let socket = manager.defaultSocket

Сервер Socket.IO v4 теперь будет выводить EIO=3, если параметр запроса EIO не указан.

Значение по умолчанию параметра pingTimeout было увеличено

Значение по умолчанию для pingTimeout (используемое в механизме определения отключения) было обновлено с 60000 до 5000 в socket.io@2.1.0 (март 2018 г.).

Тогда это объяснялось так:

Некоторые пользователи сталкивались с большими задержками между отключением на стороне сервера и на стороне клиента. Событие "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();

Что приводит к отключению всех экземпляров Socket с пространством имен "admin"

  • в комнате "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("connect", (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.

Известные проблемы миграции

  • cannot get emit of undefined

Следующее выражение:

socket.to("room1").broadcast.emit(/* ... */);

работало в Socket.IO v3, но теперь считается недопустимым, так как флаг broadcast бесполезен, поскольку метод to("room1") уже переводит экземпляр Socket в режим трансляции.

// 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(/* ... */);

© 2014–2021 Automattic
Licensed under the MIT License.
https://socket.io/docs/v4/migrating-from-3-x-to-4-0

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API