Socket.io 聊天室

619 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第1天,点击查看活动详情

Demo 截图:

Demo 源码地址:github.com/goblin-labo…

文档地址:socket.io/docs/v4/

概念

  1. 定义

Socket.IO 是一个可以在客户端和服务器之间实现低延迟、双向和基于事件的通信库。

基于事件体现在以下几点:

  • Server/Client 端发送消息使用 emit 接口
  • 接收消息通过 on 接口进行订阅
  • 客户端如果不订阅消息,Server 端即使调用了 emit 接口发送消息,实际是不会产生消息发送的
  1. 特性

它建立在WebSocket协议之上,并提供额外的保证,例如回退到 HTTP 长轮询或自动重新连接。

  • 默认使用 WebSocket 进行传输,如果客户端不支持,自动回退到 HTTP 长轮询
  • 默认包含 ping/pong 机制,定期检查连接状态
  • 自动重新连接,连接断开后自动重连,重连期间的消息会被缓存起来,并在重新连接后发送
  • 支持广播,msg-sync 中是自己去遍历 socket 实现的
  1. 注意事项

socket.io/docs/v4/#wh…

Socket.IO 不是 WebSocket

Socket.IO 不是 WebSocket

Socket.IO 不是 WebSocket

尽管 Socket.IO 确实在可能的情况下使用 WebSocket 进行传输,但它为每个数据包添加了额外的元数据。这就是为什么 WebSocket 客户端将无法成功连接到 Socket.IO 服务器,而 Socket.IO 客户端也将无法连接到普通 WebSocket 服务器

  1. 快速开始

socket.io/get-started…

Server 端

下面的代码就能启动一个 socket.io server,客户端可以连接对应的 3000 端口

const express = require('express');
const app = express();
const http = require('http');
const server = http.createServer(app);
const { Server } = require("socket.io");
const io = new Server(server);

app.get('/', (req, res) => {
  res.sendFile(__dirname + '/index.html');
});

io.on('connection', (socket) => {
  console.log('a user connected');
});

server.listen(3000, () => {
  console.log('listening on *:3000');
});

跨域支持

socket.io/docs/v4/han…

const io = new Server(httpServer, {
  /* options */
  cors: {
    origin: "*",
  },
});

Server 实例 connection 事件

socket.io/docs/v4/ser…

当有新的 client 连上了时触发,事件回调函数的第一个参数是 socket 实例,后续使用这个 socket 实例和这个 client 进行通信。

目前都是使用单播的方式实现的,所以这个需要缓存,如果使用广播和组播的方案这个 socket 是不是就不用缓存了?

Client 端

引入

pnpm add socket.io-client
import io from "socket.io-client";

const socket = io("http://192.168.16.119:3001/");
socket.on("connect", () => {
  console.log("socket.io connected");
  console.log(socket.id);
  console.log(socket.connected);
});
socket.on("disconnect", () => {
  console.log("socket.io disconnect");
  console.log(socket.connected);
});
socket.on("connect_error", (error) => {
  console.log("socket.io error");
  console.log(socket.connected);
});

客户端的 socket 实例和 Server 端的 socket 实例 id 一致,两个实例间通过 emit 接口发送消息,通过 on 接口订阅消息。

socket 连接相关的事件有 connectdisconnectconnect_error

socket.io/docs/v4/cli…

Client 发送消息给 Server

Client 端代码:

const msg = { /* 消息内容 */ };
socket.emit("message", JSON.stringify(msg), (arg) => {
  console.log(arg); // got it
});;

Server 端代码:

socket.on("message", (arg, callback) => {
  const msg = JSON.parse(arg);
  callback("got it");
});

上面的例子中,Client 端发送的内容在 Server 端 socket 实例 message 事件回调函数中获取,同时回调函数中 callback 的内容在 Client 发送的回调函数中能够获取到。

Server 发送消息给 Client

Server 端代码:

const stringifed = JSON.stringify(msgInfo);
this.list.forEach((it) => {
  it.emit("message", stringifed);
});

Client 端代码:

socket.on("message", , (arg) => {
  console.log(arg);
})

Server 端发消息时需要通知所有的 Client,目前的实现方式是将所有连接的 socket 实例缓存下来,需要发送的时候遍历进行单播,也可以使用广播和组播的方式进行实现。

超时异常检测

上面都是默认 emit 接口可以成功,异常检测的处理方法如下

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