灏天阁

EventSource VS WebSocket

· Yin灏

EventSource

定义

EventSource 也称为 SSE(Server-Sent Events),是服务器推送的一个网络事件接口,一个 EventSource 会对 http 服务开启一个持久化链接,它发送的事件格式是‘text/stream’,开启 EventSource 事件后,它会一直保持开启状态,直到被要求关闭

Server 使用

我采用的是 node + Koa 的方式,由于它是属于 http 的请求方式,所以通过路由匹配,主要需要设置{‘Content-Type’: ’text/event-stream’}形式

 const app = new Koa();
 app.use(async (ctx, next) => {
    if (ctx.path === '/stream') {
        ctx.request.socket.setTimeout(0)
        ctx.req.socket.setNoDelay(true)
        ctx.req.socket.setKeepAlive(true)
        ctx.set({
            'Content-Type': 'text/event-stream',
            'Cache-Control': 'no-cache',
            'Connection': 'keep-alive',
            'Access-Control-Allow-Origin': '*',
            'Access-Control-Allow-Credentials': 'true'
        })
        const stream = new PassThrough()
        ctx.status = 200
        ctx.body = stream
        setInterval(() => {
            stream.write(`data: ${new Date()}\n\n`)
        }, 1000)
        return
    }

    try {
        await next()
    }
})

client 使用

初始化事件对象, EventSource 第一个参数是访问的 http 的 url, 第二个 config 主要配置跨域属性, 3000 是客户端端口,通过 proxy 代理到后端

let serverEvent = new EventSource("http://localhost:3000/stream", {
  withCredentials: true,
});

image.png

相关的事件监听方法

serverEvent.onopen = (event) => {
  console.log(event, "onopen----event00000");
};
// 连接成功,消息推送
serverEvent.onmessage = (event) => {
  console.log(event, "onmessage----event00000");
};

// 服务发生异常,譬如跨域问题等
serverEvent.onerror = (event) => {
  console.log(event, "onerror----event00000");
};

由于它在 http 下,所以你在 network 中可以找到对应的 api,在里面有 EventStream 就是对应的返回结果,如果报错可以通过查看返回的 status 等信息排查问题原因

image.png

WebSocket

定义

WebSocket 可以在用户的浏览器和服务器之间打开交互的通信方式,使用此 API,您可以向服务器发送消息并接受事件驱动的响应,而无需通过轮训服务器的方式获得相应。

server 使用

在 server 端,新建目录文件 wss/websocket.js, 其内容如下, 其中 WebSocket.Server 的第一个参数是后端开启的 server 服务,path 对应的是 websocket 的路径,不填写默认是’/’,我这里采用的 ws 的库,另外还有像 socket.io 等库

const WebSocket = require("ws");
class ws {
  static ws = WebSocket.Server; // 默认实例
  static init(server) {
    // 创建实例
    // eslint-disable-next-line new-cap
    console.log("this.init, this.init, this.init, ");
    this.ws = new WebSocket.Server({ server, path: "/websocket" });
    this.ws.on("connection", async (ws, request) => {
      let count = 0;
      this.ws.clients.forEach((client) => {
        setInterval(() => {
          const data = {
            id: count,
            firstName: `first-${count}`,
          };
          client.send(JSON.stringify(data));
          count++;
        }, 2000);
      });
      this.ws.send("connected");
    });
  }
}

module.exports = ws;

client 使用

这里 WebSocket 后面添加的是 ws 对应的 url,其中 3000 是客户端端口,websocket 对应的路径同 server 端 WebSocket.Server 第二个参数 path 对应。

const exampleSocket = new WebSocket("ws://localhost:3000/websocket");

exampleSocket.onmessage = (event) => {
  const data = JSON.parse(event.data);
  if (data?.id >= 3) {
    exampleSocket.close();
  }
};

需要注意的是,这里如果需要访问 proxy 到服务端,通过 vite/webpack 相关配置里需要加{ws: true}的属性,如下图所示

image.png 另外,它的数据查看方式是在 network 下的 ws 中查看

image.png

对比

EventSource WebSocket
单向传输(server 到 client) 双向传输
基于 http 协议 基于 tcp 协议
只支持文本(utf-8) 支持二进制/文本(utf-8)格式数据传输
浏览器限制传输个数(chrome 限制 6 个,因为是 http 请求,http1.0 有限制) 浏览器不限制
轻量级协议,实现简单 重量级协议,实现复杂
支持断开重连 需要额外部署
不支持跨域,需要设置 header 支持跨域
没有防火墙阻塞 有防火墙

- Book Lists -