发送事件

有几种方法可以在服务器和客户端之间发送事件。

发送事件 - 图1提示

对于 TypeScript 用户,可以为事件提供类型提示。请查看这个.

基本的 emit

Socket.IO API 的灵感来自 Node.js EventEmitter,这意味着您可以在一侧发出事件并在另一侧注册侦听器:

服务器

  1. io.on("connection", (socket) => {
  2. socket.emit("hello", "world");
  3. });

客户端

  1. socket.on("hello", (arg) => {
  2. console.log(arg); // world
  3. });

这也适用于另一个方向:

服务器

  1. io.on("connection", (socket) => {
  2. socket.on("hello", (arg) => {
  3. console.log(arg); // world
  4. });
  5. });

客户端

  1. socket.emit("hello", "world");

您可以发送任意数量的参数,并且支持所有可序列化的数据结构,包括像BufferTypedArray这样的二进制对象。

服务器

  1. io.on("connection", (socket) => {
  2. socket.emit("hello", 1, "2", { 3: '4', 5: Buffer.from([6]) });
  3. });

客户端

  1. // client-side
  2. socket.on("hello", (arg1, arg2, arg3) => {
  3. console.log(arg1); // 1
  4. console.log(arg2); // "2"
  5. console.log(arg3); // { 3: '4', 5: ArrayBuffer (1) [ 6 ] }
  6. });

无需JSON.stringify(),因为它会为您完成。

  1. // BAD
  2. socket.emit("hello", JSON.stringify({ name: "John" }));
  3. // GOOD
  4. socket.emit("hello", { name: "John" });

笔记:

  • Date对象将被转换为(并作为)它们的字符串表示形式,例如1970-01-01T00:00:00.000Z

  • MapSet必须手动序列化:

  1. const serializedMap = [...myMap.entries()];
  2. const serializedSet = [...mySet.keys()];
  • 您可以使用该toJSON()方法自定义对象的序列化

一个类的例子:

  1. class Hero {
  2. #hp;
  3. constructor() {
  4. this.#hp = 42;
  5. }
  6. toJSON() {
  7. return { hp: this.#hp };
  8. }
  9. }
  10. socket.emit("here's a hero", new Hero());

回调

事件很棒,但在某些情况下,您可能需要更经典的请求-响应 API。在 Socket.IO 中,此功能称为确认。

您可以添加一个回调作为emit()的最后一个参数,一旦对方确认事件,就会调用此回调:

服务器

  1. io.on("connection", (socket) => {
  2. socket.on("update item", (arg1, arg2, callback) => {
  3. console.log(arg1); // 1
  4. console.log(arg2); // { name: "updated" }
  5. callback({
  6. status: "ok"
  7. });
  8. });
  9. });

客户端

  1. socket.emit("update item", "1", { name: "updated" }, (response) => {
  2. console.log(response.status); // ok
  3. });

超时

从 Socket.IO v4.4.0 开始,您现在可以为每个发射分配超时:

  1. socket.timeout(5000).emit("my-event", (err) => {
  2. if (err) {
  3. // the other side did not acknowledge the event in the given delay
  4. }
  5. });

You can also use both a timeout and an acknowledgement:

  1. socket.timeout(5000).emit("my-event", (err, response) => {
  2. if (err) {
  3. // the other side did not acknowledge the event in the given delay
  4. } else {
  5. console.log(response);
  6. }
  7. });

易失性事件

易失性事件是在底层连接未准备好时不会发送的事件(有点像UDP,在可靠性方面)。

例如,如果您需要发送在线游戏中角色的位置(因为只有最新的值才有用),这可能会很有趣。

  1. socket.volatile.emit("hello", "might or might not be received");

另一个用例是在客户端未连接时丢弃事件(默认情况下,事件会被缓冲直到重新连接)。

例子:

服务器

  1. io.on("connection", (socket) => {
  2. console.log("connect");
  3. socket.on("ping", (count) => {
  4. console.log(count);
  5. });
  6. });

客户端

  1. let count = 0;
  2. setInterval(() => {
  3. socket.volatile.emit("ping", ++count);
  4. }, 1000);

如果重新启动服务器,您将在控制台中看到:

  1. connect
  2. 1
  3. 2
  4. 3
  5. 4
  6. # the server is restarted, the client automatically reconnects
  7. connect
  8. 9
  9. 10
  10. 11

如果没有volatile标志,您将看到:

  1. connect
  2. 1
  3. 2
  4. 3
  5. 4
  6. # the server is restarted, the client automatically reconnects and sends its buffered events
  7. connect
  8. 5
  9. 6
  10. 7
  11. 8
  12. 9
  13. 10
  14. 11