1. 程式人生 > >Struts 2 + websocket handshake unexpected response code 404問題

Struts 2 + websocket handshake unexpected response code 404問題

環境:

tomcat 7  + Struts 2 Spring4 MyBatis 

服務端程式碼:

@ServerEndpoint(value = "/wsServer", configurator = GetHttpSessionConfigurator.class)
public class WsServerEndpoint {
    private static final Logger LOGGER = Logger.getLogger(WsServerEndpoint.class);
    private static final SimpleDateFormat TIME_FMT =
        new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");
    private static Hashtable<String, Session> sessionMap = new Hashtable<String, Session>();
    private static Hashtable<String, HttpSession> httpSessMap =
        new Hashtable<String, HttpSession>();

    /**
     * 接收到連線開啟請求的處理方法.
     * 
     * @param session
     * @param config
     */
    @OnOpen
    public void onOpen(Session session, EndpointConfig config) {
        HttpSession httpSession = WsUtil.getHttpSessionFromCfg(config, "WsServerEndpoint.onClose");
        httpSessMap.put(session.getId(), httpSession);
        LOGGER.info(new StringBuilder("onOpen,").append(httpSession.getAttribute("name"))
            .append(",").append(httpSession.getId()));
        sessionMap.put(session.getId(), session);
    }

    /**
     * 接收到連線關閉請求的處理方法.
     * 
     * @param session
     * @param config
     */
    @OnClose
    public void onClose(Session session) {
        HttpSession httpSession = httpSessMap.get(session.getId());
        if (null != httpSession) {
            LOGGER.info(new StringBuilder("onClose,").append(httpSession.getAttribute("name"))
                .append(",").append(httpSession.getId()));
        }
        sessionMap.remove(session.getId());
    }

    /**
     * 接收到訊息請求的處理方法.
     * 
     * @param session
     * @param config
     * @throws IOException
     */
    @OnMessage
    public void onMessage(String message, Session session, EndpointConfig config)
        throws IOException {
        HttpSession httpSession = httpSessMap.get(session.getId());
        LOGGER.info(new StringBuilder("onMessage,").append(httpSession.getAttribute("name"))
            .append(",").append(httpSession.getId()));
        String name = (String) httpSession.getAttribute("name");
        String info =
            new StringBuilder().append(name).append(" has sent message :").append(message)
                .toString();
        // 回覆當前發起訊息的客戶端
        session.getBasicRemote().sendText(
            new StringBuilder("server has got your message!").append(TIME_FMT.format(new Date()))
                .toString());
        Enumeration<String> sessionIds = sessionMap.keys();
        while (null != sessionIds && sessionIds.hasMoreElements()) {
            String sessionId = sessionIds.nextElement();
            if (!session.getId().equals(sessionId)) {
                // 傳送給其他客戶端的訊息
                sessionMap.get(sessionId).getBasicRemote().sendText(info);
            }
        }
    }

    @OnError
    public void onError(Session session, Throwable e) throws IOException {
        HttpSession httpSession = httpSessMap.get(session.getId());
        if (null != httpSession) {
            LOGGER.info(new StringBuilder("onError,").append(httpSession.getAttribute("name"))
                .append(",").append(httpSession.getId()));
        }
        session.getBasicRemote().sendText(
            new StringBuilder("server encounter an exception,").append(TIME_FMT.format(new Date()))
                .append(",e=").append(e).toString());
    }
}

客戶端程式碼:
    var ws = null;
    function connect() {
        ws= new WebSocket("ws://localhost:8088/wsServer");
        ws.onopen = function () {
            setConnected(true);
        };
        ws.onmessage = function (event) {
            log('Received: ' + event.data);
        };
        ws.onclose = function (event) {
            setConnected(false);
            log('Info: connection closed.');
            log(event);
        };
    }

404現象截圖:


原因及解決方法:

已解決,原因有2個:
1、我使用的是tomcat 7容器,tomcat7本身具有對websocket的實現,會和我在pom.xml中配置的如下內容
<groupId>javax.websocket</groupId>
<artifactId>javax.websocket-api</artifactId>
<version>1.1</version>衝突,故需要增加一句
<scope>provided</scope>表示只是編譯時使用,這個大家可以參考:http://stackoverflow.com/questions/30500998/websocket-handshake-unexpected-response-code-404
2、struts中對websocket請求進行攔截了,需要配置不被攔截
在struts.properties中配置:
struts.action.excludePattern=/wsServer
注意:
1、如果想在struts.xml中配置,需先保證struts.properties檔案中沒有相同項配置,否則會覆蓋struts.xml中相同項的配置內容,如在struts.xml中配置,內容如下:
<struts>
<constant name="struts.action.excludePattern" value="/wsServer"></constant>
2、這裡配置的配置的內容,必須是request.getRequestURI()返回部分的內容,而不能配置類似於^ws://,^wss://.*,這是因為struts進行excludePattern匹配時就是使用request.getRequestURI()來進行匹配的,這個大家可以參考原始碼org.apache.struts2.dispatcher.PrepareOperations.isUrlExcluded方法