SpringBoot整合WebSocket

目录

WebSocket概述

SpringBoot整合WebSocket步骤

添加websocket依赖

启用WebSocket的支持

编写WebSocketServer及WebSocketUtil工具类

前端Demo界面编写

测试


WebSocket概述

Websocket是一种在单个TCP连接上进行全双工通信的协议,允许客户端和服务器之间进行实时数据传输。与传统的HTTP请求-响应模型不同,WebSocket建立了一种持久连接,可以在客户端和服务器之间实时地推送数据,非常适合需要实时更新的应用场景。Websockt特点如下:

  1. 建立在 TCP 协议之上,服务器端的实现比较容易。
  2. 与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。
  3. 数据格式比较轻量,性能开销小,通信高效。
  4. 可以发送文本,也可以发送二进制数据。
  5. 没有同源限制,客户端可以与任意服务器通信。
  6. 协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。

SpringBoot整合WebSocket步骤

添加websocket依赖

XML 复制代码
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>

启用WebSocket的支持

java 复制代码
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

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

编写WebSocketServer及WebSocketUtil工具类

核心:@ServerEndpoint("/webSocket/{userId}") ,里面实现@OnOpen开启连接,@onClose关闭连接,@onMessage接收消息等方法。

java 复制代码
import com.haokai.taxielder.utils.WebSocketUtil;
import jakarta.websocket.*;
import jakarta.websocket.server.PathParam;
import jakarta.websocket.server.ServerEndpoint;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.io.IOException;

/**
 * websocket接口处理类
 */
@Component
@ServerEndpoint(value = "/webSocket/{userId}")
@Slf4j
public class WebSocket {

    /**
     * 连接事件,加入注解
     * @param userId
     * @param session
     */
    @OnOpen
    public void onOpen(@PathParam(value = "userId") String userId, Session session) {
        log.info("用户[{}]加入websocket连接!", userId);
        // 添加到session的映射关系中
        WebSocketUtil.addSession(userId, session);
    }

    /**
     * 连接事件,加入注解
     * 用户断开链接
     *
     * @param userId
     * @param session
     */
    @OnClose
    public void onClose(@PathParam(value = "userId") String userId, Session session) {
        log.info("用户[{}]退出了websocket连接...", userId);
        // 删除映射关系
        WebSocketUtil.removeSession(userId);
    }

    /**
     * 当接收到用户上传的消息
     *
     * @param userId
     * @param session
     */
    @OnMessage
    public void onMessage(@PathParam(value = "userId") String userId, Session session, String message) {
        log.info("接收到用户[{}]的消息:{}", userId, message);
    }

    /**
     * 处理用户活连接异常
     *
     * @param session
     * @param throwable
     */
    @OnError
    public void onError(Session session, Throwable throwable) {
        try {
            session.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        throwable.printStackTrace();
    }

}
java 复制代码
import jakarta.websocket.RemoteEndpoint;
import jakarta.websocket.Session;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class WebSocketUtil {
    /**
     * 记录当前在线的Session
     */
    private static final Map<String, Session> ONLINE_SESSION = new ConcurrentHashMap<>();

    /**
     * 添加session
     * @param userId
     * @param session
     */
    public static void addSession(String userId, Session session){
        // 此处只允许一个用户的session链接。一个用户的多个连接,我们视为无效。
        ONLINE_SESSION.putIfAbsent ( userId, session );
    }

    /**
     * 关闭session
     * @param userId
     */
    public static void removeSession(String userId){
        ONLINE_SESSION.remove ( userId );
    }

    /**
     * 给单个用户推送消息
     * @param session
     * @param message
     */
    public static void sendMessage(Session session, String message){
        if(session == null){
            return;
        }
        // 异步发送
        RemoteEndpoint.Async async = session.getAsyncRemote ();
        async.sendText ( message );
    }

    /**
     * 向所有在线人发送消息
     * @param message
     */
    public static void sendMessageForAll(String message) {
        //jdk8 新方法
        ONLINE_SESSION.forEach((sessionId, session) -> sendMessage(session, message));
    }
}

前端Demo界面编写

html 复制代码
<!DOCTYPE HTML>
<html>
   <head>
   <meta charset="utf-8">
   <title>websocket client</title>
    
