您的位置:首页 > 编程语言 > Java开发

spring websocket

2016-04-10 12:10 459 查看
在web项目中尝尝需要涉及到数据实时更新的需求,如后台监控数据,需要实时显示的进度信息,扫描信息等,之前实现的方式大部分都采用了长连接的形式,如dwr,效率较高的websocket在IE10以下版本中不受支持,让人遗憾,现在spring 4.0提供了sockjs的支持,它会根据浏览器是否支持websocket创建不同的连接,而且无需开发人员写多余的代码,解决了websocket功能在IE10以下中的问题,废话少说自己写了一个简易群聊demo(下载源码地址http://download.csdn.net/detail/u011411069/9486431),看代码说话

因消息内容需显示实时时间故继承之前已有的工具类

/**
* Created by zhang on 2016/4/3.
*/
public class MsgUtils {

static DateFormat df=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

/**
* 当前时间
* @return
*/
public static String nowTime(){
return df.format(new Date());
}
}

/**
* Created by zhang on 2016/4/3.服务端的websocket
*/
public class MsgSocketJ extends MsgUtils implements WebSocketHandler {

static ConcurrentHashMap<String,WebSocketSession> clients=new ConcurrentHashMap<>();
//此种方式的websocket服务端为单例,成员变量需注意<span style="white-space:pre">	</span>
static Logger logger = LoggerFactory.getLogger(MsgSocketJ.class);
static ConcurrentHashMap<String,SessionBean> localInfo=new ConcurrentHashMap<>();
/**
* 握手成功后执行的方法
* @param webSocketSession
* @throws Exception
*/
@Override
public void afterConnectionEstablished(WebSocketSession webSocketSession) throws Exception {
logger.debug("A new client connected!!!");
SessionBean sessionBean=new SessionBean();//自己封装的Session实体
sessionBean.setSession(webSocketSession);
localInfo.put(webSocketSession.getId(),sessionBean);//记录客户端session
}

/**
* 接收到消息后执行的方法
* @param webSocketSession
* @param webSocketMessage
* @throws Exception
*/
@Override
public void handleMessage(WebSocketSession webSocketSession, WebSocketMessage<?> webSocketMessage) throws Exception {
String message= (String) webSocketMessage.getPayload();
if(message.startsWith("$$username")){
localInfo.get(webSocketSession.getId()).setUsername(message.split("=")[1]);
clients.put(webSocketSession.getId(),webSocketSession);
broadcast(nowTime()+" 服务器消息"+ ":欢迎\""+localInfo.get(webSocketSession.getId()).getUsername()+"\"加入聊天;",true,webSocketSession);
userList();
}else{
broadcast(nowTime()+" "+localInfo.get(webSocketSession.getId()).getUsername()+":"+message,false,webSocketSession);
}
}

/**
* 发生异常时执行的方法
* @param webSocketSession
* @param throwable
* @throws Exception
*/
@Override
public void handleTransportError(WebSocketSession webSocketSession, Throwable throwable) throws Exception {
logger.error("An error triggered!!!");
logger.error(throwable.getMessage(),throwable);
clients.remove(webSocketSession.getId());
broadcast(nowTime()+" 服务器消息"+":\""+localInfo.get(webSocketSession.getId()).getUsername()+"\"离开聊天室;",true,webSocketSession);
userList();
localInfo.remove(webSocketSession.getId());
}

/**
* 连接断开时执行的方法
* @param webSocketSession
* @param closeStatus
* @throws Exception
*/
@Override
public void afterConnectionClosed(WebSocketSession webSocketSession, CloseStatus closeStatus) throws Exception {
logger.debug("A client has disconnected!!!");
clients.remove(webSocketSession.getId());//移除离线的session
broadcast(nowTime()+" 服务器消息"+":\""+localInfo.get(webSocketSession.getId()).getUsername()+"\"离开聊天室;",true,webSocketSession);
userList();
localInfo.remove(webSocketSession.getId());
}

/**
* 是否允许部分消息
* 设为true后调用webSocketMessage.getPayload()后可能读到的只是一部分消息
* 可调用webSocketMessage.isLast()判断是否读完
* @return
*/
@Override
public boolean supportsPartialMessages() {
return false;
}

/**
* 广播消息
* @param msg
*/
public static void broadcast(String msg, boolean isServer, WebSocketSession curr){
for (Map.Entry<String,WebSocketSession> entry:clients.entrySet()){
try {
if(entry.getValue().isOpen()){
if(!isServer&&entry.getValue()==curr)
continue;//跨过自己发送的消息
entry.getValue().sendMessage(new TextMessage(msg));
}else{
clients.remove(entry.getKey());
}
} catch (IOException e) {
logger.error(e.getMessage(),e);
}
}
}

/**
* 广播用户列表
*/
public static void userList(){
StringBuffer buffer=new StringBuffer();
boolean notEmpty=false;
for (Map.Entry<String,WebSocketSession> entry:clients.entrySet()) {
if(entry.getValue().isOpen()){
notEmpty=true;
buffer.append(localInfo.get(entry.getValue().getId()).getUsername()+",");
}
}
if(notEmpty){
String list=buffer.toString();
list="$$userList"+list.substring(0,list.length());
broadcast(list,true,null);
}
}

/**
* 每10秒广播一次用户列表
*/
static Timer timer=new Timer();
static{
timer.schedule(new TimerTask() {
@Override
public void run() {
userList();
}
},100,10000);
}
}
/**
* Created by zhang on 2016/4/3.
* 注册websocket
*/
@Configuration
@EnableWebMvc
@EnableWebSocket
public class SockJSConfig implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
//addInterceptors是添加握手前的拦截器,可不添加,withSockJS,开启sockJS支持
registry.addHandler(msgSocketJ(), "/msg").addInterceptors(new WebSocketHandshakeInterceptor()) .withSockJS();
}

@Bean
public MsgSocketJ msgSocketJ(){
return new MsgSocketJ();//这种@Configuration+@Bean与在xml中配置<bean />,所以与原生websocket不一样的地方就是服务端是一个单例
}
}
/**
* Created by zhang on 2016/4/3.
*/
public class WebSocketHandshakeInterceptor implements HandshakeInterceptor {
@Override
public boolean beforeHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Map<String, Object> map) throws Exception {
//在握手前执行一些操作,可拒绝连接
return true;
}

@Override
public void afterHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Exception e) {
//握手后执行
}
}
web.xml配置

<?xml version="1.0" encoding="UTF-8"?>
<!--版本需3.0以上,<async-supported>支持-->
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0" >

<display-name>Archetype Created Web Application</display-name>

<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
<!--在websocket请求中会过的servlet,filter中必须加入,不然非原生websocket会报错-->
<async-supported>true</async-supported>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/sockjs/*</url-pattern>
</servlet-mapping>

</web-app>
前台代码

<!DOCTYPE html>
<html>
<head>
<title>Hello SockJS!</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script type="text/javascript" src="js/jquery-1.12.2.js"></script>
<script type="text/javascript" src="js/sockjs-1.0.3.js"></script>
</head>
<body>
<h2>Hello SockJS!</h2>
<h2 id="h"></h2>
<label>username:</label><input name="username" maxlength="5" type="text" /><button id="login" onclick="createWs();">login</button>
<div style="width:810px;height:500px;">
<div style="width:605px;height:480px;float:left;">
<div id="div" style="border:1px solid red;height:400px; width:600px;overflow-y:auto;border-radius:5px;"></div>
<textarea id="msg" style="width:596px;height:50px;resize:none;border-radius:5px;display:blo
b6e9
ck;"></textarea>
<input type="button" value="send" onclick="sendMsg();" />
<input type="button" value="closeWs" onclick="closeWs();" />
</div>
<div id="userList" style="width:200px;height:455px;border:1px solid red;float:left;border-radius:5px;"></div>
</div>
<script type="text/javascript">
var ws,username;

function createWs() {
if($("[name=username]").val().trim().length==0){
alert("please enter your name!");
return;
}

$("#login").attr("disabled","disabled");
$("[name=username]").attr("disabled","disabled");
username=$("[name=username]").val();

ws = new SockJS("http://localhost:8080/web/sockjs/msg");
//连接成功后前台触发的函数
ws.onopen = function () {
document.getElementById("h").innerHTML="joined successful!!!";
ws.send("$$username="+$("[name=username]").val());
};
//接收到后台推送的消息后触发的函数
ws.onmessage = function (evt) {
if(evt.data.indexOf("$$userList")==0){
var list=evt.data.replace("$$userList","")
$("#userList").empty().append(replaceAllStr(list,",","<br />"))
}else{
document.getElementById("div").innerHTML+=evt.data+"<br />";
}
};
//websocket断开连接时触发的函数
ws.onclose = function (evt) {
document.getElementById("h").innerHTML="WebSocketClosed!";
};
//发生异常时触发的函数
ws.onerror = function (evt) {
document.getElementById("h").innerHTML="WebSocketError!";
};
}

/**
* 断开连接
*/
function closeWs() {
if(ws){
ws.close();
ws=null;
$("#login").removeAttr("disabled");
$("[name=username]").removeAttr("disabled");
}
}

/**
* 发送消息
*/
function sendMsg(){
if(ws){
ws.send($("#msg").val());
document.getElementById("div").innerHTML+=nowTime()+" "+username+":"+$("#msg").val()+"<br />";
$("#msg").val("");
}
}

/**
* 当前时间
* @returns {string}
*/
function nowTime() {
var date=new Date();
var MM=date.getMonth()<10?"0"+(date.getMonth()+1):date.getMonth()+1;
var dd=date.getDate()<10?"0"+(date.getDate()+1):date.getDate()+1;
var hh=date.getHours()<10?"0"+date.getHours():date.getHours();
var min=date.getMinutes()<10?"0"+date.getMinutes():date.getMinutes();
var ss=date.getSeconds()<10?"0"+date.getSeconds():date.getSeconds();
return date.getFullYear()+"-"+MM+"-"+dd+" "+hh+":"+min+":"+ss;
}

function replaceAllStr(Str,old,New){
while (Str.indexOf(old)!=-1){
Str=Str.replace(old,New);
}
return Str;
}
</script>
</body>
</html>
用到的依赖(日志未添加)
<spring.version>4.1.2.RELEASE</spring.version>
<jackson.version>2.3.1</jackson.version>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-websocket</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息