首先将与Spring有关的jar包的版本都升级为4.0.0以上,并且在web.xml中将<web-app>里面的version改为"3.0"。然后在web.xml添加以下配置:
<servlet>
<servlet-name>websocketServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:META-INF/spring/websocketmessage-applicationContext.xml</param-value>
</init-param>
<async-supported>true</async-supported>
</servlet>
<servlet-mapping>
<servlet-name>websocketServlet</servlet-name>
<url-pattern>/websocket/*</url-pattern>
</servlet-mapping>
在后台需要添加两个类,HandshakeInterceptor和WebsocketEndPoint。
HandshakeInterceptor类的作用创建握手,即前台与后台进行连接。其实现为:
public class HandshakeInterceptor extends HttpSessionHandshakeInterceptor {
private static final PlatformLogger logger = PlatformLogger.getLogger(WebsocketEndPoint.class);
@Override
public boolean beforeHandshake(ServerHttpRequest request,
ServerHttpResponse response, WebSocketHandler wsHandler,
Map<String, Object> attributes) throws Exception {
logger.info("Before Handshake");
return super.beforeHandshake(request, response, wsHandler, attributes);
}
@Override
public void afterHandshake(ServerHttpRequest request,
ServerHttpResponse response, WebSocketHandler wsHandler,
Exception ex) {
logger.info("After Handshake");
super.afterHandshake(request, response, wsHandler, ex);
}
}
WebsocketEndPoint是websocket处理类
public class WebsocketEndPoint extends TextWebSocketHandler {
private static final PlatformLogger logger = PlatformLogger.getLogger(WebsocketEndPoint.class);
private static Map<WebSocketSession,String> map=new HashMap<WebSocketSession, String>(); // 一个session对应一个message 维护 session和message的关系
private static Map<WebSocketSession,Long> sessionAndAppIdMap= new HashMap<WebSocketSession,Long>(); //维护session和appId的关系,
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
//以下三行获得此session对应的租户
Map<String,Object> sessionAttributesMap= session.getHandshakeAttributes();
Object credential=((SecurityContext) sessionAttributesMap.values().toArray()[0]).getAuthentication().getPrincipal();
Long appId=((MyUserDetail) credential).getAppId();
Long userId=((MyUserDetail) credential).getId();
logger.info("appId= " + appId);
logger.info("userId= " + userId);
//维护起session和appId的关系,一个session可以对应多个appId
sessionAndAppIdMap.put(session,appId);
//得到前台发来的消息,里面可能包含多个sNum
String ReceivedMessage = message.getPayload();
map.put(session,ReceivedMessage);
JSONObject dataJson=new JSONObject(ReceivedMessage);
//得到sNum的数组
JSONArray sNum= dataJson.getJSONArray("sensors");
//得到前台发来的命令:取消订阅、订阅
String command=dataJson.getString("c");
if (command.equals("cancelSubscribe")){ //取消订阅
CommandProcessing.cancelSubscribe(ReceivedMessage, appId, userId);
} else if (command.equals("Subscribe")){ //订阅
CommandProcessing.subscribe(appId, userId, sNum, session);
}
super.handleTextMessage(session, message);
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
CommandProcessing.cancelSubscribe(map,session,sessionAndAppIdMap);
map.remove(session);
sessionAndAppIdMap.remove(session);
logger.info("连接关闭!");
super.afterConnectionClosed(session, status);
}
public static Map<String,List<WebSocketSession>> getWsImpMap(){
return CommandProcessing.wsImpMap;
}
}
在websocketmessage-applicationContext.xml中的配置为:
<?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:handlers>
<websocket:mapping path="/hello" handler="websocketEndPoint"/>
<websocket:handshake-interceptors>
<bean class="org.whut.platform.fundamental.websocket.handler.HandshakeInterceptor"/>
</websocket:handshake-interceptors>
</websocket:handlers>
<!-- 定义跳转的文件的前后缀 -->
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 这里的配置我的理解是自动给后面action的方法return的字符串加上前缀和后缀,变成一个 可用的url地址 -->
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
</beans>
在前台,我们需要做的工作有以下:打开一个连接,为连接创建事件监听器,断开连接,发送消息返回到服务器,关闭连接。具体代码如下:
// 创建一个Socket实例
var socket = new WebSocket(url); //url的地址为...../websocket/hello 前面的路径由你的项目决定 websocket在web.xml中的拦截路径中有配置,hello在
websocketmessage-applicationContext.xml中有配置
// 打开Socket
socket.onopen = function(event) {
// 发送一个初始化消息
socket.send('I am the client and I\'m listening!');
// 监听消息
socket.onmessage = function(event) {
console.log('Client received a message',event);
};
// 监听Socket的关闭
socket.onclose = function(event) {
console.log('Client notified socket has closed',event);
};
// 关闭Socket....
//socket.close()
};
我们可以将这些处理在前台封装在一个js中,在以后有用到websocket的时候,我们可以直接进行调用。
webSocket.js中的代码:
$.extend({
initWebSocket:function initWebSocket(s,method){
var url= $.URL.websocket.register;//配置地址的路径
ws = new WebSocket(url);
ws.onopen = function(){
setInterval(function(){$.post($.URL.user.keepAlive,null,null)},1000*60);
console.log("open"); ws.send(s);};
ws.onmessage=function (event){method(event.data);};
ws.onclose =function onclose(evt){console.log("WebSocketClosed!");};
ws.onerror =function onerror(evt){console.log("WebSocketError!");};
},
WebSocketClose:function WebSocketClose(){ws.close();ws=null},
WebSocketSend:function WebSocketSend(str){ws.send(str)},
WebSocketConnect:function WebSocketConnect(){return ws}
});
在调用websocket的初始化的时候,有一个回调方法的参数method,如果前台需要对后台传过来的数据进行处理,可以前台调用时,定义一个回调函数,然后将函数名作为参数传递过来(这里用到了闭包的技术)即可。$.initWebSocket(s,webSocketCallback);
function webSocketCallback(data){
//对data进行处理
}
如果前台需要向后台传送数据,可将数据封装在参数s中进行传送。如下面的代码:
$.initWebSocket($.subscription(data),webSocketCallback);
$.extend({
subscription:function subscription(data){
//封装data,并return data
});