Websocket @serverendpoint 404


 

今天寫一個前后端交互的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--;
    }
    
}
View Code

 

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();
    }
    
}
View Code

 

 

共同學習,共同進步,若有補充,歡迎指出,謝謝!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM