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.rhkb.cn/news/503561.html

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

相关文章

JVM vs JDK vs JRE

JVM是Java虚拟机的缩写&#xff0c; 用于实现Java的一次编译&#xff0c;处处运行。 Java代码写成.class后&#xff0c;由本地的虚拟机运行。 JDK&#xff08;Java Development Kit&#xff09;是一个功能齐全的 Java 开发工具包&#xff0c;供开发者使用。 JDK包含了JRE。…

Android修改开机动画路径

frameworks\base\cmds\bootanimation\BootAnimation.cpp 路径的定义 优先查找的顺序

select下拉框,首次进入页面没有显示value的情况

bug场景&#xff1a; 类似这种bug情况排查如下&#xff1a; 首先 理解含义 options就是存放键值对的&#xff0c;id就是key&#xff0c;对上了它就自动把label显示 而且如果你用来当作key和label的字段&#xff0c;与后端返回的不一致&#xff0c;还可以进行更改 其次 排查接…

Redis中的主从/Redis八股

四、Redis主从 1.搭建主从架构 不像是负载均衡&#xff0c;这里是主从&#xff0c;是因为redis大多数是读少的是写 步骤 搭建实例&#xff08;建设有三个实例&#xff0c;同一个ip不同端口号&#xff09; 1&#xff09;创建目录 我们创建三个文件夹&#xff0c;名字分别叫700…

Mysql--基础篇--函数(字符串函数,日期函数,数值函数,聚合函数,自定义函数及与存储过程的区别等)

MySQL提供了丰富的内置函数&#xff0c;涵盖了字符串处理、数值计算、日期和时间操作、聚合统计、控制流等多种功能。这些函数可以帮助你简化SQL查询&#xff0c;提升开发效率。 除了内置函数&#xff0c;MySQL还支持自定义函数&#xff08;User-Defined Functions&#xff09;…

【linux系统之redis6】redis的安装与初始化

下载redis的linux对应的安装包&#xff0c;并上传到linux虚拟机里面 解压压缩包 tar -zxzf redis-6.2.6.tar.gz解压后&#xff0c;进入redis文件 cd redis-6.2.6执行编译 make && make install看到下图&#xff0c;就说明redis安装成功了 默认的安装路径&#xff0c…

怎么管理电脑usb接口,分享四种USB端口管理方法

怎么管理电脑usb接口&#xff0c;分享四种USB端口管理方法 USB接口作为电脑重要的外部接口&#xff0c;方便了数据传输和设备连接。 然而&#xff0c;不加管理的USB接口也可能带来安全隐患&#xff0c;例如数据泄露、病毒传播等。 因此&#xff0c;有效管理电脑USB接口至关重…

[开源]自动化定位建图系统

系统状态机&#xff1a; 效果展示&#xff1a; 1、 机器人建图定位系统-基础重定位&#xff0c;定位功能演示 2、 机器人建图定位系统-增量地图构建&#xff0c;手动回环检测演示 3、… 开源链接&#xff1a; https://gitee.com/li-wenhao-lwh/lifelong-backend Qt人机交互…

重新整理机器学习和神经网络框架

本篇重新梳理了人工智能&#xff08;AI&#xff09;、机器学习&#xff08;ML&#xff09;、神经网络&#xff08;NN&#xff09;和深度学习&#xff08;DL&#xff09;之间存在一定的包含关系&#xff0c;以下是它们的关系及各自内容,以及人工智能领域中深度学习分支对比整理。…

PyTorch 框架实现线性回归:从数据预处理到模型训练全流程

系列文章目录 01-PyTorch新手必看&#xff1a;张量是什么&#xff1f;5 分钟教你快速创建张量&#xff01; 02-张量运算真简单&#xff01;PyTorch 数值计算操作完全指南 03-Numpy 还是 PyTorch&#xff1f;张量与 Numpy 的神奇转换技巧 04-揭秘数据处理神器&#xff1a;PyTor…

Elasticsearch:优化的标量量化 - 更好的二进制量化

