小工具知识网

当前位置: 首页 > 行业知识 > Springboot3.X集成WebSocket完整流程

行业知识

Springboot3.X集成WebSocket完整流程

2026-11-04 14:57:26 来源:互联网转载

WebSocket介绍

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

环境介绍

springboot

pom文件。

        org.springframework.boot    spring-boot-starter-parent    3.0.5  

websocket依赖

                  org.springframework.boot           spring-boot-starter-websocket       

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 */@Configurationpublic 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 {     @Override    public void destroy() {        // TODO Auto-generated method stub        // 这里不重要    }     @Override    public void init(EndpointConfig arg0) {        // TODO Auto-generated method stub        // 这里也不重要     }     /*    *  encode()方法里的参数和Text里的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})@Componentpublic 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 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使用结束

上一篇:服务器怎么判断带宽?

下一篇:【C语言课设计划】个人通讯录管理系统(C语言大作业 链表 结构体 运行截图 完整代码)