websocket的介绍及springBoot集成示例

news2024/11/26 8:22:06

目录

一、什么是Websocket

二、Websocket特点

三、WebSocket与HTTP的区别

四、常见应用场景

五、SpringBoot集成WebSocket

1. 原生注解

2. Spring封装


一、什么是Websocket

        WebSocket 是一种在单个 TCP 连接上进行 全双工 通信的协议,它可以让客户端和服务器之间进行实时的双向通信。WebSocket 使用一个长连接,在客户端和服务器之间保持持久的连接,从而可以实时地发送和接收数据。

        在 WebSocket 中,客户端和服务器之间可以互相发送消息,客户端可以使用 JavaScript 中的 WebSocket API 发送消息到服务器,也可以接收服务器发送的消息。

二、Websocket特点


        简单来说,websocket 具有双向通信,实时性强,支持二进制,控制开销的特点。

  1. 协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。
  2. 实时通信,服务器可以随时主动给客户端下发数据。
  3. 保持连接状态,Websocket需要先创建连接,所以是一种有状态的协议,之后通信时就可以省略部分状态信息。
  4. 控制开销,连接创建后,服务器和客户端之间交换数据时,用于协议控制的数据包头部相对较小。在不包含扩展的情况下,对于服务器到客户端的内容,此头部大小只有2至10字节(和数据包长度有关);对于客户端到服务器的内容,头部还需要加上额外的4字节的掩码。
  5. 实现简单,建立在 TCP 协议之上,服务器端的实现比较容易,并且没有同源限制,客户端可以与任意服务器通信。
  6. 支持二进制传输,Websocket定义了二进制帧,可以发送文本,也可以发送二进制数据。
  7. 与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。
  8. 支持扩展,用户可以扩展协议、实现部分自定义的子协议,如部分浏览器支持压缩等。

三、WebSocket与HTTP的区别


        websocket和http都是基于TCP的应用层协议,使用的也是 80 端口(若运行在 TLS 之上时,默认使用 443 端口)。

        其区别主要就在于连接的性质和通信方式。

        WebSocket是一种双向通信的协议,通过一次握手即可建立持久性的连接,服务器和客户端可以随时发送和接收数据。而HTTP协议是一种请求-响应模式的协议,每次通信都需要发送一条请求并等待服务器的响应。

        WebSocket的实时性更好,延迟更低,并且在服务器和客户端之间提供双向的即时通信能力,适用于需要实时数据传输的场景。

四、常见应用场景

  1. 实时聊天:WebSocket能够提供双向、实时的通信机制,使得实时聊天应用能够快速、高效地发送和接收消息,实现即时通信。
  2. 实时协作:用于实时协作工具,如协同编辑文档、白板绘画、团队任务管理等,团队成员可以实时地在同一页面上进行互动和实时更新。
  3. 实时数据推送:用于实时数据推送场景,如股票行情、新闻快讯、实时天气信息等,服务器可以实时将数据推送给客户端,确保数据的及时性和准确性。
  4. 多人在线游戏:实时的双向通信机制,适用于多人在线游戏应用,使得游戏服务器能够实时地将游戏状态和玩家行为传输给客户端,实现游戏的实时互动。
  5. 在线客服:WebSocket可以用于在线客服和客户支持系统,实现实时的客户沟通和问题解决,提供更好的用户体验,减少等待时间。

五、SpringBoot集成WebSocket

引入依赖

<dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-websocket</artifactId>
 </dependency>

1. 原生注解

WebSocketConfig 

package com.cjian.websocket.annotation;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

//开启WebSocket的支持,并把该类注入到spring容器中
@Configuration
@EnableWebSocket
public class WebSocketConfig {

    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }

}

说明:

这个配置类很简单,通过这个配置 spring boot 才能去扫描后面的关于 websocket 的注解

WsServerEndpoint

package com.cjian.websocket.annotation;

import cn.hutool.json.JSONUtil;
import jakarta.websocket.OnClose;
import jakarta.websocket.OnMessage;
import jakarta.websocket.OnOpen;
import jakarta.websocket.Session;
import jakarta.websocket.server.PathParam;
import jakarta.websocket.server.ServerEndpoint;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;

@ServerEndpoint(value = "/websocket/{sessionId}")
@Component
public class WsServerEndpoint {

    private static ConcurrentHashMap<String, WsServerEndpoint> webSocketMap = new ConcurrentHashMap<>();

    //实例一个session,这个session是websocket的session
    private Session session;

    //新增一个方法用于主动向客户端发送消息
    public static void sendMessage(String message, String sessionId) {
        WsServerEndpoint webSocket = webSocketMap.get(sessionId);
        if (webSocket != null) {
            try {
                webSocket.session.getBasicRemote().sendText(message);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 连接成功
     *
     * @param session
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("sessionId") String sessionId) {
        this.session = session;
        webSocketMap.put(sessionId, this);
        sendMessage("connect success", sessionId);
    }

    /**
     * 连接关闭
     *
     * @param session
     */
    @OnClose
    public void onClose(Session session, @PathParam("sessionId") String sessionId) throws IOException {
        webSocketMap.remove(sessionId);
        session.close();
    }

    /**
     * 接收到消息
     *
     * @param text
     */
    @OnMessage
    public void onMsg(String text, @PathParam("sessionId") String sessionId) {
        sendMessage("receive msg from client:" + text, sessionId);
    }

}

说明

这里有几个注解需要注意一下,首先是他们的包都在 **jakarta.websocket **下(我用的jdk22)。并不是 spring 提供的,而 jdk 自带的,下面是他们的具体作用。

  1. @ServerEndpoint:通过这个 spring boot 就可以知道你暴露出去的 ws 应用的路径,有点类似我们经常用的@RequestMapping。比如你的启动端口是8080,而这个注解的值是ws,那我们就可以通过 ws://127.0.0.1:8080/ws 来连接你的应用
  2. @OnOpen:当 websocket 建立连接成功后会触发这个注解修饰的方法,注意它有一个 Session 参数
  3. @OnClose:当 websocket 建立的连接断开后会触发这个注解修饰的方法,注意它有一个 Session 参数
  4. @OnMessage:当客户端发送消息到服务端时,会触发这个注解修改的方法,它有一个 String 入参表明客户端传入的值
  5. @OnError:当 websocket 建立连接时出现异常会触发这个注解修饰的方法,注意它有一个 Session 参数

另外一点就是服务端如何发送消息给客户端,服务端发送消息必须通过上面说的 Session 类,通常是在@OnOpen 方法中,当连接成功后把 session 存入 Map 的 value,key 是与 session 对应的用户标识,当要发送的时候通过 key 获得 session 再发送。

使用postman测试:

2. Spring封装

package com.cjian.websocket.spring;

import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

@Component
public class CustomWebsocketHandler extends TextWebSocketHandler {

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        String name = (String) session.getAttributes().get("name");
        session.sendMessage(new TextMessage(name + " connection success"));
    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        session.sendMessage(new TextMessage("receive msg:" + message.getPayload()));
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        session.close();
    }
}

说明

通过继承 TextWebSocketHandler 类并覆盖相应方法,可以对 websocket 的事件进行处理,这里可以同原生注解的那几个注解连起来看

  1. afterConnectionEstablished 方法是在 socket 连接成功后被触发,同原生注解里的 @OnOpen 功能
  2. afterConnectionClosed  方法是在 socket 连接关闭后被触发,同原生注解里的 @OnClose 功能
  3. handleTextMessage 方法是在客户端发送信息时触发,同原生注解里的 @OnMessage 功能
CustomInterceptor
package com.cjian.websocket.spring;


import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;

import java.util.Map;

@Component
public class CustomInterceptor extends HttpSessionHandshakeInterceptor {

    /**
     * 握手前
     */
    @Override
    public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
        System.out.println("start hand shake");
        ServletServerHttpRequest httpRequest = (ServletServerHttpRequest) request;
        String name = httpRequest.getServletRequest().getParameter("name");
        attributes.put("name", name);
        return super.beforeHandshake(request, response, wsHandler, attributes);
    }

    /**
     * 握手后
     */
    @Override
    public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) {
        System.out.println("hand shake end");
    }

}

说明

通过实现 HandshakeInterceptor 接口来定义握手拦截器,注意这里与上面 Handler 的事件是不同的,这里是建立握手时的事件,分为握手前与握手后,而  Handler 的事件是在握手成功后的基础上建立 socket 的连接。所以在如果把认证放在这个步骤相对来说最节省服务器资源。它主要有两个方法 beforeHandshake 与 **afterHandshake **,顾名思义一个在握手前触发,一个在握手后触发。

CustomWebSocketConfig
package com.cjian.websocket.spring;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;

@Configuration
@EnableWebSocket
public class CustomWebSocketConfig implements WebSocketConfigurer {

    @Autowired
    private CustomWebsocketHandler customWebsocketHandler;

    @Autowired
    private CustomInterceptor myInterceptor;

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry
                .addHandler(customWebsocketHandler, "myWS")
                .addInterceptors(myInterceptor)
                .setAllowedOrigins("*");
    }
}

