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 容器时通常不需要它。
复制代码
相关推荐
用户47949283569156 分钟前
我只是给Typescript提个 typo PR,为什么还要签协议?
前端·后端·开源
Surpass余sheng军13 分钟前
AI 时代下的网关技术选型
人工智能·经验分享·分布式·后端·学习·架构
JosieBook16 分钟前
【Spring Boot】Spring Boot调用 WebService 接口的两种方式:动态调用 vs 静态调用 亲测有效
java·spring boot·后端
喵个咪1 小时前
开箱即用的 GoWind Admin|风行,企业级前后端一体中后台框架:基于 GORM 从零实现新服务
后端·go·orm
是梦终空2 小时前
计算机毕业设计252—基于Java+Springboot+vue3+协同过滤推荐算法的农产品销售系统(源代码+数据库+2万字论文)
java·spring boot·vue·毕业设计·源代码·协同过滤算法·农产品销售系统
计算机毕设VX:Fegn08952 小时前
计算机毕业设计|基于springboot + vue服装商城系统(源码+数据库+文档)
数据库·vue.js·spring boot·课程设计
a努力。2 小时前
【基础数据篇】数据等价裁判:Comparer模式
java·后端
开心猴爷2 小时前
苹果App Store应用程序上架方式全面指南
后端
小飞Coding3 小时前
三种方式打 Java 可执行 JAR 包,你用对了吗?
后端
bcbnb3 小时前
没有 Mac,如何在 Windows 上架 iOS 应用?一套可落地的工程方案
后端