服务粉丝

我们一直在努力
当前位置:首页 > 财经 >

WebSocket 入门:简易聊天室

日期: 来源:前端西瓜哥收集编辑:西瓜

大家好,我是前端西瓜哥,今天我们用 WebSocket 来实现一个简单的聊天室。

WebSocket 是一个应用层协议,有点类似 HTTP。但和 HTTP 不一样的是,它支持真正的全双工,即不仅客户端可以主动发消息给服务端,服务端也可以主动发消息给客户端。

尤其是后者,让我们不用再基于 HTTP 长轮询或短轮询的低效方式来实现服务端通知。相比 HTTP,WebSocket 的服务端推送更轻量,并能减少服务端的压力。

服务端

nodejs 并没有提供原生的 WebSocket 模块。如果要实现,需要基于 net 模块,根据 WebSocket 标准去做实现。

因为实现很复杂,所以西瓜哥我选择直接用第三库 ws。

yarn add ws

类似 nodejs 原生的 http 等模块,ws 库支持 WebSocket 的服务端或客户端, 提供偏底层的 API。

我们先实现服务端代码:

import { WebSocketServer } from "ws";

// 创建一个 ws 服务
const wsSever = new WebSocketServer({
  port: 6060,
});

// 每当一个客户端进行了 ws 连接,就会创建一个 ws 对象
wsSever.on("connection", (ws) => {
  // 新客户端连接时,广播
  wsSever.clients.forEach((client) => {
    client.send(`有人进入聊天室,当前聊天室人数:${wsSever.clients.size}`);
  });

  // 广播任何客户端发送的消息
  ws.on("message", (data) => {
    const msg = data.toString();
    wsSever.clients.forEach((client) => {
      client.send(msg);
    });
  });

  // 当有客户端退出时,广播
  ws.on("close", () => {
    wsSever.clients.forEach((client) => {
      client.send(`有人退出了聊天室,当前聊天室人数:${wsSever.clients.size}`);
    });
  });
});

每当一个客户端进行了 websocket 连接,都会触发 wsServer 的 connection 事件,然后拿到一个 ws 对象。

这个 ws 对象代表了某个客户端和服务端的连接,我们可以通过它来接收对应客户端的消息,并让服务端对指定客户端进行主动消息推送。

新创建的 ws 对象会在建立连接时保存到 wsServer.clients 集合下,并在关闭连接后移除。所以我们可以利用这个 wsServer.clients 来进行广播,实现聊天室功能。

客户端

客户端使用原生的 WebSocket 对象,来和服务端进行 WebSocket 连接。

const ws = new WebSocket('ws://localhost:6060');

ws.addEventListener('message', (event) => {
  const div = document.createElement('div');
  div.innerText = event.data;
  document.body.append(div);
})

// 点击发送按钮,将输入框中的内容发送给服务器
const input = document.querySelector('input');
const btn = document.querySelector('button');
btn.onclick = () => {
  ws.send(input.value);
  input.value = '';
}

效果

简易聊天室

改为使用 Socket.IO

ws 库是偏底层的实现,比较简单。

另一个库 Socket.IO 的底层使用了 ws,并做功能上的增强,提供更多的能力。

相比 ws,Socket.IO 能够做到:

  1. 如果浏览器不支持 WebSocket,回退为 HTTP 长轮询方案来模拟 WebSocket( WebSocket 于 2011 年完成 RFC,已经很久了,目前来说主流浏览器都已经支持 WebSocket 了,还不支持 WebSocket 的浏览器是屑);
  2. 使用心跳包机制实现了自动重连。
  3. 包缓存。断连时发送数据,会将数据保存下来,等重新连接后再发送;
  4. 自定义事件支持;
  5. 广播;

相比自己去一个个实现,使用流行的轮子可能是更好的选择。

我们将前面的功能用 Socket.IO 实现一下。

服务端:

import { Server } from "socket.io";

// socket.io v3.x 开始默认不允许跨域,需要在配置显式设置为允许跨域
const io = new Server(6060, { cors: { origin: "*" } });

io.on("connection", (socket) => {
  // 新客户端连接时,广播
  io.emit("chat", `有人进入聊天室,当前聊天室人数:${io.engine.clientsCount}`);

  // 广播任何客户端发送的消息
  socket.on("chat", (data) => {
    io.emit("chat", data);
  });

  // 当有客户端退出时,广播
  socket.on("disconnect", () => {
    io.emit("chat", `有人退出了聊天室,当前聊天室人数:${io.engine.clientsCount}`);
  });
});

需要特别注意的是,Socket.IO 的 v3.x 版本开始,默认不允许跨域,需要在配置显式设置为允许跨域。

客户端:

const socket = io('ws://localhost:6060');

socket.on('chat', (data) => {
  const div = document.createElement('div');
  div.innerText = data;
  document.body.append(div);
})

// 点击发送按钮,将输入框中的内容发送给服务器
const input = document.querySelector('input');
const btn = document.querySelector('button');
btn.onclick = () => {
  console.log('发送');
  socket.emit('chat', input.value);
  input.value = '';
}

Socket.IO 优点是实现了生产环境需要的底层非业务能力,让我们能更心无旁骛地去编写业务代码。

