3分钟使用 WebSocket 搭建属于自己的聊天室(WebSocket 原理、应用解析)
文章目录
- WebSocket 的由来
- WebSocket 是什么
- WebSocket 优缺点
- 优点
- 缺点
- WebSocket 适用场景
- 主流浏览器对 WebSocket 的兼容性
- WebSocket 通信过程以及原理
- 建立连接
- 具体过程
- 示例
- Sec-WebSocket-Key
- Sec-WebSocket-Extensions
- 数据通信
- 数据帧
- 帧头(Frame Header)
- 掩码(Masking)
- 负载数据(Payload Data)
- 来自 MDN 的一个小例子
- 维持连接
- 关闭连接
- 使用 WebSocket 实现一个简易聊天室
- 前端源码
- 后端源码 Java
- 总结
- 参考
- 个人简介
WebSocket 的由来
- 在 WebSocket 出现之前,我们想实现实时通信、变更推送、服务端消息推送功能,我们一般的方案是使用 Ajax 短轮询、长轮询两种方式:
- 比如我们想实现一个服务端数据变更时,立即通知客户端功能,没有 WebSocket 之前我们可能会采用以下两种方案:短轮询或长轮询

- 上面两种方案都有比较明显的缺点:
1、HTTP 协议包含的较长的请求头,有效数据只占很少一部分,浪费带宽
2、短轮询频繁轮询对服务器压力较大,即使使用长轮询方案,客户端较多时仍会对客户端造成不小压力
- 在这种情况下,HTML5 定义了 WebSocket 协议,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。
WebSocket 是什么
- WebSocket 是一种网络传输协议,可在单个 TCP 连接上进行全双工通信,位于 OSI 模型的应用层。
- WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。客户端和服务器只需要完成一次握手,两者之间就可以创建持久性的连接,并进行双向数据传输。

WebSocket 优缺点
优点
- 实时性: WebSocket 提供了双向通信,服务器可以主动向客户端推送数据,实现实时性非常高,适用于实时聊天、在线协作等应用。
- 减少网络延迟: 与轮询和长轮询相比,WebSocket 可以显著减少网络延迟,因为不需要在每个请求之间建立和关闭连接。
- 较小的数据传输开销: WebSocket 的数据帧相比于 HTTP 请求报文较小,减少了在每个请求中传输的开销,特别适用于需要频繁通信的应用。
- 较低的服务器资源占用: 由于 WebSocket 的长连接特性,服务器可以处理更多的并发连接,相较于短连接有更低的资源占用。
- 跨域通信: 与一些其他跨域通信方法相比,WebSocket 更容易实现跨域通信。
缺点
- 连接状态保持: 长时间保持连接可能会导致服务器和客户端都需要维护连接状态,可能增加一些负担。
- 不适用于所有场景: 对于一些请求-响应模式较为简单的场景,WebSocket 的实时特性可能并不是必要的,使用 HTTP 请求可能更为合适。
- 复杂性: 与传统的 HTTP 请求相比,WebSocket 的实现和管理可能稍显复杂,尤其是在处理连接状态、异常等方面。
WebSocket 适用场景
- 实时聊天应用: WebSocket 是实现实时聊天室、即时通讯应用的理想选择,因为它能够提供低延迟和高实时性。
- 在线协作和协同编辑: 对于需要多用户协同工作的应用,如协同编辑文档或绘图,WebSocket 的实时性使得用户能够看到其他用户的操作。
- 实时数据展示: 对于需要实时展示数据变化的应用,例如股票行情、实时监控系统等,WebSocket 提供了一种高效的通信方式。
- 在线游戏: 在线游戏通常需要快速、实时的通信,WebSocket 能够提供低延迟和高并发的通信能力。
- 推送服务: 用于实现消息推送服务,向客户端主动推送更新或通知。
主流浏览器对 WebSocket 的兼容性

