WebSocket底层原理及 java 应用

WebSocket 底层原理

1. WebSocket 协议的基本原理

WebSocket 是一个在客户端和服务器之间建立持久、全双工的连接的协议。与传统的 HTTP 请求/响应模型不同,WebSocket 允许客户端和服务器双方通过一个持久的连接进行双向通信。

1.1 WebSocket 握手过程

WebSocket 握手是一个基于 HTTP 的协议升级过程。以下是详细步骤:

  1. 客户端发起请求
    客户端向服务器发送一个 HTTP 请求,包含一个 Upgrade 头部,表明想要将连接从 HTTP 协议切换到 WebSocket 协议。

    示例请求:

    GET /chat HTTP/1.1
    Host: example.com
    Upgrade: websocket
    Connection: Upgrade
    Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
    Sec-WebSocket-Version: 13
    

    关键字段:

    • Upgrade:请求将协议切换为 WebSocket。
    • Sec-WebSocket-Key:客户端生成的一个随机 Base64 编码值,用于验证服务器响应。
    • Sec-WebSocket-Version:WebSocket 协议的版本号。
  2. 服务器回应
    服务器收到请求后,如果支持 WebSocket 协议并同意建立连接,会返回一个 HTTP 101 状态码,表示协议升级成功。

    示例响应:

    HTTP/1.1 101 Switching Protocols
    Upgrade: websocket
    Connection: Upgrade
    Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
    

    关键字段:

    • Sec-WebSocket-Accept:通过将客户端发送的 Sec-WebSocket-Key 与特定的 GUID 进行处理,计算出的一个值,用于确认客户端的请求是否合法。
  3. 连接建立后,数据交换
    一旦握手完成,客户端和服务器之间就建立了一个持久的 WebSocket 连接,双方可以在该连接上任意时刻发送和接收消息。这个过程基于 WebSocket 数据帧(Frame)进行。

    WebSocket 数据帧:

    • 每个数据帧包含一个固定的头部,之后是数据内容(例如文本消息、二进制数据)。
    • 数据帧的头部包含了帧的类型、数据长度、是否加密等信息。
1.2 WebSocket 数据传输

WebSocket 使用帧(Frame)来传输数据。每个数据帧的格式如下:

字段长度描述
FIN, RSV1-31 Byte控制位,表示数据帧是否结束以及是否有扩展数据
Opcode1 Byte操作码,标识帧的类型(如文本帧、二进制帧等)
Mask1 Byte是否启用了掩码(客户端必须启用掩码)
Payload Length1-8 Bytes数据帧的长度(实际有效数据长度)
Masking-Key4 Bytes客户端发送数据时的掩码密钥(如果 Mask 为 1)
Payload DataN Bytes数据帧的实际数据
  • 文本数据:对于文本数据,WebSocket 使用 UTF-8 编码进行传输。
  • 二进制数据:WebSocket 也支持二进制数据传输,例如图像文件、音频流等。

1.3 客户端和服务器的双向通信

  • 客户端到服务器:客户端可以通过 send() 方法将数据发送到服务器。
  • 服务器到客户端:服务器通过 WebSocket 会话(Session)发送消息。

这种全双工通信模型保证了客户端和服务器之间能够即时、连续地交换数据,消除了 HTTP 的请求/响应延迟。


Java 使用 WebSocket 示例

2.1 WebSocket 服务端代码

我们可以使用 Java 进行 WebSocket 服务端开发。Java EE 提供了 @ServerEndpoint 注解来标识 WebSocket 端点,接收和发送消息。

以下是一个简单的 WebSocket 服务端示例:

2.1.1 服务端代码(ChatServer.java)
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;@ServerEndpoint("/chat")
public class ChatServer {// 当客户端连接时调用@OnOpenpublic void onOpen(Session session) {System.out.println("Client connected: " + session.getId());}// 当接收到客户端的消息时调用@OnMessagepublic void onMessage(String message, Session session) {System.out.println("Message received: " + message);try {// 向客户端发送消息session.getBasicRemote().sendText("Echo: " + message);} catch (IOException e) {e.printStackTrace();}}// 当客户端断开连接时调用@OnClosepublic void onClose(Session session) {System.out.println("Client disconnected: " + session.getId());}// 当发生错误时调用@OnErrorpublic void onError(Session session, Throwable throwable) {System.out.println("Error occurred: " + throwable.getMessage());throwable.printStackTrace();}
}
2.1.2 配置与部署

