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方法