- 由上图可知:目前主流的 Web 浏览器都支持 WebSocket,因此我们可以在大多数项目中放心地使用它。
WebSocket 通信过程以及原理
建立连接
- WebSocket 协议属于应用层协议,依赖传输层的 TCP 协议。它通过 HTTP/1.1 协议的 101 状态码进行握手建立连接。
具体过程
- 客户端发送一个 HTTP GET 请求到服务器,请求的路径是 WebSocket 的路径(类似 ws://example.com/socket)。请求中包含一些特殊的头字段,如 Upgrade: websocket 和 Connection: Upgrade,以表明客户端希望升级连接为 WebSocket。
- 服务器收到这个请求后,会返回一个 HTTP 101 状态码(协议切换协议)。同样在响应头中包含 Upgrade: websocket 和 Connection: Upgrade,以及一些其他的 WebSocket 特定的头字段,例如 Sec-WebSocket-Accept,用于验证握手的合法性。
- 客户端和服务器之间的连接从普通的 HTTP 连接升级为 WebSocket 连接。之后,客户端和服务器之间的通信就变成了 WebSocket 帧的传输,而不再是普通的 HTTP 请求和响应。
示例
// 客户端请求
GET ws://localhost:8888/ HTTP/1.1
Host: localhost:8888
Connection: Upgrade
Upgrade: websocket
Origin: http://localhost:63342
Sec-WebSocket-Version: 13
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,ja;q=0.8,en;q=0.7
Sec-WebSocket-Key: b7wpWuB9MCzOeQZg2O/yPg==
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits// 服务端响应
HTTP/1.1 101 Web Socket Protocol Handshake
Connection: Upgrade
Date: Wed, 22 Nov 2023 08:15:00 GMT
Sec-WebSocket-Accept: Q4TEk+qOgJsKy7gedijA5AuUVIw=
Server: TooTallNate Java-WebSocket
Upgrade: websocket
Sec-WebSocket-Key
- 与服务端响应头部的 Sec-WebSocket-Accept 是配套的,提供基本的防护,比如恶意的连接,或者无意的连接;这里的“配套”指的是:Sec-WebSocket-Accept 是根据请求头部的 Sec-WebSocket-Key 计算而来,计算过程大致为基于 SHA1 算法得到摘要并转成 base64 字符串。
Sec-WebSocket-Extensions
- 用于协商本次连接要使用的 WebSocket 扩展。
数据通信
- WebSocket 的每条消息可能会被切分成多个数据帧(最小单位)。发送端会将消息切割成多个帧发送给接收端,接收端接收消息帧并将关联的帧重新组装成完整的消息。
数据帧
0 1 2 30 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1+-+-+-+-+-------+-+-------------+-------------------------------+|F|R|R|R| opcode|M| Payload len | Extended payload length ||I|S|S|S| (4) |A| (7) | (16/64) ||N|V|V|V| |S| | (if payload len==126/127) || |1|2|3| |K| | |+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +| Extended payload length continued, if payload len == 127 |+ - - - - - - - - - - - - - - - +-------------------------------+| |Masking-key, if MASK set to 1 |+-------------------------------+-------------------------------+| Masking-key (continued) | Payload Data |+-------------------------------- - - - - - - - - - - - - - - - +: Payload Data continued ... :+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +| Payload Data continued ... |+---------------------------------------------------------------+
帧头(Frame Header)
- FIN(1比特): 表示这是消息的最后一个帧。如果消息分成多个帧,FIN 位在最后一个帧上设置为 1。
- RSV1、RSV2、RSV3(各1比特): 保留位,用于将来的扩展。
- Opcode(4比特): 指定帧的类型,如文本帧、二进制帧、连接关闭等。
WebSocket 定义了几种帧类型,其中最常见的是文本帧(Opcode 为 0x1)和二进制帧(Opcode 为 0x2)。其他帧类型包括连接关闭帧、Ping 帧、Pong 帧等。
- Mask(1比特): 指示是否使用掩码对负载进行掩码操作。
- Payload Length: 指定数据的长度。如果小于 126 字节,直接表示数据的长度。如果等于 126 字节,后面跟着 16 比特的无符号整数表示数据的长度。如果等于 127 字节,后面跟着 64 比特的无符号整数表示数据的长度。
掩码(Masking)
- 如果 Mask 位被设置为 1,则帧头后面的 4 字节即为掩码,用于对负载数据进行简单的异或操作,以提高安全性。
负载数据(Payload Data)
- 实际要传输的数据,可以是文本、二进制数据等
来自 MDN 的一个小例子
Client: FIN=1, opcode=0x1, msg="hello"
Server: (process complete message immediately) Hi.Client: FIN=0, opcode=0x1, msg="and a"
Server: (listening, newmessage containing text started)Client: FIN=0, opcode=0x0, msg="happy new"
Server: (listening, payload concatenated to previous message)Client: FIN=1, opcode=0x0, msg="year!"
Server: (process complete message) Happy new year to you too!
维持连接
- 当建立连接后,连接可能因为网络等原因断开,我们可以使用心跳的方式定时检测连接状态。若连接断开,我们可以告警或者重新建立连接。
关闭连接
- WebSocket 是全双工通信,当客户端发送关闭请求时,服务端不一定立即响应,而是等服务端也同意关闭时再进行异步响应。
- 下面是一个客户端关闭的例子:
Client: FIN=1, opcode=0x8, msg="1000"
Server: FIN=1, opcode=0x8, msg="1000"
使用 WebSocket 实现一个简易聊天室
- 下面是一个简易聊天室小案例,任何人打开下面的网页都可以加入我们聊天室进行聊天,然后小红和小明加入了聊天:

前端源码
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>WebSocket Chat</title>
</head>
<body>
<div id="chat"></div>
<input type="text" id="messageInput" placeholder="Type your message">
<button onclick="sendMessage()">Send</button><script>const socket = new WebSocket('ws://localhost:8888');socket.onopen = (event) => {console.log('WebSocket connection opened:', event);};socket.onmessage = (event) => {const messageDiv = document.getElementById('chat');const messageParagraph = document.createElement('p');messageParagraph.textContent = event.data;messageDiv.appendChild(messageParagraph);};socket.onclose = (event) => {console.log('WebSocket connection closed:', event);};function sendMessage() {const messageInput = document.getElementById('messageInput');const message = messageInput.value;socket.send(message);messageInput.value = '';}
</script>
</body>
</html>
后端源码 Java
package chat;import org.java_websocket.WebSocket;
import org.java_websocket.handshake.ClientHandshake;
import org.java_websocket.server.WebSocketServer;import java.net.InetSocketAddress;public class ChatServer extends WebSocketServer {public ChatServer(int port) {super(new InetSocketAddress(port));}@Overridepublic void onOpen(WebSocket conn, ClientHandshake handshake) {System.out.println("New connection from: " + conn.getRemoteSocketAddress().getAddress().getHostAddress());}@Overridepublic void onClose(WebSocket conn, int code, String reason, boolean remote) {System.out.println("Closed connection to: " + conn.getRemoteSocketAddress().getAddress().getHostAddress());}@Overridepublic void onMessage(WebSocket conn, String message) {System.out.println("Received message: " + message);// Broadcast the message to all connected clientsbroadcast(message);}@Overridepublic void onError(WebSocket conn, Exception ex) {System.err.println("Error on connection: " + ex.getMessage());}@Overridepublic void onStart() {}public static void main(String[] args) {int port = 8888;ChatServer server = new ChatServer(port);server.start();System.out.println("WebSocket Server started on port: " + port);}
}
总结
- WebSocket 是一种在客户端和服务器之间建立实时双向通信的协议。具备全双工、低延迟等优点,适用于实时聊天、多人协助、实时数据展示等场景。
参考
- WebSocket:概念、原理
个人简介
👋 你好,我是 Lorin 洛林,一位 Java 后端技术开发者!座右铭:Technology has the power to make the world a better place.
🚀 我对技术的热情是我不断学习和分享的动力。我的博客是一个关于Java生态系统、后端开发和最新技术趋势的地方。
🧠 作为一个 Java 后端技术爱好者,我不仅热衷于探索语言的新特性和技术的深度,还热衷于分享我的见解和最佳实践。我相信知识的分享和社区合作可以帮助我们共同成长。
💡 在我的博客上,你将找到关于Java核心概念、JVM 底层技术、常用框架如Spring和Mybatis 、MySQL等数据库管理、RabbitMQ、Rocketmq等消息中间件、性能优化等内容的深入文章。我也将分享一些编程技巧和解决问题的方法,以帮助你更好地掌握Java编程。
🌐 我鼓励互动和建立社区,因此请留下你的问题、建议或主题请求,让我知道你感兴趣的内容。此外,我将分享最新的互联网和技术资讯,以确保你与技术世界的最新发展保持联系。我期待与你一起在技术之路上前进,一起探讨技术世界的无限可能性。
📖 保持关注我的博客,让我们共同追求技术卓越。
相关文章:
3分钟使用 WebSocket 搭建属于自己的聊天室(WebSocket 原理、应用解析)
文章目录 WebSocket 的由来WebSocket 是什么WebSocket 优缺点优点缺点 WebSocket 适用场景主流浏览器对 WebSocket 的兼容性WebSocket 通信过程以及原理建立连接具体过程示例Sec-WebSocket-KeySec-WebSocket-Extensions 数据通信数据帧帧头(Frame Header)…...
Jina AI 的 8K 向量模型上线 AWS Marketplace,支持本地部署!
在当前多模态 AI 和大模型技术风头正劲的背景下,Jina AI 始终领跑于创新前沿,技术领先。2023 年 10 月 30 日,Jina AI 隆重推出 jina-embeddings-v2,这是全球首款支持 8192 输入长度的开源向量大模型,其性能媲美 OpenA…...
ubuntu上查看各个进程的实时CPUMEM占用的办法
top常见参数top界面分析system monitorhtop1、查看htop的使用说明2、显示树状结构3、htop使用好文推荐top top的用法应该是最为普遍的 常见参数 -d 更新频率,top显示的界面几秒钟更新一次 -n 更新的次数,top显示的界面更新多少次之后就自动结束了 当然也可以将top日志通过…...
电大搜题——打开学习之门的最佳选择
在快节奏的现代社会,追求知识和学习成为愈发重要的需求。然而,许多人由于时间和机会的限制,无法实现自己的教育梦想。就在这个时候,安徽开放大学广播电视大学通过推出电大搜题微信公众号,为广大学子提供了一个便捷高效…...
[论文笔记] Scaling Laws for Neural Language Models
概览: 一、总结 计算量、数据集大小、模型参数量大小的幂律 与 训练损失呈现 线性关系。 三个参数同时放大时,如何得到最佳的性能? 更大的模型 需要 更少的样本 就能达到相同的效果。 </...
【每日OJ —— 622. 设计循环队列】
每日OJ —— 622. 设计循环队列 1.题目:622. 设计循环队列2.解法2.1.解法讲解2.1.1.算法讲解2.1.2.代码实现2.1.3.提交通过展示 1.题目:622. 设计循环队列 2.解法 1.本题有很多解法:可以使用数组,单链表,双链表&#x…...
【Vue】生命周期一文详解
目录 一、beforeCreate 下面是一个beforeCreate的简单使用示例: 在控制台输出before create hook。 二、created 下面是一个created的简单使用示例: 在控制台输出created hook。 三、beforeMount 下面是一个beforeMount的简单使用示例࿱…...
JVM垃圾回收相关算法
目录 一、前言 二、标记阶段:引用计数算法 三、标记阶段:可达性分析算法 (一)基本思路 (二)GC Roots对象 四、对象的finalization机制 五、MAT与JProfiler的GC Roots溯源 六、清除阶段:…...
crontab 无法激活、启动 pyenv failed to activate virtualenv
root crontab 无法激活、启动 pyenv crontab代码 30 1 * * * sh /data/work/roop/sh/startSwapFaceDev.sh > /dev/null 2>&1 sh核心代码 echo "${YELLOW}pyenv activate ${venv} ${NOCOLOR}" eval "$(pyenv init -)" eval "$(pyenv v…...
系列八、key是弱引用,gc垃圾回收时会影响ThreadLocal正常工作吗
一、key是弱引用,gc垃圾回收时会影响ThreadLocal正常工作吗 到这里,有些小伙伴可能有疑问,ThreadLocalMap的key既然是 弱引用,那么GC时会不会贸然地把key回收掉,进而影响ThreadLocal的正常使用呢?答案是不会…...
pytorch中.to(device) 和.cuda()的区别
在PyTorch中,使用GPU加速可以显著提高模型的训练速度。在将数据传递给GPU之前,需要将其转换为GPU可用的格式。 函数原型如下: def cuda(self: T, device: Optional[Union[int, device]] None) -> T:return self._apply(lambda t: t.cuda…...
Mysql 递归查询子类Id的所有父类Id
文章目录 问题描述先看结果表结构展示实现递归查询集合查询结果修复数据 问题描述 最近开发过程中遇到一个问题,每次添加代理关系都要去递归查询一下它在不在这个代理关系树上.很麻烦也很浪费资源.想着把代理关系的父类全部存起来 先看结果 表结构展示 表名(t_agent_user_rela…...
设计模式 之单例模式
单例模式是一种创建型设计模式,它确保一个类只有一个实例,并提供全局访问点,使得该实例可以在程序的任何地方被访问。单例模式经常用于管理共享资源或限制对象创建数量的情况下。 实现一个单例模式需要注意以下几个关键点: 构造…...
ros2不同机器通讯时IP设置
看到这就是不同机器的IP地址,为了避免在路由器为不同的机器使用DHCP分配到上面的地址,可以设置DHCP分配的范围:(我的路由器是如下设置的,一般路由器型号都不一样,自己找一下) 防火墙设置-----&…...
Nginx模块开发之http过滤器filter
文章目录 什么是过滤模块Nginx相关数据结构介绍ngx_module_t的数据结构ngx_http_module_t数据结构ngx_command_s数据结构 相关宏定义filter(过滤器)实现Nginx模块开发流程Nginx 模块执行具体实现流程create_loc_confmerge_loc_confpostconfiguration修改…...
26 - 原型模式与享元模式:提升系统性能的利器
原型模式和享元模式,前者是在创建多个实例时,对创建过程的性能进行调优;后者是用减少创建实例的方式,来调优系统性能。这么看,你会不会觉得两个模式有点相互矛盾呢? 其实不然,它们的使用是分场…...
【Web安全】sqlmap的使用笔记及示例
【Web安全】sqlmap的使用笔记 文章目录 【Web安全】sqlmap的使用笔记1. 目标2. 脱库2.1. 脱库(补充) 3. 其他3.1. 其他(补充) 4. 绕过脚本tamper讲解 1. 目标 操作作用必要示例-u指定URL,检测注入点sqlmap -u http://…...
机器学习第12天:聚类
文章目录 机器学习专栏 无监督学习介绍 聚类 K-Means 使用方法 实例演示 代码解析 绘制决策边界 本章总结 机器学习专栏 机器学习_Nowl的博客-CSDN博客 无监督学习介绍 某位著名计算机科学家有句话:“如果智能是蛋糕,无监督学习将是蛋糕本体&a…...
若依框架导出下载pdf/excel以及导入打印等
一、打印文件 // 报表打印 handlePdf(row) {wayAPI(row.billcode).then((res) > {var binaryData [];binaryData.push(res);let url window.URL.createObjectURL(new Blob(binaryData, {type: "application/pdf"})); window.open("/static/pdf/web/v…...
汇编-PROC定义子过程(函数)
过程定义 过程用PROC和ENDP伪指令来声明, 并且必须为其分配一个名字(有效的标识符) 。目前为止, 我们所有编写的程序都包含了一个main过程, 例如: 当要创建的过程不是程序的启动过程时, 就用RET指令来结束它。RET强制…...
【2026最新】保姆级VMware安装Ubuntu24虚拟机教程(附安装包)
第一部分:为什么选择 Ubuntu 24.04 LTS? 在开始动手安装之前,让我们先了解一下我们即将迎来的这位“新朋友”——Ubuntu 24.04 LTS。 什么是 Ubuntu? Ubuntu(乌班图)是世界上最受欢迎的开源 Linux 操作系…...
零基础学AI编程之一 Claude Code安装保姆级教程
Claude 可以在终端运行(国内可用)、网页端(需登陆账号)、桌面端app(需登陆账号)和vscode插件的方式(国内可用)运行(4种) 如果是有Claude账号的ÿ…...
Siemens 6SC9811-4DA04转换器模块
SIEMENS 6SC9811-4DA04 是西门子 SIMODRIVE 系列中的一款高性能模块,在工业自动化系统中承担信号处理与控制功能。以下是综合整理的15条主要特点:中间15条特点:产品定位多样:有描述称为多重脉冲模块,用于处理多路输入脉…...
华三路由器NAT配置
本文详细介绍了H3C路由器的NAT配置,包括Basic NAT(一对一转换)、NAPT(一对多转换)和Easy IP配置。还讨论了公网主动访问私网所需的NAT Server配置,以及当公网地址不属于路由器接口地址网段时的静态路由设置…...
SAP ABAP开发避坑指南:COMMIT WORK和COMMIT WORK AND WAIT到底怎么选?
SAP ABAP开发实战:COMMIT WORK与COMMIT WORK AND WAIT的智能决策框架 在SAP ABAP开发中,数据提交操作的选择往往决定了系统的稳定性和业务数据的可靠性。许多开发者在面对COMMIT WORK和COMMIT WORK AND WAIT时,常常陷入两难:是追求…...
观察不同时段通过Taotoken调用大模型的延迟波动情况
观察不同时段通过Taotoken调用大模型的延迟波动情况 1. 测试环境与数据收集方法 我们基于实际业务场景,对Taotoken平台进行了为期两周的持续监测。测试环境采用Python编写的自动化脚本,每小时向平台发送固定内容的请求,记录每次调用的响应时…...
用ICode闯关游戏学Python:range函数的15个实战用法(附避坑指南)
用ICode闯关游戏学Python:range函数的15个实战用法(附避坑指南) 在编程学习的道路上,枯燥的语法常常成为初学者的绊脚石。而ICode国际青少年编程竞赛却为我们打开了一扇趣味学习的大门——通过游戏化的闯关模式,让Pyth…...
Sign in with Apple配置避坑指南:Service ID、回调地址和Key那些事儿
Sign in with Apple配置避坑指南:Service ID、回调地址和Key那些事儿 第一次接触Sign in with Apple的开发者,往往会被Apple开发者后台的各种ID和配置项搞得晕头转向。Service ID和App ID有什么区别?为什么我的回调地址总是报错?生…...
思维链(CoT)大模型必会技巧:让AI先思考再回答,准确率飙升!
思维链(CoT)是一种让大模型在输出答案前先展示推理过程的技术,能显著提升复杂问题的解决准确率。通过在提示中加入“让我们一步一步思考”等短语或提供带推理过程的示例,模型能更有效地处理数学计算、代码分析、法律合同判断等任务…...
循环水泵PLC数据采集监控管理系统方案
某大型工业园区配备了一套PLC自动化控制的循环冷却水系统,由6台循环水泵、3组冷却塔及配套管网组成,承担着园区内多个生产车间关键设备的冷却任务。然而,随着设备运行年限增长,客户面临一系列棘手问题:由于循环水泵站与…...
