springboot整合websocket,入门学习

1.简介

websocket是一种基于TCP连接上进行全双工通信的协议,设计用于提供低延迟全双工长期运行的连接,可以说websocket的出现就是解决实时通信的问题

全双工:通信的双方可以同时发送和接受数据,不需要等对方的响应或传输完成
半双方:允许数据在两个方向上传输,但是在同一个时间段只允许在一个方向上运输

实时通信:即时消息传递、音视频通话、在线会议和实时数据传输等,可以实现即时的数据传输和交流,不需要用户主动请求或刷新来获取更新数据

image.png

2.常见的消息推送方式

2.1轮询方式

2.1.1短轮询

浏览器以指定的时间间隔向服务器发出http请求,服务器实时返回数据给浏览器
image.png

数据有延迟并且对服务器的压力较大

2.1.2长轮询

浏览器发出ajax请求,服务器端接收到请求后,会阻塞请求直到有数据或者超时才会返回
image.png

相对于短轮询而言,对服务器的压力会小一点

2.2 SSE(server-sent event):服务器发送事件

  • SSE在服务器和客户端之间打开一个单向通道
  • 服务端响应的不再是一次性的数据包,而是text/event-stream类型的数据流信息
  • 服务器有数据变更时将数据流式传输到客户端

image.png

2.3 websocket

看本篇

3.原理解析

image.png

  • 浏览器发送请求,请求头中有UPgrade:websocket,请求将http协议升级为websocket协议
  • 服务器响应,响应状态码为101,表示将将http协议升级为websocket协议
  • 握手后就可以双向数据传输

image.png

4.websocket API

4.1客户端(浏览器)API

(1)websocket对象创建

let ws = new WebSocket(URL);

URL说明:

  • 格式:协议://ip地址/访问路径,默认端口为80
  • 协议:协议名称为ws

(2)websocket对象相关事件
image.png

(3)websocket对象提供的方法
image.png

总体结构
image.png

4.2服务端API

Tomcat的7.0.5 版本开始支持WebSocket,并且实现了Java WebSocket规范
Java WebSocket应用由一系列的Endpoint组成,Endpoint 是一个java对象,代表WebSocket链接的一端,对于服务端,我们可以视为处理具体WebSocket消息的接口。
可以通过两种方式定义Endpoint:

  • 第一种是编程式,即继承类javax.websocket.Endpoint并实现其方式
  • 第二种是注解式,即定义一个POJO,并添加@ServerEndpoint相关注解

Endpoint实例在WebSocket握手时创建,并在客户端与服务端链接过程中有效,最后在链接关闭时结束。在Endpoint接口中明确定义了与其生命周期相关的方法,规范实现者确保生命周期的各个阶段调用实例的相关方法。生命周期方法如下:
image.png

左边是编程式,右边是注解式

image.png

服务端如何接收客户端发送的数据呢?

  • 编程式:通过添加MessageHandler消息处理器来接收消息
  • 注解式:在定义Endpoint时,通过@OnMessage竹节指定接收消息的方法

服务端如何推送数据给客户端呢?
发送消息则由RemoteEndpoint完成,它的实例由Session(会话)维护,websocket连接成功后就建立了会话。
发送消息有2种方式发送消息:

  • 通过session.getBasicRemote获取同步消息发送的实例,然后调用其sendXxx()发送消息,如sendText()方法向客户端发送文本消息
  • 通过session.getAsyncRemote获取异步消息发送的实例,然后调用其sendXxx()发送消息…

5.实现

1.流程分析

image.png

  • 登录完成后发送请求转为websocket协议,记录session,并广播消息,向所有客户端响应用户列表消息,显示用户在线列表
  • 客户端发送消息后服务端解析消息,并判断收消息的人,将消息推送给指定的客户端
  • 关闭连接,向所有客户端响应用户列表消息,显示用户在线列表

2.消息格式

客户端—>服务端

image.png
toName:谁接收消息

服务端—>客户端

image.png
system:判断是否为系统消息
fromName:谁发送的消息
message:消息内容

系统消息中的李四王五是在线用户列表

3.代码实现

(1)引入依赖

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

(2)编写配置类,扫描添加有@ServerEndpoint注解的 Bean

@Configuration
public class WebsocketConfig {
    @Bean
    public ServerEndpointExporter endpointExporter(){
        return new ServerEndpointExporter();
    }
}

ServerEndpointExporter是一个Spring Boot提供的用于自动注册和管理WebSocket端点的类。通过将ServerEndpointExporter作为Bean定义在配置类中,Spring Boot会自动扫描并注册所有带有@ServerEndpoint注解的WebSocket端点。

(3)编写配置类,用于获取 HttpSession 对象

@Configuration
public class GetHttpSessionConfig extends ServerEndpointConfig.Configurator {
    @Override
    public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
        //获取httpSession对象
        HttpSession httpSession = (HttpSession) request.getHttpSession();
        //将httpsession对象保存起来
        sec.getUserProperties().put(HttpSession.class.getName(), httpSession);
    }
}

修改握手请求,在这个方法中,我们可以获取到HTTP请求的会话对象(HttpSession),并将其保存起来,以便后续在WebSocket连接中使用。

ServerEndpointConfig是Java WebSocket API中的一个接口,用于配置WebSocket端点的相关参数和属性,通过getUserProperties方法获取到用户属性,并使用put方法将HttpSession对象存储在其中。

(4)编写Endpoint类

