WebSocket 入门:简易聊天室

news2024/10/6 5:53:56

大家好,我是前端西瓜哥,今天我们用 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://www.coloradmin.cn/o/179810.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于Tkinter制作简易的串口bootloader上位机

文章目录前言1.测试设备1.1 UART Bootloaer软件架构图1.2 UART Bootloader流程图1.3 通信数据处理1.3.1 S19文件的简单介绍1.3.2 S19文件的传输方式1.3.2 接收数据之后的处理1.4 链接文件设置1.4.1 Bootloader设置1.4.2 Application设置2.上位机2.1 参考资料2.2 Tkinter简介2.3…

C++初阶:vector类

文章目录1 vector介绍2 实现vector2.1 类的定义2.2 默认成员函数2.2.1 构造函数2.2.2 析构函数2.2.3 拷贝构造2.2.4 赋值重载2.3访问接口2.4 容量接口2.5 修改接口2.5.1 尾插尾删2.5.2 任意位置插入2.5.3 任意位置删除2.6 其他接口1 vector介绍 1 vector是表示可变大小数组的序…

每日学术速递1.26

CV - 计算机视觉 今天带来的是北航IRIP实验室被国际人工智能联合会议IJCAI-ECAI 2022接收的3篇论文。 IJCAI 是人工智能领域中最主要的学术会议之一,原为单数年召开,自2015年起改为每年召开,本次IJCAI与ECAI一起召开。IJCAI官网显示&#xf…

【Linux】冯诺依曼体系结构与操作系统概念理解

👑作者主页:安 度 因 🏠学习社区:StackFrame 📖专栏链接:Linux 文章目录一、前言二、冯诺依曼体系结构1、体系简述2、内存的重要性3、硬件方案解释软件行为4、体系结构中的数据流动5、拓展三、操作系统简述…

ch1 操作系统启动

lab1 实验准备 按照实验解压后进入oslab中,按照make编译。 cd /home/shiyanlou/oslab/ tar -zxvf hit-oslab-linux-20110823.tar.gz \-C /home/shiyanlou/ ./run cd ./linux-0.11/ make all make clean ..... make all运行脚本即可启动内核 调试 汇编级调试和C语…

贪心算法的题目

每一步都做出一个局部最优的选择,最终的结果就是全局最优 只有一部分问题才能用贪心算法(严格来讲,一个问题能不能用贪心算法需要证明的) 2022.8.30 蔚来笔试题: 有a个y,b个o,c个u,用这些字母拼成一个字符串&#xf…

Anaconda软件中的 Environments 及 Jupyter Lab使用方法介绍

来源:投稿 作者:助教-Frank 编辑:学姐 本篇是打造舒适的AI开发环境系列-软件篇1 上期内容:学人工智能电脑&主机八大件配置选择指南 本文的重点: (1)Environments使用中如何安装python包.; (2)Jupyter Lab如何在…

Kettle(6):表输入组件——mysql转mysql

1 需求 前面我们已经将Excel中数据抽取到了MySQL的t_user表中。 现在有了新需求,要将MySQL数据库中的 t_user 表中的数据抽取出来,装载到另外一张表 t_user1中。 2 构建Kettle数据流图 2.1 从核心对象的输入组件中,将「表输入」组件拖拽到中…

电脑下载软件用什么软件好?安卓手机下载软件用哪个软件好?IDM下载器说:在做的都是弟弟

大年初五,迎财神,先祝大家新的一年财源滚滚,接下来为大家分享超级经典的IDM下载器,电脑端毫无争议的下载工具,安卓平台idm也是力压群雄,下面就为大家详细分享下: 1:1DM下载器&#x…

微服务统一登陆认证怎么做

[微服务统一登陆认证怎么做}?JWT 无状态登录原理 1.1.什么是有状态? 有状态服务,即服务端需要记录每次会话的客户端信息,从而识别客户端身份,根据用户身份进行请求的处理,典型的设计如tomcat中的session…

notepad++在行首行尾添加字符 | 选中列

目录 1、首行/尾行添加字符 1【使用快捷键 CtrlH】 2【^为行首、$为行尾】 3、查找模式选中正则表达式 2、Notepad中列选(竖选) 1、首行/尾行添加字符 1【使用快捷键 CtrlH】 或者鼠标 2【^为行首、$为行尾】 3、查找模式选中正则表达式 2、Notepad中列选(竖…

深度学习入门(一)感知机

该文将介绍感知机A(perceptron)这一算法。感知机是由美国学者Frank Rosenblatt在1957年提出来的。为何我们现在还要学习这一很久以前就有的 算 法 呢 ? 因 为 感 知 机 也 是 作 为 神 经 网 络(深 度 学 习)的起源的算…

详解Windows通过命令行查看电脑连接过的WIfI密码

CONTENT打开命令行进入命令行下的netsh工具查看连接过的WiFi名称指定WiFi名称查看密码在Windows操作系统中(PS:Windows Vista及以后的Windows系统)可以通过命令行工具netsh查看和更改电脑的无线连接设置,包括WiFi。本篇博客将详细…

C语言进阶——文件管理

每当我们写好一段代码运行结束之后,再次运行的时候就会发现,之前在终端上输入的数据都会消失,那么如何把之前输入的数据保存下来呢? 我们一般把数据持久化的方式有把数据存放在磁盘文件中、存放到数据库。打印等方式进行保存。 …

Java---微服务---elasticsearch安装部署

elasticsearch安装部署1.部署单点es1.1.创建网络1.2.加载镜像1.3.运行2.部署kibana2.1.部署2.2.DevTools3.安装IK分词器3.1.在线安装ik插件(较慢)3.2.离线安装ik插件(推荐)1)查看数据卷目录2)下载并解压缩分…

RocketMQ源码本地搭建调试

1 GitHub源码 git clone https://github.com/apache/rocketmq.git导入IDEA,可在命令行执行mvn compile一下,保证源码能够正确编译。本次我使用的master分支的版本-4.8.0。下面我们开始准备启动Namesrv。 2 启动Namesrv 到namesrv模块找到NamesrvStart…

web游戏---canvas基础图形

基础 canvas标签 canvas是H5中新推出的标签,这个提供一块画布,可以在上面绘制图案,通过这种方式制作web游戏带来的性能消耗比操作DOM要小的多。 如果知做浏览器游戏,为了保证性能最好使用画布来制作。 坐标系 画布的坐标系和…

ThinkPadE540重装系统

过年这段时间,帮家里人重装了一下win10系统,在这里记录一下,方便今后还要使用。 先准备两个U盘,一个存储电脑的文件(以防文件丢失),一个空u盘(制作重装系统的) 一.下载镜…

【5-卷积神经网络】北京大学TensorFlow2.0

课程地址:【北京大学】Tensorflow2.0_哔哩哔哩_bilibiliPython3.7和TensorFlow2.1六讲:神经网络计算:神经网络的计算过程,搭建第一个神经网络模型神经网络优化:神经网络的优化方法,掌握学习率、激活函数、损…

Junit单元测试框架【基础篇】

Junit单元测试框架【基础篇】🍎一.Junit单元测试框架🍒1.1 注解🍒1.2 断言🍒1.3 用例执行顺序🍒1.4 测试套件🍉1.4.1 指定类🍉1.4.1 指定包🍒1.5 参数化🍉1.5.1 单参数&a…