spring4+tomcat8+jdk8应用websocket

环境 JDK8.0 + Spring4.2.3 + Tomcat8.0 

    websocket是html5的特性之一,现有主流的浏览器都已经支持websocket,websocket也成了为Java EE7的规范之一。目前jetty、tomcat等主流的应用服务器都支持websocket协议。tomcat自7以上支持websocket协议,tomcat7和tomcat8还是有些不同。tomcat8对websocket有了更好的支持和样例,在webapp\examples下面有websocket的样例,可以直接运行,java文件在example的WEB-INF目录下,和class放在一起,非常方便。

    spring4也增加了对websocket的API支持,目前有些容器对JSR-356规范还未实现,且不同版本的容器实现方式也有不同,使用spring提供的API可以做到对websocket的开发统一。

一. JAVA API for websocket JSR-356

1. 非常简单,服务器段只需要使用@ServerEndpoint标识就可以,具体使用方法:

    在需要设置为websocket服务的类上面添加@ServerEndpoint(value = 'abcd'),实现对应的方法即可。

import javax.websocket.OnClose;
import javax.websocket.OnOpen;
import javax.websocket.server.ServerEndpoint;

import org.springframework.context.annotation.Configuration;

@Configuration
@ServerEndpoint(value = "/abcd")
public class RealEndpoint {
	@OnOpen
	public void onOpen() {
		System.out.println("Client connected");
	}

	@OnClose
	public void onClose() {
		System.out.println("Connection closed");
	}
}

2. 在客户端界面使用

var host = 'ws://' + window.location.host + '/THSCADAWEB/collectionList';
var webSocket;
if ('WebSocket' in window) {
	webSocket = new WebSocket(host);
} else if ('MozWebSocket' in window) {
	webSocket = new MozWebSocket(host);
} else {
	Ext.Msg.alert('提示', '当前浏览器不支持websocket,请更换浏览器');
    return;
}

webSocket.onerror = function(event) {
	console.log('error');
};

webSocket.onopen = function(event) {
	console.log('open');
};

webSocket.onmessage = function(event) {
	var data = event.data;
	console.log('onmessage');
	var address = data.split(';')[0];
	var status = data.split(';')[1];
	var collectionStore = Ext.getCmp('deployCollectionGrid').getStore();
	var rec = collectionStore.findExact('remote_address', address);
	if(rec > -1){
		unit = collectionStore.getAt(rec).set('collection_connStatus',status);
	}else{
		collectionStore.add({
			'remote_address' : address,
			'collection_connStatus' : status
		});
	}	
};


当然复杂点也可以判断浏览器是否支持websocket之类的,可以借鉴tomcat8里面的样例。

3.  所用到的包

     如果只使用JAVA API,还需要导入tomcat下面的websocket-api.jar,这比较以来与tomcat的实现,而且直接导入还不行,直接导入tomcat下面的websocket-api.jar,部署运行的话,一直报错。Error during WebSocket handshake : Unexpected response code : 404。据说这是因为jar包冲突的原因导致。

     解决方法:删除原有项目中的java ee7.jar包,将tomcat里面的websocket-api.jar包单独复制到一个目录里面,起名tomcat-websocket,然后在项目上右键->properties->Java Build Path -> Libraries -> Add Library -> User Library -> User Libraries -> New -> User library name 输入tomcat-websocket,打勾下面的system library -> ok -> 选中刚才加入的tomcat-websocket,点击Add External JARs,加入刚才复制websocket-api.jar的目录tomcat-websocket -> 勾选建好的User library  -> Finish。

    建好后再导入就出现了javax-websocket的包,并且可以部署到tomcat8中进行运行。


二、Spring API for WebSocket

   用java的api是不是比较麻烦,但是用Spring就不用处理上述过程了。Spring从4起开始推出WebSocket API ,现在使用的是Spring4.2.3,算是较新的版本。当然上述JAVA API for websocket‘也可以在Spring工程中使用,但是还是需要注意和应用服务器jar包冲突的问题。 

1.  WebSocketConfig

     Spring通过一个配置类来注册并且管理websoceket服务

package com.th.scada.websocket;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;

import com.th.scada.real.controller.RealSocketHandler;

/**
 * Spring框架WebSocket配置管理类
 * @author guoyankun
 */
@Configuration
@EnableWebMvc
@EnableWebSocket
public class WebSocketConfig extends WebMvcConfigurerAdapter implements
		WebSocketConfigurer {
	/**
	 * 注册websocket
	 */
	@Override
	public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
		// 注册多个websocket服务,addHandler第一个参数是websocket的具体业务处理类,第二个参数collectionList相当于endpoint
		registry.addHandler(RealSocketHandler(), "/collectionList").addInterceptors(new WebSocketHandshakeInterceptor());
		// 可以注册多个websocket的endpoint
	}
	@Bean
	public WebSocketHandler RealSocketHandler() {
		return new RealSocketHandler();
	}
}


2.  Interceptor,顾名思义,拦截器,实现HandshakeInterceptor接口,处理websocket握手开始和握手结束的事件。

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.HandshakeInterceptor;

import com.th.scada.util.Constants;

public class WebSocketHandshakeInterceptor implements HandshakeInterceptor {

    private static Logger logger = LoggerFactory.getLogger(HandshakeInterceptor.class);
    @Override
    public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map attributes) throws Exception {
        if (request instanceof ServletServerHttpRequest) {
            ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request;
            HttpSession session = servletRequest.getServletRequest().getSession(false);
            if (session != null) {
                //使用userName区分WebSocketHandler,以便定向发送消息
                String userName = (String) session.getAttribute(Constants.SESSION_USERNAME);
                if(userName != null){
                	attributes.put(Constants.WEBSOCKET_USERNAME,userName);
                }
            }
        }
        return true;
    }

    @Override
    public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) {

    }
}

3.  RealSocketHandler,实现WebSocketHandler接口,处理具体的websocket信息交互内容。

import java.io.IOException;
import java.util.ArrayList;

import org.apache.log4j.Logger;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.WebSocketMessage;
import org.springframework.web.socket.WebSocketSession;

public class RealSocketHandler implements WebSocketHandler {
	private static final ArrayList users = new ArrayList();
	private static Logger logger = Logger.getLogger(RealSocketHandler.class);

	@Override
	public void afterConnectionClosed(WebSocketSession session, CloseStatus arg1) throws Exception {
		logger.debug("websocket connection closed");
		System.out.println("afterConnectionClosed");
		users.remove(session);
	}

	@Override
	public void afterConnectionEstablished(WebSocketSession session)
			throws Exception {
		logger.debug("connect to the websocket succcess ... ...");
		System.out.println("afterConnectionEstablished");
		users.add(session);
	}

	@Override
	public void handleMessage(WebSocketSession session, WebSocketMessage arg1) throws Exception {
		System.out.println("handleMessage");
	}

	@Override
	public void handleTransportError(WebSocketSession session, Throwable arg1) throws Exception {
		System.out.println("handleTransportError");
		logger.debug("websocket connection closed");
		users.remove(session);
	}

	@Override
	public boolean supportsPartialMessages() {
		return false;
	}

	/**
	 * 给所有在线用户发送消息
	 *
	 * @param message
	 *            
	 */
	public void sendMessageToUsers(TextMessage message) {
		for (WebSocketSession user : users) {
			try {
				if (user.isOpen()) {
					user.sendMessage(message);
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * 给某个用户发送消息
	 *
	 * @param userName
	 * @param message
	 */
	public void sendMessageToUser(String userName, TextMessage message) {
		for (WebSocketSession user : users) {
			if (user.getAttributes().get("websocket_username").equals(userName)) {
				try {
					if (user.isOpen()) {
						user.sendMessage(message);
					}
				} catch (IOException e) {
					e.printStackTrace();
				}
				break;
			}
		}
	}
}
4.  使用Spring API for WebSocket,就不需要tomcat里面的websocket-api.jar包了,取而代之的是spring-websocket-4.2.3.RELEASE.jar。






你可能感兴趣的:(Spring)