使用spring4实现websocket连接(二)

原创 2018-02-05 15:54 阅读(795)次

上一篇关于使用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;
    }
}


WebsocketHandler.java

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'