温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

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

发布时间:2021-09-28 09:48:38 来源:亿速云 阅读:178 作者:柒染 栏目:大数据

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

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

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

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

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

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

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}") @RestController public class TestWebSocket {     // 用来记录当前连接数的变量     private static volatile int onlineCount = 0;     // concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象     private static CopyOnWriteArraySet<TestWebSocket> webSocketSet = new CopyOnWriteArraySet<TestWebSocket>();     // 与某个客户端的连接会话,需要通过它来与客户端进行数据收发     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;   @Configuration public 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;   @RestController public class TestWebSocketController implements WebSocketHandler {       private static AtomicInteger onlineCount = new AtomicInteger(0);       private static final ArrayList<WebSocketSession> 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<String, Object> 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 @EnableWebSocket public 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") @RestController public 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;   @SpringBootApplication public 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实现

<!DOCTYPE html> <html> <head>     <title>WebSocket示例</title>     <meta content='width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no' name='viewport' />     <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> </head> <body>     <input id="text" type="text"/>     <button onclick="send()">发送消息</button>     <hr/>     <button onclick="closeWebSocket()">关闭WebSocket连接</button>     <hr/>     <div id="message"></div> </body>   <script type="text/javascript">     var websocket = null;     //判断当前浏览器是否支持WebSocket     if ('WebSocket' in window) {         // 不带参数的写法         websocket = new WebSocket("ws://127.0.0.1:18080/testWebsocket");         // 通过路径传递参数的方法(服务端采用第一种方法"@ServerEndpoint"实现)         websocket = new WebSocket("ws://127.0.0.1:18080/testWebsocket/23/Lebron");         // 通过类似GET请求方式传递参数的方法(服务端采用第二种方法"WebSocketHandler"实现)         websocket = new WebSocket("ws://127.0.0.1:18080/testWebsocket?id=23&name=Lebron");     }     else {         alert('当前浏览器 Not support websocket')     }       //连接发生错误的回调方法     websocket.onerror = function () {         setMessageInnerHTML("WebSocket连接发生错误");     };       //连接成功建立的回调方法     websocket.onopen = function () {         setMessageInnerHTML("WebSocket连接成功");     }       //接收到消息的回调方法     websocket.onmessage = function (event) {         setMessageInnerHTML(event.data);     }       //连接关闭的回调方法     websocket.onclose = function () {         setMessageInnerHTML("WebSocket连接关闭");     }       //监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。     window.onbeforeunload = function () {         closeWebSocket();     }       //将消息显示在网页上     function setMessageInnerHTML(innerHTML) {         document.getElementById('message').innerHTML += innerHTML + '<br/>';     }       //关闭WebSocket连接     function closeWebSocket() {         websocket.close();     }       //发送消息     function send() {         var message = document.getElementById('text').value;         websocket.send(message);     } </script> </html>

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. ");

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

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI