SpringBoot--05--整合WebSocket,实现全双工通信

文章目录

为什么需要websocket

传统的HTTP协议是单向通信的,支持客户端向服务器发送请求,服务器接收请求。但是服务器有时也要向客户端发送请求。

websocket是一个全双工的通信协议,客户端可以向服务器发送请求,服务器也可以向客户端发送请求。

应用场景:订单通知、弹幕系统、比赛或者直播一些实时的排行榜数据。

网上定义:

WebSocket 是一种基于 TCP 协议的全双工通信协议 ,它允许客户端和服务器之间建立持久的、双向的通信连接。相比传统的 HTTP 请求 - 响应模式,WebSocket 提供了实时、低延迟的数据传输能力。通过 WebSocket,客户端和服务器可以在任意时间点互相发送消息,实现实时更新和即时通信的功能。WebSocket 协议经过了多个浏览器和服务器的支持,成为了现代 Web 应用中常用的通信协议之一。它广泛应用于**聊天应用、实时数据更新、多人游戏等场景,**为 Web 应用提供了更好的用户体验和更高效的数据传输方式。

项目中使用websocket

导入maven坐标
c 复制代码
<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
编写配置类

编写配置类,用户注册websocket的bean

java 复制代码
@Configuration
public class WebSocketConfiguration {

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

}
server代码

编写server代码

@ServerEndpoint("/ws/{sid}") 类似于RequestMapping。

接收前端传过来的参数。

java 复制代码
package com.njitzx.controller;

import org.springframework.stereotype.Component;

import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

/**
 * WebSocket服务
 */
@Component
@ServerEndpoint("/ws/{sid}")
public class WebSocketServer {

    //存放会话对象   存放会话对象
    private static Map<String, Session> sessionMap = new HashMap();

    /**
     * 连接建立成功调用的方法
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("sid") String sid) {
        System.out.println("客户端:" + sid + "建立连接");
        sessionMap.put(sid, session);
    }

    /**
     * 收到客户端消息后调用的方法   类似于Controller的方法
     *
     * @param message 客户端发送过来的消息
     */
    @OnMessage
    public void onMessage(String message, @PathParam("sid") String sid) {
        System.out.println("收到来自客户端:" + sid + "的信息:" + message);
    }

    /**
     * 连接关闭调用的方法
     *
     * @param sid
     */
    @OnClose
    public void onClose(@PathParam("sid") String sid) {
        System.out.println("连接断开:" + sid);
        sessionMap.remove(sid);
    }

