您的位置:首页 > 运维架构 > Tomcat

spring4+tomcat8+jdk8应用websocket

2015-11-24 16:44 459 查看
环境 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<String, Object
> 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<WebSocketSession> users = new ArrayList<WebSocketSession>();
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。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: