webSocket 使用
一.什么是webSocket?
众所周知,浏览器支持无状态的http协议,用户在浏览器端发送一个请求,服务端返回一个响应。这种响应是基于用户不断的请求才能返回。在html5之前,要实现一个实时获取业务数据的功能,或许还只能用轮询的方式,每次去发送一个请求获取服务器响应的数据。那html5到来之时,带来了一个便利的技术 webSocket.它不像http请求那样,每次要获取数据,就建立一次连接,获取完后连接就断开。webSocket 只建立一次和客户端的长连接,以后的每次活动,都无需再和浏览器客户端建立连接,提高了这种频繁请求的效率,减少了网络带宽的损耗。
二.webSocket 在项目中的运用
最近项目上有个需求,要获取电源设备的实时电流值,并以可视化的折线图展示出来。基于这块业务需求,我用webSocket实现了该功能,废话不多说了,直接贴上干货!
(1)准备webSocket所需的jar包。
这里我需要说明下,该jar包与 tomcat 自身的jar有冲突,建议不要在本项目内引用,可外置放在其他项目中引用过来。
(2)前端js代码块
//========== WebScoket 通信地址 var webScoket_url = "ws://"+location.host+"/PowerDemo/websocket_login"; var webSocket = new WebSocket(webScoket_url); //js 客户端初始化WebSocket自身事件方法 function initWebSocket(){ //接受服务端返回的消息 webSocket.onerror = function(event) { console.log("webSocket异常",event); }; //我们创建一个连接到服务器的连接时将会调用此方法。 webSocket.onopen = function(event) { //onOpen(event) console.log("创建连接打开",event); }; //接收消息事件。 webSocket.onmessage = function(event) { onMessage(event) }; webSocket.onclose = function(event){ console.log("页面的关闭",event); webSocket = null; } } function onMessage(event) { // document.getElementById('messages').innerHTML +=event.data+'<br />'; // console.log(event+"==="+event.data); commonService.publishNotify("getMessageEvent", event.data); }
webScoket_url 是请求Java后台的地址,访问方式是"ws://"开头+服务IP和端口,在加上后台的请求配置地址。 最终拼接的效果 :"ws://"+location.host+"/PowerDemo/websocket_login"
前端websocekt 自带四个监听事件,onerror,onopen,onmessage,onclose 这四个事件方法。
1.onerror: webSocket连接请求出现异常时,自动触发onerror方法。
2.onopen:初始化打开webSocket时.调用onopern方法。
3.onmessage: 浏览器客户端与后台通讯连接过程中,如有数据响应,则触发onmessage方法。
4.onclose: webSocket关闭方法,这个方法目前我运用的时候,是通过销毁webSocket对象,才触发了此方法。
/** * webSocket.readyState 状态值 CONNECTING 0 连接还没开启。 OPEN 1 连接已开启并准备好进行通信。 CLOSING 2 连接正在关闭的过程中。 CLOSED 3 连接已经关闭,或者连接无法建立。 */ vm.testScoket = function(obj){ var json_str = {"type":obj.NUMPLATE}; if(webSocket == null){ webSocket = new WebSocket(webScoket_url); $timeout(function(){ initWebSocket(); if(webSocket.readyState == 1){ webSocket.send(JSON.stringify(json_str)); } },1000) } else if(webSocket != null && webSocket.readyState == 1){ initWebSocket(); webSocket.send(JSON.stringify(json_str)); } else if(webSocket.readyState == 2 || webSocket.readyState == 3){ webSocket = new WebSocket(webScoket_url); $timeout(function(){ initWebSocket(); if(webSocket.readyState == 1){ webSocket.send(JSON.stringify(json_str)); } },1000) } //QuestionService.initModal("views/power/failure/msgModal.html","ModalController",null); }
这段代码是webSocket调用的入口,其中根据webSocket所处的不同状态,做了对应的业务处理。
(3)后台java端webSocket
package com.zdwl.scoket; import java.io.IOException; import java.util.Date; import javax.websocket.OnClose; import javax.websocket.OnMessage; import javax.websocket.OnOpen; import javax.websocket.Session; import javax.websocket.server.ServerEndpoint; import net.sf.json.JSONObject; import net.sf.json.JsonConfig; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import com.zdwl.comm.JqgridPage; import com.zdwl.comm.util.JsonDateValueProcessor; import com.zdwl.comm.util.SpringUtil; import com.zdwl.model.vo.power.PowerEvent; import com.zdwl.service.power.PowerEventQueryServiceImpl; @Configuration @EnableWebMvc @ServerEndpoint(value = "/websocket") public class BusPowerWebScoket extends SpringUtil{ @OnMessage public void onMessage(String message, Session session) throws IOException, InterruptedException { PowerEventQueryServiceImpl eventQueryService=(PowerEventQueryServiceImpl)super.getBean("powerEventQueryService"); //接受前端的web 消息 System.out.println("接收: " + message); JSONObject jo=null; try { jo = new JSONObject().fromObject(message); System.out.println("==="+jo.get("rows")+"======"+jo.toString()); PowerEvent event=new PowerEvent(); String beginTime =jo.get("beginDate")+""; String date = null;//shoveFrontSecondForGetDate(5*60); event.setBeginDate(date); JqgridPage page = new JqgridPage(); String pageSize = jo.get("rows")+""; String curPageNo =jo.get("page")+""; if (curPageNo != null) { page.setCurPageNo(new Integer(curPageNo)); } if (pageSize != null) { page.setPageSize(new Integer(pageSize)); } int sentMessages = 0; while(true){ Thread.sleep(3000); eventQueryService.queryRealTimeCurrentInfo(page, event, null); JsonConfig jsonConfig = new JsonConfig(); jsonConfig.registerJsonValueProcessor(Date.class, new JsonDateValueProcessor()); JSONObject json = JSONObject.fromObject(page,jsonConfig); String str = json.toString(); //发送文本消息 session.getBasicRemote().sendText(str); sentMessages++; } } catch (Exception e) { e.printStackTrace(); } } @OnOpen public void onOpen() { System.out.println("客户端链接。。。"); } @OnClose public void onClose() { System.out.println("链接关闭。。。。"); } /*public static void main(String[] args) { shoveFrontSecondForGetDate(120); }*/ /*public static String shoveFrontSecondForGetDate(int secs){ Date date = new Date(); Long ll = date.getTime() - secs*1000L; SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); // System.out.println("当前时间:"+sdf.format(date)); date= new Date(ll); System.out.println("推前"+secs+"秒的时间:"+sdf.format(date)); return sdf.format(date); }*/ }
代码中的message参数,是前端首次建立webSocket对象传送过来的消息,仅且只传送一回。
接着在后台跑一个死循环的获取消息数据业务,此处我用Spring ApplicationContext容器获取了service类,贴上代码如下:
package com.zdwl.comm.util; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; public class SpringUtil implements ApplicationContextAware{ private static ApplicationContext appContext; @Override public void setApplicationContext(ApplicationContext paramApplicationContext) throws BeansException { appContext = paramApplicationContext; } public static Object getBean(String paramString){ return appContext.getBean(paramString); } }
在循环体中,运用session对象的方法,发送数据消息 session.getBasicRemote().sendText(str);
然后前端不断接受数据消息,实现了实时获取数据的功能。
三.存在的问题
如何终止这个webSocket的请求呢?目前我还没有找到好的方法去终止,用了最暴力的关闭当前浏览器选项卡,终止了webSocket的长连接。
如果有好的解决方案的小伙伴,欢迎留言,万分感谢!
四.参考资源
http://qiita.com/tnakagawa/items/f7c764d044ba56d9e0fd (赞)
http://www.oschina.net/translate/java-ee-html5-websocket-example
https://www.tutorialspoint.com/websockets/websockets_closing_connection.htm