千家信息网

SpringBoot跟WebSocket的开发过程是怎样的

发表于:2025-02-03 作者:千家信息网编辑
千家信息网最后更新 2025年02月03日,SpringBoot跟WebSocket的开发过程是怎样的,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。1. 服务端的实
千家信息网最后更新 2025年02月03日SpringBoot跟WebSocket的开发过程是怎样的

SpringBoot跟WebSocket的开发过程是怎样的,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。

1. 服务端的实现,我尝试了两种方式:

  • 第一种是用"@ServerEndPoint"注解来实现,实现简单;

  • 第二种稍显麻烦,但是可以添加拦截器在WebSocket连接建立和断开前进行一些额外操作。

  不管用哪种实现方式,都需要先导入jar包(如下),其中version根据实际springboot版本选择,避免冲突

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

1.1 第一种实现方法

(1)WebSocket 业务逻辑实现。参数传递采用路径参数的方法,通过以下方式获取参数:

  • @ServerEndpoint("/testWebSocket/{id}/{name}")

  • public void onOpen(Session session, @PathParam("id") long id, @PathParam("name") String name)

import java.util.concurrent.CopyOnWriteArraySet;import javax.websocket.OnClose;import javax.websocket.OnError;import javax.websocket.OnMessage;import javax.websocket.OnOpen;import javax.websocket.Session;import javax.websocket.server.ServerEndpoint;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.web.bind.annotation.RestController;@ServerEndpoint("/testWebSocket/{id}/{name}")@RestControllerpublic class TestWebSocket {    // 用来记录当前连接数的变量    private static volatile int onlineCount = 0;    // concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象    private static CopyOnWriteArraySet webSocketSet = new CopyOnWriteArraySet();    // 与某个客户端的连接会话,需要通过它来与客户端进行数据收发    private Session session;    private static final Logger LOGGER = LoggerFactory.getLogger(TestWebSocket.class);      @OnOpen    public void onOpen(Session session, @PathParam("id") long id, @PathParam("name") String name) throws Exception {        this.session = session;        System.out.println(this.session.getId());        webSocketSet.add(this);        LOGGER.info("Open a websocket. id={}, name={}", id, name);    }    @OnClose    public void onClose() {        webSocketSet.remove(this);        LOGGER.info("Close a websocket. ");    }    @OnMessage    public void onMessage(String message, Session session) {        LOGGER.info("Receive a message from client: " + message);    }    @OnError    public void onError(Session session, Throwable error) {        LOGGER.error("Error while websocket. ", error);    }    public void sendMessage(String message) throws Exception {        if (this.session.isOpen()) {            this.session.getBasicRemote().sendText("Send a message from server. ");        }    }    public static synchronized int getOnlineCount() {        return onlineCount;    }    public static synchronized void addOnlineCount() {        TestWebSocket.onlineCount++;    }    public static synchronized void subOnlineCount() {        TestWebSocket.onlineCount--;    }}

(2)配置ServerEndpointExporter,配置后会自动注册所有"@ServerEndpoint"注解声明的Websocket Endpoint

import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.web.socket.server.standard.ServerEndpointExporter; @Configurationpublic class WebSocketConfig {     @Bean    public ServerEndpointExporter serverEndpointExporter() {        return new ServerEndpointExporter();    } }

1.2 第二种实现方法

(1)WebSocket 业务逻辑实现。参数传递采用类似GET请求的方式传递,服务端的参数在拦截器中获取之后通过attributes传递给WebSocketHandler。

import java.util.ArrayList;import java.util.concurrent.atomic.AtomicInteger; import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.web.bind.annotation.RestController;import org.springframework.web.socket.CloseStatus;import org.springframework.web.socket.WebSocketHandler;import org.springframework.web.socket.WebSocketMessage;import org.springframework.web.socket.WebSocketSession; @RestControllerpublic class TestWebSocketController implements WebSocketHandler {     private static AtomicInteger onlineCount = new AtomicInteger(0);     private static final ArrayList sessions = new ArrayList<>();     private final Logger LOGGER = LoggerFactory.getLogger(TestWebSocketController.class);     @Override    public void afterConnectionEstablished(WebSocketSession session) throws Exception {        sessions.add(session);        int onlineNum = addOnlineCount();        LOGGER.info("Oprn a WebSocket. Current connection number: " + onlineNum);    }     @Override    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {        sessions.remove(session);        int onlineNum = subOnlineCount();        LOGGER.info("Close a webSocket. Current connection number: " + onlineNum);    }     @Override    public void handleMessage(WebSocketSession wsSession, WebSocketMessage message) throws Exception {        LOGGER.info("Receive a message from client: " + message.toString());    }     @Override    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {        LOGGER.error("Exception occurs on webSocket connection. disconnecting....");        if (session.isOpen()) {            session.close();        }        sessions.remove(session);        subOnlineCount();    }     /*     * 是否支持消息拆分发送:如果接收的数据量比较大,最好打开(true), 否则可能会导致接收失败。     * 如果出现WebSocket连接接收一次数据后就自动断开,应检查是否是这里的问题。     */    @Override    public boolean supportsPartialMessages() {        return true;    }     public static int getOnlineCount() {        return onlineCount.get();    }     public static int addOnlineCount() {        return onlineCount.incrementAndGet();    }     public static int subOnlineCount() {        return onlineCount.decrementAndGet();    } }

(2)HandShake 拦截器实现

import java.util.Map; import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.ApplicationContext;import org.springframework.http.server.ServerHttpRequest;import org.springframework.http.server.ServerHttpResponse;import org.springframework.http.server.ServletServerHttpRequest;import org.springframework.web.socket.WebSocketHandler;import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor; public class TestHandShakeInterceptor extends HttpSessionHandshakeInterceptor {     private final Logger LOGGER = LoggerFactory.getLogger(TestHandShakeInterceptor.class);     /*     * 在WebSocket连接建立之前的操作,以鉴权为例     */    @Override    public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response,            WebSocketHandler wsHandler, Map attributes) throws Exception {         LOGGER.info("Handle before webSocket connected. ");         // 获取url传递的参数,通过attributes在Interceptor处理结束后传递给WebSocketHandler        // WebSocketHandler可以通过WebSocketSession的getAttributes()方法获取参数        ServletServerHttpRequest serverRequest = (ServletServerHttpRequest) request;        String id = serverRequest.getServletRequest().getParameter("id");        String name = serverRequest.getServletRequest().getParameter("name");         if (tokenValidation.validateSign()) {            LOGGER.info("Validation passed. WebSocket connecting.... ");            attributes.put("id", id);            attributes.put("name", name);            return super.beforeHandshake(request, response, wsHandler, attributes);        } else {            LOGGER.error("Validation failed. WebSocket will not connect. ");            return false;        }    }     @Override    public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response,            WebSocketHandler wsHandler, Exception ex) {        // 省略    } }

(3)WebSocket 配置类实现(注册WebSocket实现类,绑定接口,同时将实现类和拦截器绑定)

import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.annotation.Configuration;import org.springframework.web.servlet.config.annotation.EnableWebMvc;import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;import org.springframework.web.socket.config.annotation.EnableWebSocket;import org.springframework.web.socket.config.annotation.WebSocketConfigurer;import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry; import TestWebSocketController;import TestHandShakeInterceptor; @Configuration@EnableWebMvc@EnableWebSocketpublic class WebSocketConfig extends WebMvcConfigurerAdapter implements WebSocketConfigurer {     @Autowired    private TestWebSocketController testWebSocketController;     @Override    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {        registry.addHandler(TestWebSocketController, "/testWebSocket")                .addInterceptors(new TestHandShakeInterceptor()).setAllowedOrigins("*");    } }

1.3 补充说明

(1)在WebSocket实现过程中,尤其是通过"@ServerEndpoint"实现的时候,可能会出现注入失败的问题,即注入的Bean为null的问题。可以通过手动注入的方式来解决,需要改造实现类和SpringBoot启动类,如下:

@ServerEndpoint("testWebsocket")@RestControllerpublic class WebSocketController {     private TestService testService;     private static ApplicationContext applicationContext;     @OnOpen    public void onOpen(Session session) {        testService = applicationContext.getBean(TestService.class);    }     @OnClose    public void onClose() {}     @OnMessage    public void onMessage(String message, Session session) {}     @OnError    public void onError(Session session, Throwable error) {}     public static void setApplicationContext(ApplicationContext applicationContext) {        WebSocketController.applicationContext = applicationContext;    } }
import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.context.ConfigurableApplicationContext; import WebSocketController; @SpringBootApplicationpublic class Application {     public static void main(String[] args) {//        SpringApplication.run(Application.class, args);        SpringApplication springApplication = new SpringApplication(Application.class);        ConfigurableApplicationContext configurableApplicationContext = springApplication.run(args);        WebSocketController.setApplicationContext(configurableApplicationContext);  // 解决WebSocket不能注入的问题    } }

2. 客户端的实现,我尝试了html和java WebSocketClient两种方式

2.1 html实现

    WebSocket示例                    

2.2 Java WebSocketClient实现

(1)WebSocketClient 实现类

import java.net.URI; import org.java_websocket.client.WebSocketClient;import org.java_websocket.drafts.Draft;import org.java_websocket.handshake.ServerHandshake;import org.slf4j.Logger;import org.slf4j.LoggerFactory; public class TestWebSocketClient extends WebSocketClient {     private final Logger LOGGER = LoggerFactory.getLogger(TestWebSocketClient.class);     public TestWebSocketClient(URI serverUri) {        super(serverUri);    }     public TestWebSocketClient(URI serverUri, Draft protocolDraft) {        super(serverUri, protocolDraft);    }     @Override    public void onOpen(ServerHandshake serverHandshake) {        LOGGER.info("Open a WebSocket connection on client. ");    }     @Override    public void onClose(int arg0, String arg1, boolean arg2) {        LOGGER.info("Close a WebSocket connection on client. ");    }     @Override    public void onMessage(String msg) {        LOGGER.info("WebSocketClient receives a message: " + msg);    }     @Override    public void onError(Exception exception) {        LOGGER.error("WebSocketClient exception. ", exception);    } }

(2)WebSocketClient 发送数据

String serverUrl = "ws://127.0.0.1:18080/testWebsocket"URI recognizeUri = new URI(serverUrl);client = new TestWebSocketClient(recognizeUri, new Draft_6455());client.connect();client.send("This is a message from client. ");

看完上述内容是否对您有帮助呢?如果还想对相关知识有进一步的了解或阅读更多相关文章,请关注行业资讯频道,感谢您对的支持。

0