本次是第二次接入spring-websocket到项目。此次接入遇到的问题与上一次完全不一样。
前端会判断当前浏览器是否支付websocket,支持的话,使用原生服务连接; 不支持的话采用sockjs库。
为了区分前端是否采用原生库的服务创建websocket,所以配置了两个Mapping-url.
前端的代码
var ws = null, ws_uri = "/sba/ws/srv"; // /sba是spring-mvc的拦截前缀。
/** websocket */
function initWebSocket(){
var ws_pre = "ws://" + window.location.host;
var protocolStr = document.location.protocol;
if(protocolStr == "https:") {
ws_pre = "wss://" + window.location.host;
}
//判断当前浏览器是否支持WebSocket。浏览器的原生ws组件,没有心跳机制,默认是5分钟超时,具体需要参照http代理的超时配置。
if ('WebSocket' in window) {
ws = new WebSocket(ws_pre + ws_uri);
}
else if ('MozWebSocket' in window) {
ws = new MozWebSocket(ws_pre + ws_uri);
}
else { // 有心跳协议,可以一直保持连接。
ws_uri = "/sba/ws/sockjs";
if(protocolStr == "https:") {
ws = new SockJS("https://"+window.location.host + ws_uri);
} else {
ws = new SockJS("http://"+window.location.host + ws_uri);
}
}
ws.onopen = function () {
alert("WS服务已开启。");
};
//这个事件是接受后端传过来的数据
ws.onmessage = function (event) {
//根据业务逻辑解析数据
var data = JSON.parse(event.data);
alert(data);
};
ws.onclose = function (event) {
alert("WS服务已关闭.");
};
ws.onerror = function (event) {
alert("WS服务出现异常:" + +event.data);
};
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:websocket="http://www.springframework.org/schema/websocket"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/websocket
http://www.springframework.org/schema/websocket/spring-websocket.xsd">
<!-- 支持原生websocket -->
<websocket:handlers allowed-origins="*">
<websocket:mapping path="/ws/srv" handler="wsMessageHandler"/>
<websocket:handshake-interceptors>
<bean class="com.jahelai.ws.websocket.WsHandshakeInterceptor"/>
</websocket:handshake-interceptors>
</websocket:handlers>
<!-- 支持sockjs -->
<websocket:handlers allowed-origins="*">
<websocket:mapping path="/ws/sockjs" handler="wsMessageHandler"/>
<websocket:handshake-interceptors>
<bean class="com.jahelai.ws.websocket.WsHandshakeInterceptor"/>
</websocket:handshake-interceptors>
<websocket:sockjs />
</websocket:handlers>
<bean id="wsMessageHandler" class="com.jahelai.ws.websocket.WsMesssageHandler"/>
</beans>
import java.util.Map;
import javax.servlet.http.HttpSession;
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 WsHandshakeInterceptor extends HttpSessionHandshakeInterceptor {
@Override
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
Map<String, Object> attributes) throws Exception {
///获取请求参数,首先我们要获取HttpServletRequest对象才能获取请求参数;当ServerHttpRequset的层次结构打开后其子类可以获取到我们想要的http对象,那么就简单了。
//我这里是把获取的请求数据绑定到session的map对象中(attributes)
ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request;
HttpSession session = servletRequest.getServletRequest().getSession(false);
if (session != null) {
String userName = (String) session.getAttribute("SESSION_USERNAME");
if(userName == null){
userName = "WEBSOCKET_USERNAME_IS_NULL";
}
// httpsession的用户标识与websocket的WebSocketSession建立关联。
attributes.put("username", session.getAttribute("username"));
}
return super.beforeHandshake(request, response, wsHandler, attributes);
}
@Override
public void afterHandshake(ServerHttpRequest request,
ServerHttpResponse response, WebSocketHandler wsHandler,
Exception ex) {
super.afterHandshake(request, response, wsHandler, ex);
}
}
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
/**
* ws服务的消息处理器。
* 接入spring-websocket的步骤:
* 1。配置ws的url,开发消息处理器,协议升级处理器
* 2。将配置文件加入到spring的上下文。
* 3。 spring-security开放ws的url校验
* 4。开发前端的连接,消息处理。
* @author jahe.lai
*
*/
public class WsMesssageHandler extends TextWebSocketHandler {
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
super.afterConnectionEstablished(session);
// username=session.getAttributes().get("username").toString()
// 将WebSocketSession与用户的标识关联,以供调用。可以Map<username, WebSocketSession>
WsSessionManager.addOnline(session);
}
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
super.handleTextMessage(session, message);
// Todo 你对交互消息的处理写在这里。
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
super.afterConnectionClosed(session, status);
WsSessionManager.remove(session);
}
}
见: http://nginx.org/en/docs/http/websocket.html