Springboot3.X集成WebSocket完整流程

WebSocket介绍

WebSocket 是一种网络传输协议,可在单个 TCP 连接上进行全双工通信,位于 OSI 模型的应用层WebSocket 协议在 2011 年由 IETF 标准化为 RFC 6455,后由 RFC 7936 补充规范。WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocketAPI 中,浏览器和服务器只需要完成一次握手,两者之间就可以创建持久性的连接,并进行双向数据传输

环境介绍

springboot

pom文件

  <!--spring 版本-->
  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.0.5</version>
  </parent>

websocket依赖

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

Vue

"vue": "3.2.45",
"websocket": "^1.0.34"

后端代码示例

目录结构

在这里插入图片描述

配置类

package gzsf.epg.sever.webSocket.config;

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

/**
 * webSocket的配置类
 * @author 
 * @date 2023/10/7
 */

@Configuration
public class WebSocketConfig {

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

}
package gzsf.epg.sever.webSocket.websocket;

import jakarta.websocket.Session;

/**
 * @author
 * @date 2023/10/7
 */
public class WebSocketClient {

    // 与某个客户端的连接会话,需要通过它来给客户端发送数据
    private Session session;

    //连接的uri
    private String uri;

    public Session getSession() {
        return session;
    }

    public void setSession(Session session) {
        this.session = session;
    }

    public String getUri() {
        return uri;
    }

    public void setUri(String uri) {
        this.uri = uri;
    }

}

如下是后端发送消息的配置,如需发送文本类的数据需要配置这个

package gzsf.epg.sever.webSocket.util;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.json.JsonMapper;
import gzsf.epg.sever.datacheck.vo.ExportVo;
import jakarta.websocket.EncodeException;
import jakarta.websocket.Encoder;
import jakarta.websocket.EndpointConfig;

public class ServerEncoder implements Encoder.Text<ExportVo> {
 
    @Override
    public void destroy() {
        // TODO Auto-generated method stub
        // 这里不重要
    }
 
    @Override
    public void init(EndpointConfig arg0) {
        // TODO Auto-generated method stub
        // 这里也不重要
 
    }
 
    /*
    *  encode()方法里的参数和Text<T>里的T一致,如果你是Student,这里就是encode(Student student)
    */
    @Override
    public String encode(ExportVo responseMessage) throws EncodeException {
        try {
            JsonMapper jsonMapper = new JsonMapper();
            return jsonMapper.writeValueAsString(responseMessage);
 
        } catch ( JsonProcessingException e) {
            e.printStackTrace();
            return null;
        }
    }
}

服务类

package gzsf.epg.sever.webSocket.service;

import gzsf.epg.sever.datacheck.vo.ExportVo;
import gzsf.epg.sever.webSocket.util.ServerEncoder;
import gzsf.epg.sever.webSocket.websocket.WebSocketClient;
import jakarta.websocket.*;
import jakarta.websocket.server.PathParam;
import jakarta.websocket.server.ServerEndpoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

import java.io.IOException;

import java.util.concurrent.ConcurrentHashMap;

/**
 * @author 
 * @date 2023/10/7
 */

@ServerEndpoint(value = "/websocket/{userName}", encoders = {ServerEncoder.class})
@Component
public class WebSocketService {
    private static final Logger log = LoggerFactory.getLogger(WebSocketService.class);

    private static ApplicationContext applicationContext;

    public static void setApplicationContext(ApplicationContext context) {
        applicationContext = context;
    }

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


    /**
     * 与某个客户端的连接会话,需要通过它来给客户端发送数据
     */
    private Session session;
    /**
     * 接收userName
     */
    private String userName;


    /**
     * 客户端与服务端连接成功
     *
     * @param session
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("userName") String userName) {
        this.session = session;
        //根据token获取用户名
        this.userName = userName;
        WebSocketClient client = new WebSocketClient();
        client.setSession(session);
        client.setUri(session.getRequestURI().toString());
        webSocketMap.put(userName, client);
        log.info("连接成功!"+session.getRequestURI().toString());
        log.info("连接成功!"+session);
    }

    /**
     * 客户端与服务端连接关闭
     */
    @OnClose
    public void onClose() {
        log.info("连接关闭!");
    }

    /**
     * 客户端与服务端连接异常
     *
     * @param error
     * @param session
     */
    @OnError
    public void onError(Throwable error, Session session) {
    }

    /**
     * 客户端向服务端发送消息
     *
     * @param message
     * @throws IOException
     */
    @OnMessage
    public void onMsg(Session session, String message) throws IOException {
        log.info("消息测试!");
    }

    public static void sendVo(String userName, ExportVo vo) {
        try {
            WebSocketClient webSocketClient = webSocketMap.get(userName);
            if (webSocketClient != null) {
                webSocketClient.getSession().getBasicRemote().sendObject(vo);
            }
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException(e.getMessage());
        } catch (EncodeException e) {
            throw new RuntimeException(e.getMessage());
        }
    }

    /**
     * 向指定客户端发送消息
     *
     * @param message
     */
    public static void sendMessage(String userName, String message) {
        try {
            log.info("准备发消息!"+userName);
            WebSocketClient webSocketClient = webSocketMap.get(userName);
            log.info("连接成功》》》》》》》》》"+webSocketMap);
            if (webSocketClient != null) {
                log.info("发送的消息是!"+message);
                webSocketClient.getSession().getBasicRemote().sendText(message);
            }
        } catch (IOException e) {
            e.printStackTrace();
            log.info("失败");
            throw new RuntimeException(e.getMessage());
        }
    }
}

需要注意

修改启动类如下,服务类就能注入,否则在调用service的时候会空指针异常,具体原因可以去了解下 spring 默认管理

package gzsf.epg.sever;