    /**
     * 群发
     *
     * @param message
     */
    public void sendToAllClient(String message) {
        //将前端传过来的数据群发, 所有人都能拿到会话   values拿到map集合的所有属性值
        Collection<Session> sessions = sessionMap.values();
        for (Session session : sessions) {
            try {
                //服务器向客户端发送消息
                session.getBasicRemote().sendText(message);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

使用定时任务向前端发送消息。

java 复制代码
import com.njitzx.controller.WebSocketServer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

@Component
public class WebSocketTask {
    @Autowired
    private WebSocketServer webSocketServer;

    /**
     * 通过WebSocket每隔5秒向客户端发送消息
     */
    @Scheduled(cron = "0/5 * * * * ?")
    public void sendMessageToClient() {
        webSocketServer.sendToAllClient("这是来自服务端的消息 外卖超时了老哥:" + DateTimeFormatter.ofPattern("HH:mm:ss").format(LocalDateTime.now()));
    }
}

需要在启动类上面加上注解,开启定时器

cpp 复制代码
//开始定时器
@EnableScheduling
前端代码
html 复制代码
<!DOCTYPE HTML>
<html>
<head>
    <meta charset="UTF-8">
    <title>WebSocket Demo</title>
</head>
<body>
    <input id="text" type="text" />
    <button onclick="send()">发送消息</button>
    <button onclick="closeWebSocket()">关闭连接</button>
    <div id="message">
    </div>
</body>
<script type="text/javascript">
    var websocket = null;
    var clientId = Math.random().toString(36).substr(2);

    //判断当前浏览器是否支持WebSocket
    if('WebSocket' in window){
        //连接WebSocket节点
        websocket = new WebSocket("ws://localhost:8080/ws/"+clientId);
    }
    else{
        alert('Not support websocket')
    }

    //连接发生错误的回调方法
    websocket.onerror = function(){
        setMessageInnerHTML("error");
    };

    //连接成功建立的回调方法
    websocket.onopen = function(){
        setMessageInnerHTML("连接成功");
    }

    //接收到消息的回调方法
    websocket.onmessage = function(event){
        setMessageInnerHTML(event.data);
    }

    //连接关闭的回调方法
    websocket.onclose = function(){
        setMessageInnerHTML("close");
    }

    //监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
    window.onbeforeunload = function(){
        websocket.close();
    }

    //将消息显示在网页上
    function setMessageInnerHTML(innerHTML){
        document.getElementById('message').innerHTML += innerHTML + '<br/>';
    }

    //发送消息
    function send(){
        var message = document.getElementById('text').value;
        websocket.send(message);
    }
	
	//关闭连接
    function closeWebSocket() {
        websocket.close();
    }
</script>
</html>
和http请求URL区别

前端发送的请求参数格式,正常是http开头的 ,websocket请求是ws开头的

最后用ChatGPT生成了理解配置项

c 复制代码
`ServerEndpointExporter` 是 Spring 框架中用于配置 WebSocket 端点的一个类,特别是在 Spring WebSocket 支持中。它位于 `org.springframework.web.socket.server.standard` 包中。

### `ServerEndpointExporter` 的关键点:

1. **作用**:
   - `ServerEndpointExporter` 的主要作用是注册通过 `@ServerEndpoint` 注解定义的 WebSocket 端点。
   - 它会扫描应用上下文中的所有带有 `@ServerEndpoint` 注解的 bean,并将它们注册到底层的 WebSocket 服务器中。

2. **使用场景**:
   - 通常,你需要在 Spring 配置类中声明一个 `ServerEndpointExporter` bean。如果你使用 `@ServerEndpoint` 注解来定义 WebSocket 端点,那么这个 bean 是必须的。
   - 但如果你将应用部署到外部的 servlet 容器(如 Tomcat、Jetty 或 Undertow),通常不需要 `ServerEndpointExporter`,因为这些容器会自动处理 WebSocket 端点的注册。

3. **配置示例**:
   ```java
   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();
       }
   }
  • 在这个配置中,serverEndpointExporter 方法返回一个 ServerEndpointExporter bean,允许自动注册 WebSocket 端点。
  1. 重要说明
    • 如果你的 Spring Boot 应用程序打包为 WAR 文件并部署到外部 servlet 容器中,不应 包含 ServerEndpointExporter,因为这可能会与容器的 WebSocket 配置产生冲突。
    • 对于打包为独立 JAR 文件(使用嵌入式 servlet 容器如 Tomcat)的应用,ServerEndpointExporter 是支持 WebSocket 所必需的。
      ServerEndpointExporter 是 Spring 中用于自动注册带有 @ServerEndpoint 注解的 WebSocket 端点的工具类。在使用嵌入式 servlet 容器的独立 Spring Boot 应用中,它是必需的,但在部署到外部 servlet 容器时通常不需要它。
复制代码
相关推荐
XINGTECODE21 分钟前
海盗王集成网关和商城服务端功能golang版
开发语言·后端·golang
程序猿进阶27 分钟前
堆外内存泄露排查经历
java·jvm·后端·面试·性能优化·oom·内存泄露
FIN技术铺31 分钟前
Spring Boot框架Starter组件整理
java·spring boot·后端
凡人的AI工具箱1 小时前
15分钟学 Go 第 60 天 :综合项目展示 - 构建微服务电商平台(完整示例25000字)
开发语言·后端·微服务·架构·golang
小码的头发丝、1 小时前
Spring Boot 注解
java·spring boot
先天牛马圣体1 小时前
如何提升大型AI模型的智能水平
后端
午觉千万别睡过1 小时前
RuoYI分页不准确问题解决
spring boot
java亮小白19971 小时前
Spring循环依赖如何解决的?
java·后端·spring
2301_811274311 小时前
大数据基于Spring Boot的化妆品推荐系统的设计与实现
大数据·spring boot·后端
草莓base2 小时前
【手写一个spring】spring源码的简单实现--容器启动
java·后端·spring