说明

通过实现 WebSocketConfigurer 类并覆盖相应的方法进行 websocket 的配置。我们主要覆盖 registerWebSocketHandlers 这个方法。通过向 WebSocketHandlerRegistry 设置不同参数来进行配置。其中 **addHandler 方法添加我们上面的写的 ws 的  handler 处理类,第二个参数是你暴露出的 ws 路径。addInterceptors 添加我们写的握手过滤器。setAllowedOrigins("*") **这个是关闭跨域校验,方便本地调试,线上推荐打开。

测试:

参考:https://www.cnblogs.com/kiwifly/p/11729304.html 

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

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

相关文章

Djangorestframework+Vue搭建前后端分离项目

文章目录 1、Django环境搭建2、安装Vue3、安装ant-design插件4、echarts图标插件5、解决跨域问题 1、Django环境搭建 安装anaconda https://www.anaconda.com/download 打开命令行工具&#xff0c;出现base就表示安装成功了&#xff0c;表示当前的虚拟环境名 创建虚拟环境的p…

Isaac Sim仿真平台学习(2)系统架构

目录 0.前言 1.isaac sim的系统架构 1.Design&#xff08;设计&#xff09; 2.Tune&#xff08;调试&#xff09;和Train&#xff08;训练&#xff09; 1.PhysX 2.RTX 3.Sensors 1.感知型扩展传感器 2.物理传型感器 4.Replicator 5.Digital Twins 6.Omnigraph 7.I…

火爆国内外的《黑神话:悟空》,需要什么显卡才能玩?

一路西行&#xff0c;大圣归来&#xff01; 8月20日&#xff0c;国产游戏《黑神话&#xff1a;悟空》上午10时正式上线。这款游戏在Steam平台的同时在线玩家突破了114万&#xff0c;超越《CS2》登顶Steam热玩榜。 仅单日实际在线人数就超过了210万 &#xff0c;超过《幻兽帕鲁…

在线问诊平台开发指南:基于互联网医院系统源码的实现路径

今天&#xff0c;小编将详细讲解如何通过互联网医院系统源码开发在线问诊平台。 一、在线问诊平台的需求分析 在线问诊平的核心目标是通过互联网技术&#xff0c;实现患者与医生之间的远程交流与诊断。因此&#xff0c;在开发过程中&#xff0c;首先需要明确平台的核心功能需求…

Excel的使用总结2

目录 1、数据的分割 2、根据某个条件筛选出来的数据&#xff0c;想要直接复制&#xff0c;并粘贴到与筛选数据对应的位置 3、几个单元格中的内容连接 4、如果一个表格数据很多&#xff0c;但是你想查看一列中是否有数据&#xff0c;如何查看 1、数据的分割 首先&#xff0c…

什么是光伏气象站—光伏气象站的简述

随着全球对可再生能源需求的日益增长&#xff0c;光伏发电作为清洁、可再生的能源形式&#xff0c;正逐步成为能源结构转型的重要力量。然而&#xff0c;光伏电站的发电效率受到多种气象因素的影响&#xff0c;如太阳辐射强度、温度、风速、湿度等。为了最大化光伏系统的发电潜…

洛谷 P4516 [JSOI2018] 潜入行动

题目来源于&#xff1a;洛谷 题目本质&#xff1a;背包&#xff0c;树形dp 解题思路&#xff1a; 假设当前合并两个背包f[u][a][p1][q1] 和f[v][b][p2][q2] &#xff0c;其中 v 是 u 的儿子。考虑合并后的f[u][ab][p3][q3],q3 是合并后点 u 是否被监听&#xff0c;有两种情况…

C++拾趣——使用VSCode跨平台调试CMake编译的C/C++项目

大纲 远程连接编译调试安装插件/组件VSCode插件调试组件 配置CMakePresets.json配置CMake Tools调试 参考资料 VSCode的远程开发功能为开发者带来了诸多好处&#xff0c;极大地提升了开发效率和灵活性。首先&#xff0c;它允许开发者在本地编辑环境中直接连接到远程服务器或容器…

03 网络故障排查思路

一 排查思路 1 以业务流量路径为核心的故障排除思想 确认业务流量路径&#xff1a;在网络层和数据链路层中&#xff0c;需要根据报文转发过程确认业务流量的传输路径&#xff0c;以及数据帧在二层网络环境中是如何被交换机转发的。分层故障处理法&#xff1a;从OSI模型的底层…

【游戏行业秋招提前批最新动向应届生入职指南!】

