WebSocket创建简易版的聊天室

引言

本文主要是帮助大家快速的去上手websocket,了解一些websocket的基本原理以及使用方法,本文主要采用的是nodejs-websocket这个库,因为这个库比较简单易上手,适合新手学习,下面我们就开始叭~

为什么要使用Websocket?

对于新手刚刚接触websocket时,都会有个疑问,明明已经有了 HTTP 协议,为什么还需要另一个协议?它有什么用?

答案就是,Http协议的通信只能通过客户端发起,而无法由服务器主动发起至客户端,因此就做不到服务器主动推送消息,因此我们需要一种可以由服务器主动发起通信的协议,于是websocket就诞生了。

什么是Websocket?

WebSocket 是基于TCP/IP协议的一种通信协议,和Http不同,WebSocket是一种全双工的实时通讯,是有状态的,双向的一种协议。

Websocket解决了什么问题?

就拿聊天室来举例,最初的聊天室想要实现聊天消息的实时性,就要通过Http协议不断的去轮询发请求,查询有没有新的聊天消息,来达到实时更新的目的,但是这样效率极低,非常浪费资源,因为要保持Http的长连接或者需要不断地建立连接,因此websocket就解决了这种需要双向通信且实时连接的场景问题。

Websocket的特点

  • 建立在 TCP/IP 协议之上,服务器端的实现比较容易
  • 与 HTTP 协议有着良好的兼容性
  • 握手阶段采用 HTTP 协议,握手时不易被屏蔽
  • WebSocket的协议端口是80,SSL协议端口是443
  • 数据格式比较轻量,性能开销小,通信高效
  • 可以发送文本以及二进制数据等
  • 优秀的心跳机制,可以判断是否掉线
  • 没有同源限制,客户端可以与任意服务器通信
  • 协议标识符是ws(如果加密,则为wss
  • URL格式:ws://localhost:5000

Websocket的优点

  1. 实时性:WebSocket 支持服务器端主动向客户端推送数据并在客户端接收到数据后即可处理,实现了实时的数据传输和更新
  2. 减少数据传输量:相比于 HTTP 请求,WebSocket 协议采用二进制帧传输数据,且在协议头上更加轻量级,可以有效减少数据传输量
  3. 更低的延迟:由于 WebSocket 协议采用更加轻量级的协议头和无需频繁建立连接的方式,可以显著降低传输数据的延迟
  4. 更高的可靠性:WebSocket 协议的数据传输基于 TCP 连接,具有 TCP 协议的可靠性和容错性,保证数据的完整性和一致性
  5. 节省服务器资源:WebSocket 协议采用长连接机制,避免了频繁建立、关闭连接的开销,从而减轻了服务器的负担

Websocket的基本原理

  1. 客户端与服务器建立初始连接
    客户端发送HTTP请求,包含一个特殊的Upgrade头部,请求服务器升级协议为WebSocket。服务器验证请求后,将HTTP协议升级为WebSocket协议,并返回升级成功的响应。
  2. WebSocket握手过程
    握手成功后,客户端和服务器之间的连接由HTTP协议升级为WebSocket协议。此时,连接变成了持久化的双向通信通道。
  3. 双向通信
    一旦建立了WebSocket连接,客户端和服务器可以随时通过该连接进行双向通信,不再需要通过HTTP协议发起请求。客户端可以发送消息给服务器,服务器也可以主动推送消息给客户端,实现实时交互。
  4. 心跳维持连接
    WebSocket连接是一个持久化的连接,不会因为每次通信后关闭连接。为了保持连接活跃,客户端和服务器之间可以定期发送心跳数据包,以确保连接不被中断。

Websocket常用的库

  1. ws:ws 是一个简单易用的 WebSocket 库,适用于 Node.js 环境,轻量级。
  2. uWebSockets.js:uWebSockets.js 是一个高性能的 WebSocket 库,其具有非常低的延迟和高吞吐量,适用于需要极致性能的场景。
  3. nodejs-websocket:nodejs-websocket是一个简单易用的适用于 Node.js的WebSocket 库

Websocket相关API介绍

  1. WebSocketServer.createServer(options, 回调函数)
  • 用于创建WebSocket服务器
  • options参数是一个对象,可以包含以下可选属性:host、port、path等
  • 回调函数:当客户端连接成功时会调用它,可以在该回调函数中处理客户端消息和关闭连接等操作
  1. server.listen(port, 主机名, 回调函数)
  • 用于启动服务器并开始监听指定的端口号
  • 参数port表示要监听的端口号,hostname表示要监听的主机名,默认为0.0.0.0
  • 回调函数:当服务器准备好时会调用它
  • 调用该方法并不会立即启动服务器,而是会等待客户端连接请求
  1. server.connections

该属性是一个WebSocketConnection对象的数组,表示当前连接到服务器的所有客户端。

  1. connection.sendText(str)

该方法用于发送文本消息给客户端。参数str是要发送的文本内容。

  1. connection.sendBinary(data)

该方法用于发送二进制数据给客户端。参数data是要发送的二进制数据,可以是Buffer对象或者ArrayBuffer对象。

  1. connection.close(code, reason)

该方法用于关闭WebSocket连接。参数code表示关闭状态码,可以是一个整数;reason表示关闭原因,可以是一个字符串。

  1. connection.on(event, callback)

该方法用于注册事件回调函数,包括:

  • 'text': 客户端发送文本消息时触发。
  • 'binary': 客户端发送二进制数据时触发。
  • 'close': WebSocket连接关闭时触发。
  • 'listening': 服务器开始监听连接请求。
  • 'connection': 客户端连接到服务器。
  • 'close': 服务器关闭。

Websocket聊天室功能实现

功能主要采用的是nodejs-websocket这个库,因为这个库的API比较简单,适合新手学习

Node后端部分

javascript 复制代码
let ws = require("nodejs-websocket")
let UserId = 0
// 创建server服务
let server = ws.createServer(connect => {
  // 获取用户名
  UserId++
  connect.UserName = `用户${UserId}`
  connect.color = getRedomColor()
  // 第一次接入websocket,并给用户返回信息
  const msg = `<p style='color:#ffe0e6;'>欢迎新用户<span style='color:${connect.color};'>${connect.UserName}:</span>进入聊天室!</p>`
  // 广播消息
  broadCast(msg)
  // 监听用户发送信息时
  connect.on("text", function (str) {
    console.log("收到信息: " + str)
    const msg = `<span style='color:${connect.color};'>${connect.UserName}:</span>${str}`
    // 广播消息
    broadCast(msg)
  })
  // 监听用户离开,或关闭
  connect.on("close", function (code, reason) {
    console.log('用户离开')
    const msg = `<p style='color:#ff9b9b;'>用户<span style='color:${connect.color};'>${connect.UserName}:</span>离开了聊天室!</p>`
    // 广播消息
    broadCast(msg)
    UserId--
  })
  // 监听页面异常,对异常进行处理
  connect.on("error", function (err) {
    console.log('请求异常!');
  })
}).listen(8003, () => {
  console.log("服务已经运行在:ws://localhost:8003")
})

// 广播消息给所有用户
function broadCast(msg) {
  server.connections.forEach(item => {
    item.sendText(msg)
  })
}

// 设置随机颜色
function getRedomColor() {
  const red = Math.floor(Math.random() * 256)
  const green = Math.floor(Math.random() * 256)
  const blue = Math.floor(Math.random() * 256)
  return `rgb(${red},${green},${blue})`
}

前端部分

HTML

xml 复制代码
<body>
    <input type="text" placeholder="请输入内容">
    <button>发送请求</button>
    <!-- 展示消息 -->
    <div class="result"></div>
</body>

JavaScript

ini 复制代码
<script>
    let result = document.querySelector('div')
    let input = document.querySelector('input')
    let button = document.querySelector('button')
    let ws = new WebSocket('ws://localhost:8003')
    // 链接ws
    ws.onopen = () => {
        result.innerHTML = '链接成功!'
    }
    // 点击按钮发送input文本给服务器
    button.onclick = () => {
        ws.send(input.value) 
        input.value = ''
    }
    input.addEventListener('keyup', item => {
        if (item.keyCode == 13) {
            ws.send(input.value)
            input.value = ''
        }
    })
    // 监听服务器返回的结果
    ws.onmessage = (res) => {
        let ADIV = document.createElement('div')
        ADIV.innerHTML = res.data
        result.appendChild(ADIV)
    }
</script>
相关推荐
AlienZHOU13 小时前
AI Coding 时代下,我的技术面试实践分享
前端·后端·面试
李昊哲小课15 小时前
Spring Boot SSE 实时推送教程
spring boot·websocket·flux·sseemitter
Captaincc16 小时前
AI用量v0.1.11更新发布 新增 jusage doctor 诊断指令 托盘展示token 和余额 新增 AutoClaw 支持
前端·后端·vibecoding
计算机魔术师17 小时前
德国Wiki被黑后两周,OpenAI终于把模型失控的账本摊开了
前端
kyriewen18 小时前
我让 AI 当面试官面了我一轮:第 3 个追问我就卡住了(附 10 道追问清单)
前端·面试·ai编程
IT_陈寒18 小时前
Python的GIL把我坑惨了,多线程跑得比单线程还慢
前端·人工智能·后端
前端snow18 小时前
ai agent --- 多agent框架之图编排引擎-langgraph
前端
竹林81818 小时前
OmniPic Studio v3.2.1 核心技术架构与全平台发版解析文档
前端·浏览器
JamesZhang8007818 小时前
页面内存只涨不跌? 一次泄漏排查, 牵出 WeakMap 的诞生
前端
Z小明18 小时前
第 6 章 组件进阶
前端·vue.js