WebSocket快速实现框架:socket.io

在web端,很多时候我们需要服务器主动向浏览器发消息,比如实现一个事实聊天窗口,比如实现一个地图,然后实时显示路径拥挤程度,实现一个实时变化的数据大屏,如果使用普通的http请求,那么我们就得写一个定时器,然后定时向对应的接口发起请求,服务器返回相关的数据,这种方式叫做轮询。

轮询是指由浏览器每隔一段时间(如每秒)向服务器发出HTTP请求,然后服务器返回最新的数据给客户端。这种传统的模式带来很明显的缺点,即浏览器需要不断的向服务器发出请求,然而HTTP请求与回复可能会包含较长的头部,其中真正有效的数据可能只是很小的一部分,所以这样会消耗很多带宽资源。

轮询不仅会消耗很多带宽资源,同时因为每次请求都要经历3次握手和4次挥手,所以延迟也是比较高的,并且你无法实时的获取到数据,因为你不知道服务器上的数据什么时候会刷新,同时因为请求次数的增多,所以对服务器的压力也很大。

在这种情况下,HTML5定义了WebSocket协议,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。

1. 实现

如果需要使用WebSocket,那么首先你的服务器需要支持WebSocket,而这里就拿Nodejs来举例,Nodejs有一个名字叫做socket.io的框架,使用它,可以让你很轻易的就可以创建一个支持WebSocket的服务器。而且该框架使用的人数非常多:

image-20210404172509375

2. 开始使用

使用的方式也非常的简单,我们这里通过React来做前端框架,后端用Nodejs来书写。

2.1 服务端安装

首先是服务器端,一般来讲,使用Nodejs做服务器,那么就会用到一个服务器框架,叫做express,该框架大大简化了使用Nodejs搭建一个服务器的难度,当然,如果是一个比较庞大的项目,目前来讲推荐使用Nestjs,这里我们就仅仅只讲express。

新建一个文件夹,进入到文件中,初始化Node项目:

# 这里的-y代表全部选择默认值
npm init -y

然后引入包:

# 使用npm或者yarn
npm i express socket.io

yarn add express socket.io

创建代码:

const app = require("express")();
const http = require("http").Server(app);
// cors: true 代表接受跨域
const io = require("socket.io")(http, { cors: true });

// socket代表客户端对象,可以通过它向客户端发送一些消息
io.on("connection", (socket) => {
  // 一旦客户端连接成功,就会触发下面的语句
  console.log("连接成功");
});

http.listen(8000, () => {
  console.log("监听端口:8000");
});

其中cors: true代表可以跨域访问,因为现在前后端分离的关系,所以肯定会涉及到跨域,所以我们需要处理一下跨域。

到这里,一个简单的支持WebSocket的服务器就已经创建好了,是不是非常简单。

2.2 客户端安装

对于客户端,也就是前端项目,我们需要引入socket.io-client这个包:

# 使用npm或者yarn
npm i socket.io-client

yarn add socket.io-client

该包的使用也非常的简单,

import { io } from "socket.io-client";

// 连接一个WebSocket服务器
const socket = io("ws://localhost:8000");

注意:服务器的前缀一定要为ws://不能为http://或者https://,端口号一定要对应服务器设置的端口号。

这样就连接到了WebSocket服务器。

同时在服务器的控制台中,应该可以看到输出了连接成功的字样:

image-20210404174118997

3. 方法

socket.io有两个非常重要的方法,一个是socket.on,一个是socket.emit

  • socket.on:注册一个事件。
  • socket.emit:触发一个事件。

这两个方法在客户端和服务器都是会用到的,比如服务端使用socket.on注册一个登陆事件。

io.on("connection", (socket) => {
  console.log("连接成功");

  // 注册一个登陆事件
  // data为前端传来的数据
  socket.on("signIn", data => {
    // 打印data
    console.log(data);
    // ...后面可以写登陆相关的逻辑
  });
});

注意:因为socket表示客户端对象,所以事件应该注册到该客户端对象上,客户端才能进行触发该事件。

客户端触发事件的方法:

// 连接一个WebSocket服务器
const socket = io("ws://localhost:8000");

const data = {
  name: "张三",
  age: 80,
};

// 触发登陆事件,并且将data传递给服务器。
socket.emit("signIn", data);

这个时候可以看到控制台中打印了相关的数据:

image-20210404174835402

同理,服务端往客户端传递数据也使用同样的方法:

客户端只需要注册相应的事件:

socket.on("success", data=>{
  console.log(data)
})

服务器触发相关事件

io.on("connection", (socket) => {
  console.log("连接成功");

  // 注册一个登陆事件
  // data为前端传来的数据
  socket.on("signIn", (data) => {
    // 打印data
    console.log(data);
    // 触发登陆成功的逻辑,并且将data返回给客户端
    socket.emit("success", data);
  });
});

可以看到,客户端也输出了相应的数据:

image-20210404175050464

而服务器如果需要向所有的客户端都发送消息,则值需要使用io.emit("success", data),那么每个客户端都会接收到相同的消息。

4. 最后

socket.io这个框架大大的简化了WebSocket的实现和操作难度,所以截止目前为止,socket.io在GitHub上已经有50K+star,相对来说也是一个非常值得信赖的库,更新的也比较频繁,社区也比较活跃,本篇文章仅仅介绍了该库的最基本的用法,如果有更多的需求还是建议去读一下官方文档,我大致看了一下,官方文档写的还是非常详细的。