      <script type="text/javascript">
 
         var ws = null;
         function WebSocketTest()
         {
            if ("WebSocket" in window)
            {
               alert("浏览器支持 WebSocket!");
               
               // 打开一个 web socket
               var name = document.getElementById("name").value;
               ws = new WebSocket("ws://localhost:8081/elder-api/webSocket/" + name);
 
               //构件一个发送消息框
               var context = '<div class="sendMsg">\n' +
                       '           输入发送内容:<input id="sendWebMsg" />' +
                       '           <button onclick="javascript:sendMsgToWS()">发送</button>' +
                       '      </div>';
               document.getElementById("sendDiv").innerHTML = context;
 
               ws.onmessage = function (evt)
               {
                  var received_msg = evt.data;
                  alert("接收到数据:" + received_msg);
               };
			   
			   ws.onclose = function()
			   { 
                  // 关闭 websocket
                  alert("连接已关闭..."); 
               };
            }
            
            else
            {
               // 浏览器不支持 WebSocket
               alert("浏览器不支持 WebSocket!");
            }
         }
 
         function sendMsgToWS(){
 
            if(ws != null){
 
               var msg = document.getElementById("sendWebMsg").value;
               ws.send(msg);
            }
         }
 
      </script>
        
   </head>
   <body>
 
		
      <div id="sse">
         名称:<input id="name" />
         <button onclick="javascript:WebSocketTest()">连接 WebSocket</button>
      </div>
 
      <div id="sendDiv">
 
      </div>
      
   </body>
</html>

测试

当后台实时发生了变动,产生了消息,消息广播到前台所有连接用户

java 复制代码
 private void broadcastMessage(ScheduleOrderMsg scheduleOrderMsg) {
        ScheduleOrderMsgDto scheduleOrderMsgDto = new ScheduleOrderMsgDto();
        BeanUtil.copyProperties(scheduleOrderMsg, scheduleOrderMsgDto);
        //设置ID
        scheduleOrderMsgDto.setMsgId(scheduleOrderMsg.getMsgId() + "");
        // 获取订单信息
        Order scheduleOrder = orderMapper.getByOrderId(scheduleOrderMsg.getScheduleOrderId());
        if(scheduleOrder != null) {
            BeanUtil.copyProperties(scheduleOrder, scheduleOrderMsgDto);
        }
        //序列化
        JSONConfig jsonConfig = JSONConfig.create();
        jsonConfig.setDateFormat("yyyy-MM-dd HH:mm:ss");
        String scheduleOrderMsgJson = JSONUtil.toJsonStr(scheduleOrderMsgDto,jsonConfig);
        WebSocketUtil.sendMessageForAll(scheduleOrderMsgJson);
    }
相关推荐
智码看视界1 小时前
现代Web开发基础:全栈工程师的起航点
前端·后端·c5全栈
程序员cxuan1 小时前
Claude Fable 5 来了
人工智能·后端·程序员
JS菌1 小时前
手写一个 AI Agent 全栈项目:从沙箱执行到子智能体的完整实现
前端·人工智能·后端
wang09072 小时前
自己动手写一个spring之IOC_2
java·后端·spring
来杯@Java2 小时前
学生选课管理系统(基于springboot+vue前后端分离的项目)计算机毕业设计java
java·spring boot·spring·vue·毕业设计·maven·mybatis
ltl2 小时前
推理退化:为什么大模型会输出乱码、死循环和无意义文本
后端
ltl3 小时前
架构视图与文档:C4 模型从入门到实战
后端
invicinble3 小时前
easyexcel开发全域理解
spring boot
IT_陈寒5 小时前
Redis持久化这个坑,我爬了一整天才出来
前端·人工智能·后端