作者&#xff1a;来自 Elastic Benjamin Trent 在这里&#xff0c;我们解释了 Elasticsearch 中的优化标量量化以及如何使用它来改进更好的二进制量化 (Better Binary Quantization - BBQ)。 我们的全新改进版二进制量化 (Better Binary Quantization - BBQ) 索引现在变得更强大…

科普CMOS传感器的工作原理及特点

在当今数字化成像的时代&#xff0c;图像传感器无疑是幕后的关键 “功臣”&#xff0c;它宛如一位神奇的 “光影魔法师”&#xff0c;通过光电效应这一奇妙的物理现象&#xff0c;将光子巧妙地转换成电荷&#xff0c;为图像的诞生奠定基础。而在众多类型的图像传感器中&#xf…

IDEA中Maven依赖包导入失败报红的潜在原因

在上网试了别人的八个问题总结之后依然没有解决&#xff1a; IDEA中Maven依赖包导入失败报红问题总结最有效8种解决方案_idea导入依赖还是报红-CSDN博客https://blog.csdn.net/qq_43705131/article/details/106165960 江郎才尽之后突然想到一个原因&#xff1a;<dep…

Java100道面试题

1.JVM内存结构 1. 方法区&#xff08;Method Area&#xff09; 方法区是JVM内存结构的一部分&#xff0c;用于存放类的相关信息&#xff0c;包括&#xff1a; 类的结构&#xff08;字段、方法、常量池等&#xff09;。字段和方法的描述&#xff0c;如名称、类型、访问修饰符…

虚表 —— 隐藏行(简单版)

因为隐藏行改变了listview内部行号处理机制&#xff0c;需要处理大量细节&#xff0c;如listview内部用于传递行号的各种消息、通知等、封装的各种读取行号的函数等。 所以在工作量很大&#xff0c;一处纰漏可能导致重大bug的情况下&#xff0c;仅对隐藏行功能进行了简单封装&…

UDP -- 简易聊天室

目录 gitee&#xff08;内有详细代码&#xff09; 图解 MessageRoute.hpp UdpClient.hpp UdpServer.hpp Main.hpp 运行结果&#xff08;本地通信&#xff09; 如何分开对话显示&#xff1f; gitee&#xff08;内有详细代码&#xff09; chat_room zihuixie/Linux_Lear…

python制作翻译软件

本文复刻此教程&#xff1a;制作属于自己的翻译软件-很简单【Python】_哔哩哔哩_bilibili 一、明确需求&#xff08;以搜狗翻译为例&#xff09; &#xff08;1&#xff09;网址&#xff1a;https://fanyi.sogou.com/text &#xff08;2&#xff09; 数据&#xff1a;翻译内容…

uni-app 资源引用(绝对路径和相对路径)方法汇总

文章目录 一、前言&#x1f343;二、绝对路径和相对路径2.1 绝对路径2.2 相对路径 三、引用组件四、引用js4.1 js 文件引入4.2 NPM支持 五、引用css六、引用json6.1 json文件引入 七、引用静态资源7.1 模板内引入静态资源7.2 css 引入静态资源7.3 js/uts 引入静态资源7.4 静态资…

在 ASP.NET CORE 中上传、下载文件

创建 Web API 来提供跨客户端和服务器的文件上传和下载是常有的事。本文将介绍如何通过 ASP.NET CORE 来实现。 首先在 Visual Studio 中创建空的 Web API 项目&#xff0c;然后选择目标框架 .Net Core 3.1。 创建名为 FileController 的控制器&#xff0c;提供操作文件的接口…

基于 GEE Sentinel-1 数据集提取水体

目录 1 水体提取原理 2 完整代码 3 运行结果 1 水体提取原理 水体提取是地理信息和遥感技术的关键应用之一&#xff0c;对于多个领域都具有重要的应用价值。它有助于更好地管理水资源&#xff0c;保护环境&#xff0c;减少灾害风险&#xff0c;促进可持续发展&#xff0c;以…