缺点是丢失了灵活性。因为做了定制化,所以需要配套使用 Socket.IO 的客户端和服务端库的包,某种意义脱离了网络协议标准。在出现跨语言(比如前端是 JS,后端是 Java)的场景时,需要提供对应的语言的 Socket.IO 实现。

demo

demo 已经放到 github 上了,使用方法在 README.md 中有说明。

https://github.com/F-star/websocket-chat-demo

结尾

本文演示了 WebSocket  简易的聊天室功能是如何实现的,希望对你有所帮助。

我是前端西瓜哥,欢迎关注我,学习更多前端知识。



相关阅读,

服务端如何推送消息给客户端?

HTTP 的常见头字段有哪些?说说它们的作用

HTTP 中的常用状态码及使用场景

协同编辑中使用的 OT 算法是什么?

input 实现回车失焦,考虑输入法的情况


关注公众号,后台回复 「字节」,即可获得字节前端面试资料

相关阅读

  • 在 VSCode 中像写 TypeScript 一样写 JavaScript

  • 大家好,我是前端西瓜哥。我们在 VSCode 编辑器中编写 js 代码,是会提供类型提示的。VSCode 会推断一个变量是什么类型,并在你输入内容的时候,提供对应的 API 属性或方法补全。如
  • 一起学 pixijs(2):修改图形属性

  • 大家好,我是前端西瓜哥。我们做动画、游戏、编辑器,需要根据用户的交互等操作,去实时地改变图形的属性,比如位置,颜色等信息。今天西瓜哥带大家来看看在 pixijs 怎么修改图形的属
  • Amazing!如何根据背景色自动切换黑白文字?

  • 在项目中,经常会碰到背景色不确定的场景,为了让内容文字足够清晰可见,文字和背景之间需要有足够的对比度。换句话说,当背景是深色时,文字为白色,当背景是浅色时,文字为黑色,就像这样
  • 巧用视觉障眼法,还原 3D 文字特效

  • 最近群里有这样一个有意思的问题,大家在讨论,使用 CSS 3D 能否实现如下所示的效果:这里的核心难点在于,如何利用 CSS 实现一个立体的数字?CSS 能做到吗?不是特别好实现,但是,如果仅
  • 那些炫酷的 CSS 文字效果之诗词《兔》

  • 不知不觉已经迈入2023年,今年是兔年,想到兔子就会联想到玉兔,中秋,胡萝卜,兔子不吃窝边草,这就越扯越远了,今天的主题是用纯CSS来实现各种不错的文字效果,文字则摘录古诗词中有关《
  • 今晚 B 站直播预告 - 《你所不知道的CSS》

  • 扫码直达 B 站直播间,7 号晚 20:00 点准时开始,预计时长 1 小时,感兴趣来听听:Hi 各位小伙伴,本周六(2023/01/07)晚 20:00 点,我将在 Bilibili 给大家带来一场《你所不知道的CSS》
  • 不负时光,Coco 的 2022 年终总结

  • 从来没有写过年终总结,回首 2022,感觉有很多令人难忘的瞬间,觉得今年很有必要记录一下。在回家的列车上,回忆一整年的的经历,有挣扎、有坚持、有迷茫、有喜悦,最终也有收获。用文
  • CSS 奇思妙想之酷炫倒影

  • 在 CSS 中,倒影是一种比较常见的效果。今天,我们就将尝试,使用 CSS 完成各类不同的倒影效果,话不多说,直接进入主题。实现倒影的两种方式首先,快速过一下在 CSS 中,实现倒影的 2 种
  • 丢弃 HttpClient 了,这款轻量级框架更强!

  • 来自:CSDN,作者:Kevin&Amy链接:https://blog.csdn.net/qq_38233258/article/details/115799602一、Forest1.1 业务需求一般情况下是后端提供接口,前端调用,解决需求,但是有的时候为

热门文章

  • “复活”半年后 京东拍拍二手杀入公益事业

  • 京东拍拍二手“复活”半年后,杀入公益事业,试图让企业捐的赠品、家庭闲置品变成实实在在的“爱心”。 把“闲置品”变爱心 6月12日,“益心一益·守护梦想每一步”2018年四

最新文章

  • WebSocket 入门:简易聊天室

  • 大家好,我是前端西瓜哥,今天我们用 WebSocket 来实现一个简单的聊天室。WebSocket 是一个应用层协议,有点类似 HTTP。但和 HTTP 不一样的是,它支持真正的全双工,即不仅客户端可以
  • 在 VSCode 中像写 TypeScript 一样写 JavaScript

  • 大家好,我是前端西瓜哥。我们在 VSCode 编辑器中编写 js 代码,是会提供类型提示的。VSCode 会推断一个变量是什么类型,并在你输入内容的时候,提供对应的 API 属性或方法补全。如
  • 一起学 pixijs(2):修改图形属性

  • 大家好,我是前端西瓜哥。我们做动画、游戏、编辑器,需要根据用户的交互等操作,去实时地改变图形的属性,比如位置,颜色等信息。今天西瓜哥带大家来看看在 pixijs 怎么修改图形的属
  • Fabrie 文档 V2.7.5版本更新 | Fabrie小程序上线啦!

  • 基于用户洞察和反馈,Fabrie会定期更新迭代,用更好的自己来与你相遇,欢迎随时给我们提出问题和建议。在本次更新中,我们终于上线了「Fabrie微信小程序」,并对「团队管理页面」进行