WebSocket底层原理及 java 应用

news2025/1/8 14:52:38

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 {

    // 当客户端连接时调用
    @OnOpen
    public void onOpen(Session session) {
        System.out.println("Client connected: " + session.getId());
    }

    // 当接收到客户端的消息时调用
    @OnMessage
    public void onMessage(String message, Session session) {
        System.out.println("Message received: " + message);
        try {
            // 向客户端发送消息
            session.getBasicRemote().sendText("Echo: " + message);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // 当客户端断开连接时调用
    @OnClose
    public void onClose(Session session) {
        System.out.println("Client disconnected: " + session.getId());
    }

    // 当发生错误时调用
    @OnError
    public 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();
        }
    }

    @OnOpen
    public void onOpen(Session session) {
        System.out.println("Connected to server: " + session.getId());
        this.session = session;
    }

    @OnMessage
    public void onMessage(String message) {
        System.out.println("Received message: " + message);
    }

    @OnClose
    public void onClose() {
        System.out.println("Disconnected from server");
    }

    @OnError
    public 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<>();

    @OnOpen
    public void onOpen(Session session

) {
        players.add(session);
        System.out.println("New player connected: " + session.getId());
    }

    @OnMessage
    public 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();
        }
    }

    @OnClose
    public void onClose(Session session) {
        players.remove(session);
        System.out.println("Player disconnected: " + session.getId());
    }

    @OnError
    public 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.coloradmin.cn/o/2273269.html

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

相关文章

【JVM】总结篇-类的加载篇之 类的加载器 和ClassLoader分析

文章目录 类的加载器ClassLoader自定义类加载器双亲委派机制概念源码分析优势劣势如何打破Tomcat 沙箱安全机制JDK9 双亲委派机制变化 类的加载器 获得当前类的ClassLoader clazz.getClassLoader() 获得当前线程上下文的ClassLoader Thread.currentThread().getContextClassLoa…

java 转义 反斜杠 Unexpected internal error near index 1

代码&#xff1a; String str"a\\c"; //出现异常&#xff0c;Unexpected internal error near index 1 //System.out.println(str.replaceAll("\\", "c"));//以下三种都正确 System.out.println(str.replace(\\, c)); System.out.println(str.r…

el-table 实现纵向多级表头

为了实现上图效果&#xff0c;最开始打算用el-row、el-col去实现&#xff0c;但发现把表头和数据分成两大列时&#xff0c;数据太多时会导致所在格高度变高。但由于每一格数据肯定不一样&#xff0c;为保持高度样式一致&#xff0c;就需要我们手动去获取最高格的高度之后再设置…

2025最新版Visual Studio Code安装使用指南

2025最新版Visual Studio Code安装使用指南 Installation and Usage Guide for the Latest Visual Studio Code in 2024 By JacksonML 2025-1-7 1. Visual Studio Code背景 早在二十年前&#xff0c;通用的集成开发环境&#xff08;Integrated Deveopment Environment, 简称…

Flutter 鸿蒙化 flutter和鸿蒙next混和渲染

前言导读 这一个节课我们讲一下PlatformView的是使用 我们在实战中有可能出现了在鸿蒙next只加载一部分Flutter的情况 我们今天就讲一下这种情况具体实现要使用到我们的PlatformView 效果图 具体实现: 一、Native侧 使用 DevEco Studio工具打开 platform_view_example\oho…

LabVIEW语言学习过程是什么?

学习LabVIEW语言的过程可以分为几个阶段&#xff0c;每个阶段的重点内容逐步加深&#xff0c;帮助你从入门到精通。以下是一个简洁的学习过程&#xff1a; ​ 1. 基础入门阶段 理解图形化编程&#xff1a;LabVIEW是一种图形化编程语言&#xff0c;与传统的文本编程语言不同&am…

Kubernetes Gateway API-4-TCPRoute和GRPCRoute

1 TCPRoute 目前 TCP routing 还处于实验阶段。 Gateway API 被设计为与多个协议一起工作&#xff0c;TCPRoute 就是这样一个允许管理TCP流量的路由。 在这个例子中&#xff0c;我们有一个 Gateway 资源和两个 TCPRoute 资源&#xff0c;它们按照以下规则分配流量&#xff1…

嵌入式SD/TF卡通用协议-SDIO协议

SD卡&#xff08;SecureDigital MemoryCard&#xff09;即&#xff1a;安全数码卡&#xff0c;它是在MMC的基础上发展而来&#xff0c;是一种基于半导体快闪记忆器的新一代记忆设备&#xff0c;它被广泛地于便携式装置上使用&#xff0c;例如数码相机、个人数码助理(PDA)和多媒…

性能测试05|JMeter:分布式、报告、并发数计算、性能监控

目录 一、JMeter分布式 1、应用场景 2、原理 3、分布式相关注意事项 4、分布式配置与运行 二、JMeter报告 1、聚合报告 2、HTML报告 三、并发用户数&#xff08;线程数&#xff09;计算 四、JMeter下载第三方插件 五、性能监控 1、Concurrency Thread Group 线程组…

wujie无界微前端框架初使用

先说一下项目需求&#xff1a;将单独的四套系统的登录操作统一放在一个入口页面进行登录&#xff0c;所有系统都使用的是vue3&#xff0c;&#xff08;不要问我为啥会这样设计&#xff0c;产品说的客户要求&#xff09; 1.主系统下载wujie 我全套都是vue3&#xff0c;所以直接…

SpringIOC循环依赖与三级缓存

SpringIOC循环依赖与三级缓存 Spring解决循环依赖的核心机制就是通过三级缓存&#xff1a; 一级缓存&#xff08;singletonObjects&#xff09;&#xff1a;存储完全初始化好的Bean&#xff1b;二级缓存&#xff08;earlySingletonObjects&#xff09;&#xff1a;存储原始实例…

【顶刊TPAMI 2025】多头编码(MHE)之极限分类 Part 3:算法实现

目录 1 三种多头编码&#xff08;MHE&#xff09;实现1.1 多头乘积&#xff08;MHP&#xff09;1.2 多头级联&#xff08;MHC&#xff09;1.3 多头采样&#xff08;MHS&#xff09;1.4 标签分解策略 论文&#xff1a;Multi-Head Encoding for Extreme Label Classification 作者…

前端 图片上鼠标画矩形框,标注文字,任意删除

效果&#xff1a; 页面描述&#xff1a; 对给定的几张图片&#xff0c;每张能用鼠标在图上画框&#xff0c;标注相关文字&#xff0c;框的颜色和文字内容能自定义改变&#xff0c;能删除任意画过的框。 实现思路&#xff1a; 1、对给定的这几张图片&#xff0c;用分页器绑定…

【办公利器】ReNamer (批量文件重命名工具) Pro v7.6.0.4 多语便携版,海量文件秒速精准改名!

ReNamer是一款功能强大的文件重命名工具&#xff0c;它可以帮助用户快速方便地批量重命名文件和文件夹。 软件功能 批量重命名&#xff1a;ReNamer可以同时处理多个文件和文件夹&#xff0c;并对其进行批量重命名&#xff0c;从而节省时间和劳动力。灵活的重命名规则&#xff…

unity学习13:gameobject的组件component以及tag, layer 归类

目录 1 gameobject component 是unity的基础 1.1 类比 1.2 为什么要这么设计&#xff1f; 2 从空物体开始 2.1 创建2个物体 2.2 给 empty gameobject添加组件 3 各种组件和新建组件 3.1 点击 add component可以添加各种组件 3.2 新建组件 3.3 组件的操作 3.4 特别的…

数据库模型全解析:从文档存储到搜索引擎

目录 前言1. 文档存储&#xff08;Document Store&#xff09;1.1 概念与特点1.2 典型应用1.3 代表性数据库 2. 图数据库&#xff08;Graph DBMS&#xff09;2.1 概念与特点2.2 典型应用2.3 代表性数据库 3. 原生 XML 数据库&#xff08;Native XML DBMS&#xff09;3.1 概念与…

CSS——1.优缺点

<!DOCTYPE html> <html><head><meta charset"UTF-8"><title></title><link rel"stylesheet" type"text/css" href"1-02.css"/></head><body><!--css&#xff1a;层叠样式表…

UE5本地化和国际化语言

翻译语言 工具 - 本地化控制板 Localization Dashboard 修改图中这几个地方就可以 点击箭头处&#xff0c;把中文翻译成英语&#xff0c;如果要更多语言就点 添加新语言 最后点击编译即可 编译完&#xff0c;会在目录生成文件夹 设置界面相关蓝图中设置 切换本地化语言 必须在…

python学习笔记—15—数据容器之列表

1. 数据容器 列表(list)、元组(tuple)、字符串(str)、集合(set)、字典(dict) 2. 列表 (1) 定义 tmp_list ["super", "carry", "doinb"] print(f"tmp_list {tmp_list}, tmp_list type is {type(tmp_list)}") tmp_list1 ["doi…

【简博士统计学习方法】第1章:4. 模型的评估与选择

4. 模型的评估与选择 4.1 训练误差与测试误差 假如存在样本容量为 N N N的训练集&#xff0c;将训练集送入学习系统可以训练学习得到一个模型&#xff0c;我们将这么模型用决策函数的形式表达&#xff0c;也就是 y f ^ ( x ) y\hat{f}(x) yf^​(x)&#xff0c;关于模型的拟合…