import gzsf.epg.common.core.utils.SpringContextUtils;
import gzsf.epg.common.security.annotation.EnableCustomConfiguration;
import gzsf.epg.common.security.annotation.EnableFeignBeanClients;
import gzsf.epg.sever.webSocket.service.WebSocketService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Import;

/**
 * @author 
 * @date 2023/10/19
 */
@EnableCustomConfiguration
@EnableFeignBeanClients
@SpringBootApplication
@Import(SpringContextUtils.class)
public class ServiceApplication {
    private static final Logger logger = LoggerFactory.getLogger(ServiceApplication.class);

    public static void main(String[] args) {
        ConfigurableApplicationContext run = SpringApplication.run(ServiceApplication.class, args);
        //解决WebSocket不能注入的问题
        WebSocketService.setApplicationContext(run);
    }
}

前端代码示例

连接地址里 admin为用户名,因为websocket是单独为每个用户创建长连接,发送的消息也是一对一发送,当然也可以改为一对多,全权在业务怎么用。前端可获取用户名传到后端,如下代码示例是写死的
注意:前端也需下载依赖
下面展示一些 内联代码片

npm install vue-native-websocket
/**
 * 创建webSocket连接
 */
function createWS() {
  webSocket.value = new WebSocket("ws://127.0.0.1:8080/server/websocket/admin");
  //onopen事件监听
  webSocket.value.addEventListener('open', e => {
    console.log('与服务端连接打开->', e)
  }, false)
//onclose事件监听
  webSocket.value.addEventListener('close', e => {
    console.log('与服务端连接关闭->', e)
  }, false)
//onmessage事件监听
  webSocket.value.addEventListener('message', e => {
    console.log('接收到服务端的消息->', e)
  }, false)
//onerror事件监听
  webSocket.value.addEventListener('error', e => {
  }, false)
}

只需在需要开启长连接的时候调用createWS();即可;

简单说明一下执行流程

前端

在需要开启长连接处调用createWS();开启长连接即可,需注意用户名(该用户名就是唯一标识)传递。

后端

前端发起建立长连接请求后,执行WebSocketService类中的onOpen方法,创建client并存放在map集合中以便执行完业务后发送反馈消息。

    /**
     * 客户端与服务端连接成功
     *
     * @param session
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("userName") String userName) {
        this.session = session;
        //根据token获取用户名
        this.userName = userName;
        WebSocketClient client = new WebSocketClient();
        client.setSession(session);
        client.setUri(session.getRequestURI().toString());
        webSocketMap.put(userName, client);
        log.info("连接成功!"+session);
    }

如何调用

后端接收到前端请求后可新建线程执行业务流程,先返回执行中状态给前端,防止前后端连接超时的报错。

    @PostMapping("/importDx")
    public AjaxResult importDx(@RequestParam("xlsxFile") MultipartFile file) {
        this.onCallback(file);
        return AjaxResult.success();
    }

    public void onCallback(MultipartFile file) {
        ExecutorService executor = Executors.newCachedThreadPool();
        executor.execute(() -> {
            try {
                //服务类代码
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        });
        executor.shutdown();
    }

业务执行完成后可调用,username为创建websocket的唯一标识。

/**
             * 调用sendMessage方法使用
             */
            WebSocketService.sendMessage(username, "执行成功");

只要是在长连接里面,想做什么全看业务,简单来说干什么都行!至此WebSocket使用结束

  • 22
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
好的,我可以帮你解答这个问题。首先,你需要在 pom.xml 文件中引入 Spring Boot 的 websocket 依赖: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> ``` 然后,你需要创建一个 WebSocket 配置类,例如: ```java @Configuration @EnableWebSocket public class WebSocketConfig implements WebSocketConfigurer { @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(myHandler(), "/myHandler").setAllowedOrigins("*"); } @Bean public WebSocketHandler myHandler() { return new MyHandler(); } } ``` 在上面的代码中,我们创建了一个 WebSocket 配置类,并实现了 WebSocketConfigurer 接口。在 registerWebSocketHandlers 方法中,我们注册了一个名为 myHandler 的 WebSocket 处理器。在 setAllowedOrigins 方法中,我们设置了允许跨域访问。 接下来,你需要创建一个 WebSocket 处理器类,例如: ```java public class MyHandler implements WebSocketHandler { @Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { System.out.println("WebSocket 连接成功"); } @Override public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception { System.out.println("WebSocket 收到消息:" + message.getPayload()); session.sendMessage(new TextMessage("Hello, " + message.getPayload())); } @Override public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception { System.out.println("WebSocket 连接出错"); } @Override public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception { System.out.println("WebSocket 连接关闭"); } @Override public boolean supportsPartialMessages() { return false; } } ``` 在上面的代码中,我们创建了一个名为 MyHandler 的 WebSocket 处理器类,并实现了 WebSocketHandler 接口。在 afterConnectionEstablished 方法中,我们处理 WebSocket 连接成功的逻辑。在 handleMessage 方法中,我们处理 WebSocket 收到消息的逻辑,并向客户端发送一条消息。在 handleTransportError 方法中,我们处理 WebSocket 连接出错的逻辑。在 afterConnectionClosed 方法中,我们处理 WebSocket 连接关闭的逻辑。在 supportsPartialMessages 方法中,我们返回 false,表示不支持分段消息。 最后,你需要在你的 Spring Boot 应用中启动 WebSocket,例如: ```java @SpringBootApplication public class MyApplication { public static void main(String[] args) { SpringApplication.run(MyApplication.class, args); } } ``` 现在,你已经成功地基于 Spring Boot 2.3.12.RELEASE 使用了 WebSocket。你可以通过访问 ws://localhost:8080/myHandler 来测试你的 WebSocket 应用。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值