使用spring4实现websocket连接(二)
原创 2018-02-05 15:54 阅读(1767)次
上一篇关于使用spring4实现websocket连接的问题,我只是分享了大致如何实现,要想应用到实际项目中,还要考虑2个问题:
1.是否区分用户,用户连上wesocke时,每次发消息给用户时,是否要区分用户,是不是每个人收到的消息不一样,这里就涉及获取httpSession的问题了。
2.我们项目中一般不会只有一个websocket收发消息,有时一个是处理用户消息,一个是处理其他的,举个例子:批处理时的进度实时反馈等,都会用到websocket
所以针对上面这两个问题,我决定再深入研究了下,pom文件还是参考上一篇文章里的,并没有新加入jar包,下面直接上代码吧
,类也是上一篇里的那两个类,只是新加入了一些完善的代码,代码里都有提到上面这两个问题:
HandshakeInterceptor.java
package com.solr.websocket;
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 HandshakeInterceptor extends HttpSessionHandshakeInterceptor{
//websocket握手时处理httpsession,取用户标识,如果你的应用不区分用户,当然这里就可以不用取sesion了
@Override
public boolean beforeHandshake(ServerHttpRequest request,
ServerHttpResponse response, WebSocketHandler wsHandler,
Map<String, Object> attributes) throws Exception {
System.out.println("Before Handshake");
//演示怎么把httpsession放入websocketsession里,区分用户
HttpSession session = getSession(request);
if (session != null) {
String userId = session.getId();
attributes.put("userId", userId);
}
return super.beforeHandshake(request, response, wsHandler, attributes);
}
@Override
public void afterHandshake(ServerHttpRequest request,
ServerHttpResponse response, WebSocketHandler wsHandler,
Exception ex) {
}
//取httpSession
private HttpSession getSession(ServerHttpRequest request) {
if (request instanceof ServletServerHttpRequest) {
ServletServerHttpRequest serverRequest = (ServletServerHttpRequest) request;
return serverRequest.getServletRequest().getSession();
}
return null;
}
}
package com.solr.websocket;
import java.util.Date;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
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;
public class WebsocketHandler extends TextWebSocketHandler {
private final static Map<String, WebSocketSession> users = new ConcurrentHashMap<String, WebSocketSession>();
public static Map<String, WebSocketSession> getUsers() {
return users;
}
static{
SendMessageThread sendThread = new SendMessageThread();
Thread t = new Thread(sendThread);
t.start();
}
/**
* 连接成功时候,会触发页面上的onopen方法
*/
public void afterConnectionEstablished(WebSocketSession session)
throws Exception {
Map<String, Object> attributes = session.getHandshakeAttributes();
if (attributes != null && attributes.size() > 0) {
String userId = attributes.get("userId").toString();//只是为了演示这里可以取httpsession的id,如果你的应用要区分用户,就可以在此处取httpsession
System.out.println("userId:" + userId);
String websocketId = session.getId();//websocket id
users.put(websocketId, session);
}
System.out.println("connect to the websocket success......当前数量:"+ users.size());
}
/**
* 关闭连接时触发(关闭掉页面也会触发)
*/
public void afterConnectionClosed(WebSocketSession session,
CloseStatus closeStatus) throws Exception {
System.out.println("websocket connection closed......");
String websocketId = session.getId();//websocket id
users.remove(websocketId);
System.out.println("剩余在线用户" + users.size());
}
/**
* 异常处理
*/
public void handleTransportError(WebSocketSession session,
Throwable exception) throws Exception {
if (session.isOpen()) {
session.close();
}
System.out.println("websocket connection closed......");
String websocketId = session.getId();//websocket id
users.remove(websocketId);
}
// 收消息
@Override
protected void handleTextMessage(WebSocketSession session,
TextMessage message) throws Exception {
super.handleTextMessage(session, message);
System.out.println("收到消息:" + message.getPayload());
}
static class SendMessageThread implements Runnable {
public void run() {
while (true) {
try {
Map<String, WebSocketSession> map = WebsocketHandler.getUsers();
Set<String> keySet = map.keySet();
for (String key : keySet) {
WebSocketSession webSocketSession = map.get(key);
if (webSocketSession.isOpen()) {
String message = "现在时间:"+ new Date().toLocaleString();
TextMessage textMessage = new TextMessage(message);
webSocketSession.sendMessage(textMessage);
}
}
//定时5秒发一次消息
Thread.sleep(5000l);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
这样就是比较接近实际项目里的应用了,触发发送消息我上面的代码是定时5秒发一次消息,你也可以根据你的项目把调用发送消息的代码写在service或者springmvc的controller里,关键是看你的具体业务。
还有上面提到的问题2,就是要实现多个websocket来处理我们的业务,其实就是实现多个TextWebSocketHandler类,复制上面的WebsocketHandler类为WebsocketHandler2,当然你应该改成你业务需要的类名,我这只是举个例子。
另外在spring-websocket.xml的配置里加入如下配置就可以了:
<bean id="websocketHandler" class="com.solr.websocket.WebsocketHandler"/>
<bean id="websocketHandler2" class="com.solr.websocket.WebsocketHandler2"/>
<websocket:handlers>
<websocket:mapping path="/websocket" handler="websocketHandler"/>
<websocket:mapping path="/websocket2" handler="websocketHandler2"/>
<websocket:handshake-interceptors>
<bean class="com.solr.websocket.HandshakeInterceptor"/>
</websocket:handshake-interceptors>
</websocket:handlers>
这样就实现了两个websocket类来处理我们的业务了,搞定!!
注意:如果项目里有用到springMVC,需要将spring-websocket.xml文件里的配置,全部搬到springMVC的配置文件spring-mvc.xml里,也就是两个文件合并成一样文件,这样在web.xml中申明springmvc时,才能一并注册websocket的path到springmvc里,否则会报错:
WARN : No mapping found for HTTP request with URI [/websocket] in DispatcherServlet with name 'springMVC'