今天寫一個前后端交互的websocket , 本來寫着挺順利的,但測試的時候蒙了,前端websocket發的連接請求竟然連接不上
返回狀態Status 報了個404 ,然后看后台onError方法也沒觸發
只是報了下面幾條警告信息
WARN o.s.web.servlet.PageNotFound | No mapping for GET /websocket/***
WARN o.s.web.servlet.PageNotFound | No mapping for GET /websocket/***
WARN o.s.web.servlet.PageNotFound | No mapping for GET /websocket/***
沒頭緒,就上網找了一下,
有說 需要配置Websocket 注入ServerEndpointExporter 的 ;
有說 需要添加websocket jar包的
但看這些操作的緣由,卻和我的實際情況不太一樣,
不過也照着做試了一下,依然沒有變化。。。
不由地懷疑,我是在后端代碼那邊少寫了什么???
於是又仔細看了幾遍 幾個相關的博客,忽然發現有的加了 @Component 注解,有的沒加,有可能問題出現在這里
果然,在加了這個@Component 注解后,網頁和后台的websocket 就連接成功了
所以,很懷疑那些博客上沒加這個注解的,是不是也能連接成功?覺得還是看官方解釋好,坑能少踩點!
后端代碼
HtmlSocketServer

package com.cloud.simulator.controller; import java.io.IOException; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import javax.websocket.OnClose; import javax.websocket.OnError; import javax.websocket.OnMessage; import javax.websocket.OnOpen; import javax.websocket.Session; import javax.websocket.server.PathParam; import javax.websocket.server.ServerEndpoint; import org.springframework.stereotype.Component; /** * @ServerEndpoint 注解是一個類層次的注解,它的功能主要是將目前的類定義成一個websocket服務器端, * 注解的值將被用於監聽用戶連接的終端訪問URL地址,客戶端可以通過這個URL來連接到WebSocket服務器端 * 即 @ServerEndpoint 可以把當前類變成websocket服務類 */ //訪問服務端的url地址 @ServerEndpoint(value = "/websocket/simulator/{userNo}") @Component public class HtmlSocketServer { //靜態變量,用來記錄當前在線連接數。應該把它設計成線程安全的。 private static int onlineCount = 0; //concurrent包的線程安全Map,用來存放每個客戶端對應的HtmlSocketServer對象。若要實現服務端與單一客戶端通信的話,可以使用Map來存放,其中Key可以為用戶標識 private static Map<String, HtmlSocketServer> webSocketMap = new ConcurrentHashMap<String, HtmlSocketServer>() ; //與某個客戶端的連接會話,需要通過它來給客戶端發送數據 private Session session; //當前發消息的客戶端的編號 private String userNo = "" ; public static Map<String, HtmlSocketServer> getWebSocketMap() { return webSocketMap ; } /** * 連接成功后調用的方法 * @param session 可選的參數。session為與某個客戶端的連接會話,需要通過它來給客戶端發送數據 */ @OnOpen public void onOpen(@PathParam(value = "userNo") String param, Session session) { userNo = param ; this.session = session; webSocketMap.put(param, this) ; //在線數加1 addOnlineCount(); System.out.println("有新連接加入!當前在線人數為" + getOnlineCount()); } /** * 連接關閉調用方法 */ @OnClose public void onClose() { if ( !userNo.equals("") ) { webSocketMap.remove(userNo); //在線數減1 subOnlineCount(); System.out.println("關閉一個連接!當前在線人數為" + getOnlineCount()); } } /** * 連接異常 */ @OnError public void onError(Throwable error) { System.out.println("連接異常 ---onError"); error.printStackTrace(); } /** * 接收到客戶短消息 */ @OnMessage public void onMessage(String mess) { System.out.println("接收到客戶端消息 --onMessage =" + mess); } /** * 這個方法與上面幾個方法不一樣。沒有用注解,是根據自己需要添加的方法。 * @param message * @throws IOException */ public void sendMessage(String message) throws IOException{ this.session.getBasicRemote().sendText(message); //this.session.getAsyncRemote().sendText(message); } /** * 給指定的人發送消息 * @param message */ public void sendToUser(String toUserNo, String message) { String now = getNowTime(); try { HtmlSocketServer htmlSocketServer = webSocketMap.get(toUserNo) ; if ( htmlSocketServer != null ) { htmlSocketServer.sendMessage(now + "用戶" + userNo + "發來消息:" + message); } else { System.out.println("當前接收信息的用戶不在線"); } } catch (IOException e) { e.printStackTrace(); } } /** * 給所有人發消息 * @param message */ public void sendToAll(String message) { String now = getNowTime(); //遍歷HashMap for (String key : webSocketMap.keySet()) { try { //判斷接收用戶是否是當前發消息的用戶 if ( !userNo.equals(key) ) { webSocketMap.get(key).sendMessage(now + "用戶" + userNo + "發來消息:" + message); } } catch (IOException e) { e.printStackTrace(); } } } /** * 獲取當前時間 * * @return */ private String getNowTime() { Date date = new Date(); DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String time = format.format(date); return time; } public static synchronized int getOnlineCount() { return onlineCount; } public static synchronized void addOnlineCount() { HtmlSocketServer.onlineCount++; } public static synchronized void subOnlineCount() { HtmlSocketServer.onlineCount--; } }
WebMvcConfig

package com.cloud.simulator.configuration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.springframework.web.socket.server.standard.ServerEndpointExporter; /** * Spring MVC 配置 */ @Configuration public class WebMvcConfig implements WebMvcConfigurer { private final Logger logger = LoggerFactory.getLogger(WebMvcConfig.class); //服務器支持跨域 @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("*") .allowedMethods("GET", "POST","OPTIONS") .allowedHeaders("*") .exposedHeaders("Access-Control-Allow-Headers", "Access-Control-Allow-Methods", "Access-Control-Allow-Origin", "Access-Control-Max-Age", "X-Frame-Options") .allowCredentials(false) .maxAge(3600); } /** * The bean shown in the preceding example registers any @ServerEndpoint * annotated beans with the underlying WebSocket container. When deployed to a * standalone servlet container, this role is performed by a servlet container * initializer, and the ServerEndpointExporter bean is not required. * * @return * 在Spring中可以直接使用Java WebSocket API來提供服務,如果使用內置的web容器,需要做的僅僅是需要在下面添加 */ /** 注入ServerEndpointExporter,這個bean會自動注冊使用了@ServerEndpoint注解聲明的Websocket endpoint 。 * 要注意,如果使用獨立的servlet容器,而不是直接使用springboot的內置容器,就不要注入ServerEndpointExporter,因為它將由容器自己提供和管理。*/ @Bean public ServerEndpointExporter serverEndpointExporter() { return new ServerEndpointExporter(); } }
共同學習,共同進步,若有補充,歡迎指出,謝謝!