游戏行业秋招提前批最新动向&应届生入职指南&#xff01; 秋招非常非常重要&#xff01;绝不能轻易错过&#xff01;&#xff01;! 早起的鸟儿有虫吃&#xff0c; 这句话对于企业和应届生双方而言都是适用的。 对于有校招需求的企业而言&#xff0c; 秋招是一年中规模最…

IDEA XML文件去掉黄色和绿色底色

XML映射文件去掉黄色背景&#xff1a;Settings ⇒ Editor ⇒ Inspections ⇒ SQL XML映射文件去掉绿色背景&#xff1a;Settings ⇒ Editor ⇒ Color Scheme ⇒ General ⇒ Code Java去掉重复代码提示&#xff1a;Settings ⇒ Editor ⇒ Inspections ⇒ General

页面设计任务 个人信息页面

目录 成品: 任务要求&#xff1a; 1. 创建一个基本的个人简介网页 2. 样式和布局要求 3. 详细样式要求 源码&#xff1a; 详细讲解&#xff1a; 1.导航栏部分&#xff1a; 2.头像和介绍部分: 3.技能列表部分 4.作品集部分 成品: 任务要求&#xff1a; 1. 创建一个基本…

B站宋红康JAVA基础视频教程个人笔记chapter07(面向对象高级)

文章目录 1.static关键字的学习1.1为什么需要static关键字1.2 static总结 2.单例设计模式3.静态代码块4.final关键字的使用5.抽象类和抽象方法5.1抽象类的由来 6.接口的使用6.1基本概念理解 7.匿名内部类的学习8.内部类9.枚举类9.包装类10.IDEA快捷键大全 1.static关键字的学习…

IOTE 2024 第二十二届国际物联网展 ·深圳站 8月28-30日唯创知音

​ 世界聚焦物联网&#xff0c;产业规模空前&#xff01;一场高端产业研学盛会即将如约而至。 IOTE 2024 第二十二届国际物联网展深圳站&#xff0c;2024年8月28-30日将在深圳国际会展中心(宝安)开展&#xff0c;汇聚全球超800家参展企业、13万来自工业、物流、基础建设、智慧…

MySQL基本查询 -- 表的增删改查CRUD

CRUD : Create(创建), Retrieve(读取)&#xff0c;Update(更新)&#xff0c;Delete&#xff08;删除&#xff09; 文章目录 1.Createa.insert语法&#xff1a;插入指定列&#xff1a;全列插入&#xff1a;一次插入多行数据&#xff1a; b. on duplicate key update ~ 插入否则更…

【前端】文件上传框架plupload使用(前后端交互)

这个框架是用来给前端设置文件上传的按钮的。 首先要明白&#xff0c;前端向后端发送请求的方式有get和post&#xff0c;两者的区别在于&#xff0c;前者只能在网址中携带参数&#xff0c;后者是在请求体body中携带参数。 Plupload向后端发送请求是post请求方式&#xff0c;发送…

Axios介绍;前后端分离开发的介绍;YAPI的使用;Vue项目简介、入门;Elementui的使用;nginx介绍

1 Ajax 1.1 Ajax介绍 1.1.1 Ajax概述 我们前端页面中的数据&#xff0c;如下图所示的表格中的学生信息&#xff0c;应该来自于后台&#xff0c;那么我们的后台和前端是互不影响的2个程序&#xff0c;那么我们前端应该如何从后台获取数据呢&#xff1f;因为是2个程序&#xf…

基于 Dots + GPU Instance 的大规模物体渲染

之前写的两篇开放世界技术栈都是公司其他同事做的&#xff0c;所以很多细节了解不详细。但这次是全程我自己搭建的轮子&#xff0c;可以讲得稍微详细些。 之前写的大规模物件渲染的 GPU 版本&#xff0c;虽然渲染量大效率高&#xff0c;但是有个很致命的缺陷&#xff1a;无法与…

Azure OpenAI citations with message correlation

题意&#xff1a;“Azure OpenAI 引用与消息关联” 问题背景&#xff1a; I am trying out Azure OpenAI with my own data. The data is uploaded to Azure Blob Storage and indexed for use with Azure AI search “我正在尝试使用自己的数据进行 Azure OpenAI。数据已上传…

行业应用 | 桥田MMC磁力换模系统-冲压场景案例分享

随着市场竞争的日益加剧&#xff0c;制造形态从单品种大批量转变为多品种小批量&#xff0c;品种的多样化对模具的多样化产生了需求&#xff0c;在更换产品品类时&#xff0c;首先需要更换加工模具。冲压是金属加工中的典型场景&#xff0c;如何缩短冲压模具的更换时间&#xf…