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.
*/
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>
相关文章推荐
- Spring和Websocket相结合实现消息的推送
- iOS 设备通过 java-apns 组件实现苹果 APNs 消息推送实现
- pull方式的消息推送
- Netty系列之Netty百万级推送服务设计要点
- IOS学习之 消息推送
- iOS消息推送机制的实现
- 二维码的生成Demo
- Apple Push Notification Services in iOS 6 Tutorial: Part 1/2
- Apple Push Notification Services in iOS 6 Tutorial: Part 2/2
- 【iOS】远程消息推送
- Android消息推送
- 关于消息推送
- 基于java-flex-blazeds的消息推送
- Android消息推送
- 基于spring4 的websocket 简单示例
- APNS - Apple Push Notification Service
- 魔推mpush:IOS开发之数字证书及其原理
- 魔推mpush:当消息推送service被杀以后
- 魔推mpush:消息推送的大学问,别把用户惹毛了!
- 魔推mpush:我们应该占领用户的手机吗?