要使 WebSocket 服务端正常运行,需要在 web.xml 中配置 WebSocket 端点。

<servlet><servlet-name>WebSocketServlet</servlet-name><servlet-class>org.glassfish.tyrus.servlet.TyrusServlet</servlet-class>
</servlet><servlet-mapping><servlet-name>WebSocketServlet</servlet-name><url-pattern>/chat/*</url-pattern>
</servlet-mapping>

这将使得客户端可以通过 /chat 路径与服务器建立连接。

2.2 WebSocket 客户端代码

Java WebSocket 客户端可以通过 WebSocketContainer 创建与服务器的连接,发送和接收消息。

2.2.1 客户端代码(ChatClient.java)
import javax.websocket.*;
import java.net.URI;@ClientEndpoint
public class ChatClient {private Session session;public void connectToServer() {try {WebSocketContainer container = ContainerProvider.getWebSocketContainer();container.connectToServer(this, new URI("ws://localhost:8080/chat"));} catch (Exception e) {e.printStackTrace();}}@OnOpenpublic void onOpen(Session session) {System.out.println("Connected to server: " + session.getId());this.session = session;}@OnMessagepublic void onMessage(String message) {System.out.println("Received message: " + message);}@OnClosepublic void onClose() {System.out.println("Disconnected from server");}@OnErrorpublic void onError(Throwable error) {error.printStackTrace();}public void sendMessage(String message) {try {session.getBasicRemote().sendText(message);} catch (Exception e) {e.printStackTrace();}}public static void main(String[] args) {ChatClient client = new ChatClient();client.connectToServer();client.sendMessage("Hello, WebSocket server!");}
}

2.3 运行 WebSocket 服务

  1. 启动服务器并运行 ChatServer.java
  2. 启动客户端 ChatClient.java,客户端将连接到服务端并发送消息。
  3. 服务端将接收到的消息原样返回。

前后端基于 WebSocket 的答题游戏开发示例

我们可以用 WebSocket 实现一个简单的答题游戏,其中客户端向服务器发送答案,服务器根据答案判断是否正确,并实时更新得分。

3.1 游戏流程

  1. 客户端:显示问题并接收用户输入的答案。
  2. 服务器端:接收答案并返回是否正确,更新得分。
  3. 实时反馈:每个用户的得分通过 WebSocket 即时反馈给客户端。

3.2 游戏前后端实现

3.2.1 服务端实现(GameServer.java)
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.CopyOnWriteArrayList;@ServerEndpoint("/game")
public class GameServer {// 存储所有连接的玩家会话private static final CopyOnWriteArrayList<Session> players = new CopyOnWriteArrayList<>();@OnOpenpublic void onOpen(Session session) {players.add(session);System.out.println("New player connected: " + session.getId());}@OnMessagepublic void onMessage(String message, Session session) {System.out.println("Received answer: " + message);// 假设问题的正确答案是 "42"String response = message.equals("42") ? "Correct!" : "Wrong!";try {session.getBasicRemote().sendText(response);} catch (IOException e) {e.printStackTrace();}}@OnClosepublic void onClose(Session session) {players.remove(session);System.out.println("Player disconnected: " + session.getId());}@OnErrorpublic void onError(Throwable error) {error.printStackTrace();}
}
3.2.2 客户端实现(GameClient.html)

前端使用简单的 HTML 和 JavaScript 通过 WebSocket 与后端进行通信。

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>答题游戏</title><script>var socket;function connect() {socket = new WebSocket("ws://localhost:8080/game");socket.onopen = function () {console.log("Connected to server");};socket.onmessage = function (event) {document.getElementById("response").innerText = event.data;};socket.onclose = function () {console.log("Disconnected from server");};}function sendAnswer() {var answer = document.getElementById("answer").value;socket.send(answer);}</script>
</head>
<body onload="connect()"><h1>答题游戏</h1><p>问题:是什么是答案?</p><input type="text" id="answer" placeholder="输入你的答案"><button onclick="sendAnswer()">提交</button><p id="response"></p>
</body>
</html>

3.3 游戏流程

  1. 客户端:加载 HTML 页面,连接到 WebSocket 服务器。
  2. 客户端输入答案并点击提交。
  3. 服务器:判断答案是否正确并返回反馈。
  4. 客户端:接收反馈并显示给玩家。

总结

通过 WebSocket,Java 可以高效地实现实时通信。在实际应用中,WebSocket 适用于那些需要双向、低延迟通信的场景,比如实时游戏、聊天应用、实时数据监控等。通过结合前后端的 WebSocket 使用,我们可以快速开发出高互动、低延迟的应用。

这个简单的答题游戏示例展示了如何使用 WebSocket 实现前后端实时数据交换。在实际项目中,你可以扩展更多功能,例如计时器、多人游戏、动态问题和答案等。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/66651.shtml

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

相关文章

数据结构:LinkedList与链表—面试题(三)

目录 1、移除链表元素 2、反转链表 3、链表的中间结点 4、返回倒数第k个结点 5、合并两个有序链表 1、移除链表元素 习题链接https://leetcode.cn/problems/remove-linked-list-elements/description/ 描述&#xff1a;给你一个链表的头节点 head 和一个整数 val &#xff…

使用 SQL 和表格数据进行问答和 RAG(1)—数据库准备

一. 从 .sql/csv/xlsx 文件创建 sqlite 数据库。 要从.sql文件准备 SQL DB&#xff0c;这里会将创建数据库的代码放到了&#xff0c;将文件复制到data/sql目录中&#xff0c;然后在终端中的项目文件夹中执行&#xff1a; pip install sqlite3现在创建一个名为sqldb的数据库&a…

案例研究:UML用例图中的结账系统

在软件工程和系统分析中&#xff0c;统一建模语言&#xff08;UML&#xff09;用例图是一种强有力的工具&#xff0c;用于描述系统与其用户之间的交互。本文将通过一个具体的案例研究&#xff0c;详细解释UML用例图的关键概念&#xff0c;并说明其在设计结账系统中的应用。 用…

EasyExcel上传校验文件错误信息放到文件里以Base64 返回给前端

产品需求&#xff1a; 前端上传个csv 或 excel 文件&#xff0c;文件共4列&#xff0c;验证文件大小&#xff0c;类型&#xff0c;文件名长度&#xff0c;文件内容&#xff0c;如果某行某个单元格数据验证不通过&#xff0c;就把错误信息放到这行第五列&#xff0c;然后把带有…

机器学习免费使用的数据集及网站链接

机器学习领域存在许多可以免费使用的数据集&#xff0c;这些数据集来自于学习、研究、比赛等目的。 一、综合性数据集平台 1.Kaggle 网址&#xff1a;Kaggle 数据集https://www.kaggle.com/datasets Kaggle是一个数据科学竞赛和社区平台&#xff0c;提供了大量的数据集供用…

Nacos 3.0 Alpha 发布,在安全、泛用、云原生更进一步

自 2021 年发布以来&#xff0c;Nacos 2.0 在社区的支持下已走过近三年&#xff0c;期间取得了诸多成就。在高性能与易扩展性方面&#xff0c;Nacos 2.0 取得了显著进展&#xff0c;同时在易用性和安全性上也不断提升。想了解更多详细信息&#xff0c;欢迎阅读我们之前发布的回…

IP查询于访问控制保护你我安全

IP地址查询 查询方法&#xff1a; 命令行工具&#xff1a; ①在Windows系统中&#xff0c;我们可以使用命令提示符&#xff08;WINR&#xff09;查询IP地址&#xff0c;在弹窗中输入“ipconfig”命令查看本地网络适配器的IP地址等配置信息&#xff1b; ②在Linux系统中&…

解决 ssh connect to host github.com port 22 Connection timed out

一、问题描述 本地 pull/push 推送代码到 github 项目报 22 端口连接超时&#xff0c;测试连接也是 22 端口连接超时 ssh 密钥没问题、也开了 Watt Toolkit 网络是通的&#xff0c;因此可以强制将端口切换为 443 二、解决方案 1、测试连接 ssh -T gitgithub.com意味着无法通…

如何在Windows 11 WSL2 Ubuntu 环境下安装和配置perf性能分析工具?

在Windows 11 WSL2 Ubuntu 环境下完整安装和配置perf性能分析工具 一、背景二、准备工作三、获取并编译Linux内核源码四、安装和配置perf五、测试perf六、总结 一、背景 由于WSL2使用的是微软定制的内核&#xff0c;并非标准的Ubuntu内核&#xff0c;因此直接使用apt安装linux…

NOVA:AutoRegressive Video Generation Without Vector Quantization——自回归视频生成无需向量量化

这篇文章介绍了一种名为NOVA的新型自回归模型&#xff0c;用于高效的文本到图像和文本到视频生成。以下是文章的主要内容总结&#xff1a; 1. 研究背景与问题 自回归大语言模型&#xff08;LLMs&#xff09;在自然语言处理&#xff08;NLP&#xff09;中表现出色&#xff0c;但…

eNSP之家——路由器--入门实例详解

eNSP路由器配置&#xff1a;IP、DHCP与DNS详解-CSDN博客 练习1&#xff1a;两个路由器配置ip地址&#xff0c;并用ping命令测试连通性。 打开ensp&#xff0c;拉进来两个路由器AR2220,再用auto连接两个路由器。 选中两个路由器&#xff0c;右键启动&#xff0c;等待半分钟路由…

Spring 设计模式:经典设计模式

Spring 设计模式&#xff1a;经典设计模式 引言 Spring 框架广泛使用了经典设计模式。 这些模式在 Spring 内部发挥着重要作用。 通过理解这些设计模式在 Spring 中的应用&#xff0c;开发者可以更深入地掌握 Spring 框架的设计哲学和实现细节。 经典设计模式 控制反转&am…

【HarmonyOS NEXT】鸿蒙应用点9图的处理(draw9patch)

【HarmonyOS NEXT】鸿蒙应用点9图的处理&#xff08;draw9patch&#xff09; 一、前言&#xff1a; 首先在鸿蒙中是不支持安卓 .9图的图片直接使用。只有类似拉伸的处理方案&#xff0c;鸿蒙提供的Image组件有与点九图相同功能的API设置。 可以通过设置resizable属性来设置R…

STM32-笔记39-SPI-W25Q128

一、什么是SPI&#xff1f; SPI是串行外设接口&#xff08;Serial Peripheral Interface&#xff09;的缩写&#xff0c;是一种高速的&#xff0c;全双工&#xff0c;同步的通信总线&#xff0c;并且 在芯片的管脚上只占用四根线&#xff0c;节约了芯片的管脚&#xff0c;同时为…

【微服务】8、分布式事务 ( XA 和 AT )

文章目录 利用Seata解决分布式事务问题&#xff08;XA模式&#xff09;AT模式1. AT模式原理引入2. AT模式执行流程与XA模式对比3. AT模式性能优势及潜在问题4. AT模式数据一致性解决方案5. AT模式一阶段操作总结6. AT模式二阶段操作分析7. AT模式整体特点8. AT模式与XA模式对比…

CTF知识点总结(三)

空格绕过方式&#xff1a; $IFS ${IFS} $IFS$数字 < <> 三种绕过方式&#xff1a; 1.sh /?ip127.0.0.1;echo$IFS$2Y2F0IGZsYWcucGhw|base64$IFS$2-d|sh 2.变量拼接 /?ip127.0.0.1;ag;cat$IFS$2fla$a.php 3.内联注释(将反引号命令的结果作为输入来执行命令) /?i…

《Spring Framework实战》5:Spring Framework 概述

欢迎观看《Spring Framework实战》视频教程 Spring 使创建 Java 企业应用程序变得容易。它为您提供一切 需要在企业环境中采用 Java 语言&#xff0c;并支持 Groovy 和 Kotlin 作为 JVM 上的替代语言&#xff0c;并且可以灵活地创建许多 类型的架构。从 Spring Framework 6.0 开…

解决npm报错:sill idealTree buildDeps

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl 报错信息 使用 npm 安装依赖时报错&#xff1a;sill idealTree buildDeps 解决方案 请按照以下步骤进行相关操作&#xff1a; 1、删除 C:\Users{账户}\ 文件夹中的 .npm…

formik 的使用

礼记有言&#xff1a;独学而无友&#xff0c;则孤陋而寡闻 让我们一起了解更多便捷方法&#xff0c;缩短开发时间去摸鱼&#xff0c;嘿嘿。 框架&#xff1a;react 在写表单的时候&#xff0c;我不太喜欢把验证写的很繁琐&#xff0c;这里讲介绍&#xff0c;验证表单的非常好用…

JVM实战—OOM的生产案例

1.每秒仅上百请求的系统为何会OOM(RPC超时时间设置过长导致QPS翻几倍) (1)案例背景 在这个案例中&#xff0c;一个每秒仅仅只有100请求的系统却因频繁OOM而崩溃。这个OOM问题会涉及&#xff1a;Tomcat底层工作原理、Tomcat内核参数的设置、服务请求超时时间。 (2)系统发生OOM的…