//定义的endpoint类
@ServerEndpoint(value = "/chat",configurator = GetHttpSessionConfig.class)//前端中写的访问websocket的路径
@Component
public class ChatEndpoint {

    //每个用户都会创建一个Endpoint对象,创建一个公用的map集合来存session
    private static final Map<String, Session> onlineUsers = new ConcurrentHashMap<>();

    private HttpSession httpSession;

    /**
     * 建立连接后时被调用
     * @param session
     * @param config
     */
    @OnOpen
    public void onOpen(Session session, EndpointConfig config){
        //1.将session进行保存
        httpSession = (HttpSession) config.getUserProperties().get(HttpSession.class.getName());
        String username = (String) httpSession.getAttribute("user");
        onlineUsers.put(username,session);
        //2.广播消息,需要将登录的所有用户推送给所有用户
        String message = MessageUtils.getMessage(true, null, getFriends());//使用MessageUtils中的方法
        broadcastAllUsers(message);
    }

    //获取onlineUsers的map集合中的键
    private Set getFriends() {
        Set<String> set = onlineUsers.keySet();
        return set;
    }

    //广播给所有用户
    private void broadcastAllUsers(String message){
        try {
            //遍历map集合
            Set<Map.Entry<String, Session>> entries = onlineUsers.entrySet();
            for (Map.Entry<String, Session> entry : entries) {
                //获取所有用户对应的session对象
                Session session = entry.getValue();
                //发送同步消息
                session.getBasicRemote().sendText(message);
            }
        } catch (IOException e) {
            //记录日志
        }

    }


    /**
     * 浏览器发送消息到服务端时被调用
     * 将消息推送给指定的用户
     * @param message
     */
    @OnMessage
    public void onMessage(String message){

        try {
            //将消息的json字符串转换为消息的对象
            Message msg = JSON.parseObject(message, Message.class);
            //获取消息接收方的用户名
            String toName = msg.getToName();
            String msgString = msg.getMessage();
            //获取消息接收方的session
            Session session = onlineUsers.get(toName);
            //获取消息发送方的用户名
            String fromName = (String) httpSession.getAttribute("user");
            //将消息字符串转换为指定的json格式
            String msgJson = MessageUtils.getMessage(false, fromName, msgString);
            //发送消息给指定的用户
            session.getBasicRemote().sendText(msgJson);
        } catch (IOException e) {
            //记录日志
        }

    }

    /**
     * 关闭连接时被调用
     * @param session
     */
    @OnClose
    public void onClose(Session session){
        //1.从onlineUsers中剔除该用户的session
        String username = (String) httpSession.getAttribute("user");
        onlineUsers.remove(username);
        //2.广播给所有用户,该用户下线了
        //将消息字符串转换为指定的json格式
        String message = MessageUtils.getMessage(true, null, getFriends());
        broadcastAllUsers(message);
    }
}

消息工具类

public class MessageUtils {
    public static String getMessage(boolean isSystemMessage,String fromName, Object message) {
        ResultMessage result = new ResultMessage();
        result.setSystem(isSystemMessage);
        result.setMessage(message);
        if(fromName != null) {
            result.setFromName(fromName);
        }
        return JSON.toJSONString(result);
    }
}
  1. @ServerEndpoint注解被标注在ChatEndpoint类上,表示该类是一个WebSocket端点。value = "/chat"指定了WebSocket端点的URL路径为/chat,即客户端可以通过ws://localhost:8080/chat来连接到该WebSocket端点;configurator = GetHttpSessionConfig.class指定了一个配置器类GetHttpSessionConfig,用于在WebSocket握手过程中获取和保存HttpSession对象
  2. value = "/chat"指定了WebSocket端点的URL路径为/chat,即客户端可以通过ws://localhost:8080/chat来连接到该WebSocket端点;configurator = GetHttpSessionConfig.class指定了一个配置器类GetHttpSessionConfig,用于在WebSocket握手过程中获取和保存HttpSession对象
  3. onlineUsers用来创建一个公用的map集合来存所有已登录用户的session,因为每个用户登录后都会创建一个Endpoint对象。
  4. 在session.getBasicRemote().sendText()发送消息之前需要将消息字符串转化为指定的json格式

image.png
image.png
/(ㄒoㄒ)/~~

  • 24
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要实现Spring Boot整合WebSocket,你需要进行以下步骤: 1. 首先,在pom.xml文件中添加WebSocket的相关依赖。可以使用以下两个依赖之一: - 从中提到的依赖: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> ``` - 从中提到的依赖: ```xml <!--webSocket--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> ``` 2. 创建WebSocket配置类,这个类负责配置WebSocket的相关信息。你可以按照以下方式创建一个配置类[3]: ```java package com.loit.park.common.websocket; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.socket.server.standard.ServerEndpointExporter; @Configuration public class WebSocketConfig { @Bean public ServerEndpointExporter serverEndpointExporter() { return new ServerEndpointExporter(); } } ``` 3. 至此,你已经完成了WebSocket整合配置。现在,你可以在你的应用中创建WebSocket的控制器并定义你的WebSocket端点。你可以根据你的需求来实现WebSocket端点的业务逻辑。 这就是Spring Boot整合WebSocket的基本步骤。通过这种方式,你可以在Spring Boot应用中轻松地使用WebSocket进行实时通信。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [springboot整合websocket](https://blog.csdn.net/weixin_45390688/article/details/120448778)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [springboot整合websocket(详解、教程、代码)](https://blog.csdn.net/hjq_ku/article/details/127503180)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值