- 浏览: 166180 次
- 性别:
- 来自: 上海
文章分类
最新评论
-
qq_21380041:
954414957@qq.com
HTML5数据可视化第二弹:打造最美3D机房 -
qq_21380041:
楼主,求代码
HTML5数据可视化第二弹:打造最美3D机房 -
luoaz:
楼主能不能传个源码的附件上来, 分享学习下呢?
HTML5+WebGL 3D机房开发实例 -
xiaosi1278:
你好能不能把你讲的源代码发一下!!做参考403541110@q ...
TWaver 3D 编辑器的使用(一)----设计3D场景以及数据绑定 -
TWaverGeek:
BigBird2012 写道您好,您能把您使用TWaver导入 ...
TWaver导入导出AutoCAD DXF图纸
继续上一篇《HTML5 WebSocket 技术介绍》的内容,本篇将以示例说明WebSocket的使用,这个示例同时结合了TWaver HTML5的使用,场景如下:后台提供拓扑数据,并以JSON格式通过WebSocket推送到各个客户端,客户端获取到拓扑信息后,通过TWaver HTML5的Network组件呈现于界面,客户端可以操作网元,操作结果通过WebSocket提交到后台,后台服务器更新并通知所有的客户端刷新界面,此外后台服务器端还会不断产生告警,并推送到各个客户端更新界面。
大体结构
准备
需要用到jetty和twaver html5,可自行下载:
jetty :http://www.eclipse.org/jetty/
twaver html5
jetty目录结构
jetty下载解压后是下面的结构,运行start.jar(java -jar start.jar)启动jetty服务器,web项目可以发布在/webapps目录中,比如本例目录/webapps/alarm/
后台部分
后台使用jetty,其使用风格延续servlet的api,可以按Serlvet的使用和部署方式来使用,本例中主要用到三个类
WebSocketServlet
全名为org.eclipse.jetty.websocket.WebSocketServlet,用于提供websocket服务,继承于HttpServlet,增加了方法public WebSocket doWebSocketConnect(HttpServletRequest request, String protocol),在客户端第一次请求websocket连接时会调用该方法,如果允许建立连接,则返回一个WebSocket实例对象,否则返回null。
本例中将定义一个AlarmServlet类,继承于WebSocketServlet,并实现doWebSocketConnect方法,返回一个 AlarmWebSocket实例,代表一个客户端。
AlarmServlet
AlarmWebSocket中有个clients属性,用于维持一个客户端(AlarmWebSocket)列表,当与客户端建立连接时,会将客户端对应的AlarmWebSocket实例添加到这个列表,当客户端关闭时,则从这个列表中删除。
public class AlarmServlet extends org.eclipse.jetty.websocket.WebSocketServlet { private final Set<AlarmWebSocket> clients;//保存客户端列表 public AlarmServlet() { initDatas();//初始化数据 } @Override public WebSocket doWebSocketConnect(HttpServletRequest request, String protocol) { return new AlarmWebSocket(); } //... }
AlarmWebSocket
来看看AlarmWebSocket的实现,这里定义的是一个内部类,实现了接口org.eclipse.jetty.websocket.WebSocket.OnTextMessage的三个方法:onOpen/onMessage/onClose,这三个方法分别在连接建立,收到客户端消息,关闭连接时回调,如果需要向客户端发送消息,可以通过Connection#sendMessage(…)方法,消息统一使用JSON格式,下面是具体实现:
class AlarmWebSocket implements org.eclipse.jetty.websocket.WebSocket.OnTextMessage { WebSocket.Connection connection; @Override public void onOpen(Connection connect) { this.connection = connect; clients.add(this); sendMessage(this, "reload", loadDatas()); } @Override public void onClose(int code, String message) { clients.remove(this); } @Override public void onMessage(String message) { Object json = JSON.parse(message); if(!(json instanceof Map)){ return; } //解析消息,jetty中json数据将被解析成map对象 Map map = (Map)json; //通过消息中的信息,更新后台数据模型 ... //处理消息,通知到其他各个客户端 for(AlarmWebSocket client : clients){ if(this.equals(client)){ continue; } sendMessage(client, null, message); } } } private void sendMessage(AlarmWebSocket client, String action, String message){ try { if(message == null || message.isEmpty()){ message = "\"\""; } if(action != null){ message = "{\"action\":\"" + action + "\", \"data\":" + message + "}"; } client.connection.sendMessage(message); } catch (IOException e) { e.printStackTrace(); } }
后台配置
后台配置如serlvet相同,这里设置的url名称为/alarmServer
<?xml version="1.0" encoding="UTF-8"?> <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" metadata-complete="false" version="3.0"> <servlet> <servlet-name>alarmServlet</servlet-name> <servlet-class>web.AlarmServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>alarmServlet</servlet-name> <url-pattern>/alarmServer</url-pattern> </servlet-mapping> </web-app>
前台部分
看看前台的大体结构,创建websocket连接,监听相关事件,比如onmessage事件,可以收到后台发送的信息(JSON格式),解析后更新到界面,详细的处理函数将稍后介绍
function init(){ window.WebSocket = window.WebSocket || window.MozWebSocket; if (!window.WebSocket){ alert("WebSocket not supported by this browser"); return; } var websocket = new WebSocket("ws://127.0.0.1:8080/alarm/alarmServer"); websocket.onopen = onopen; websocket.onclose = onclose; websocket.onmessage = onmessage; ... } function onmessage(evt){ var data = evt.data; if(!data){ return; } data = stringToJson(data); if(!data){ return; } ... } function jsonToString(json){ return JSON.stringify(json); } function stringToJson(str){ try{ str = str.replace(/\'/g, "\""); return JSON.parse(str); }catch(error){ console.log(error); } }
WebSocket前后台流程

业务实现
数据模型
本例需要用到三种业务类型,节点,连线和告警,后台分别提供了实现类,并定义了名称,位置,线宽等属性,此外还提供了导出json数据的功能。
interface IJSON{ String toJSON(); } class Data{ String name; public Data(String name){ this.name = name; } } class Node extends Data implements IJSON{ public Node(String name, double x, double y){ super(name); this.x = x; this.y = y; } double x, y; public String toJSON(){ return "{\"name\":\"" + name + "\", \"x\":\"" + x + "\",\"y\":\"" + y + "\"}"; } } class Link extends Data implements IJSON{ public Link(String name, String from, String to, int width){ super(name); this.from =from; this.to = to; this.width = width; } String from; String to; int width = 2; public String toJSON(){ return "{\"name\":\"" + name + "\", \"from\":\"" + from + "\", \"to\":\"" + to + "\", \"width\":\"" + width + "\"}"; } } class Alarm implements IJSON{ public Alarm(String elementName, String alarmSeverity){ this.alarmSeverity = alarmSeverity; this.elementName = elementName; } String alarmSeverity; String elementName; @Override public String toJSON() { return "{\"elementName\": \"" + elementName + "\", \"alarmSeverity\": \"" + alarmSeverity + "\"}"; } }
后台维持三个数据集合,分别存放节点,连线和告警信息,此外elementMap以节点名称为键,便于节点的快速查找
Map<String, Data> elementMap = new HashMap<String, AlarmServlet.Data>(); List<Node> nodes = new ArrayList<AlarmServlet.Node>(); List<Link> links = new ArrayList<AlarmServlet.Link>(); List<Alarm> alarms = new ArrayList<AlarmServlet.Alarm>();
初始化数据
在servlet构造中,我们添加了些模拟数据,在客户端建立连接时(AlarmWebSocket#onOpen(Connection connection)),后台将节点连线和告警信息以JSON格式发送到前台(sendMessage(this, “reload”, loadDatas());)
public AlarmServlet() { initDatas(); ... } public void initDatas() { int i = 0; double cx = 350, cy = 230, a = 250, b = 180; nodes.add(new Node("center", cx, cy)); double angle = 0, perAngle = 2 * Math.PI/10; while(i++ < 10){ Node node = new Node("node_" + i, cx + a * Math.cos(angle), cy + b * Math.sin(angle)); elementMap.put(node.name, node); nodes.add(node); angle += perAngle; } i = 0; while(i++ < 10){ Link link = new Link("link_" + i, "center", "node_" + i, 1 + random.nextInt(10)); elementMap.put(link.name, link); links.add(link); } } private String loadDatas(){ StringBuffer result = new StringBuffer(); result.append("{\"nodes\":"); listToJSON(nodes, result); result.append(", \"links\":"); listToJSON(links, result); result.append(", \"alarms\":"); listToJSON(alarms, result); result.append("}"); return result.toString(); } class AlarmWebSocket implements org.eclipse.jetty.websocket.WebSocket.OnTextMessage { ... @Override public void onOpen(Connection connect) { this.connection = connect; clients.add(this); sendMessage(this, "reload", loadDatas()); } ... }
初始数据前台展示
初始数据通过后台的sendMessage(…)方法推送到客户端,客户端可以在onmessage回调函数中收到,本例我们使用twaver html5组件来展示这些信息。TWaver组件的使用流程一如既往,先作数据转换,将JSON数据转换成TWaver的网元类型,然后填充到ElementBox数据容器,最后关联上Network拓扑图组件,代码如下:
<!DOCTYPE html> <html> <head> <title>TWaver HTML5 Demo - Alarm</title> <script type="text/javascript" src="./twaver.js"></script> <script type="text/javascript"> var box, network, nameFinder; function init(){ network = new twaver.network.Network(); box = network.getElementBox(); nameFinder = new twaver.QuickFinder(box, "name"); var networkDom = network.getView(); networkDom.style.width = "100%"; networkDom.style.height = "100%"; document.body.appendChild(networkDom); window.WebSocket = window.WebSocket || window.MozWebSocket; if (!window.WebSocket){ alert("WebSocket not supported by this browser"); return; } var websocket = new WebSocket("ws://127.0.0.1:8080/alarm/alarmServer"); ... websocket.onmessage = onmessage; } ... function onmessage(evt){ var data = evt.data; if(!data){ return; } data = stringToJson(data); if(!data){ return; } var action = data.action; if(!action){ return; } if(action == "alarm.clear"){ box.getAlarmBox().clear(); return; } data = data.data; if(!data){ return; } if(action == "reload"){ reloadDatas(data); return; } if(action == "alarm.add"){ newAlarm(data) return; } if(action == "node.move"){ modeMove(data); return; } } function reloadDatas(datas){ box.clear(); var nodes = datas.nodes; var links = datas.links; var alarms = datas.alarms; for(var i=0,l=nodes.length; i < l; i++){ var data = nodes[i]; var node = new twaver.Node(); node.setName(data.name); node.setCenterLocation(parseFloat(data.x), parseFloat(data.y)); box.add(node); } for(var i=0,l=links.length; i < l; i++){ var data = links[i]; var from = findFirst(data.from); var to = findFirst(data.to); var link = new twaver.Link(from, to); link.setName(data.name); link.setStyle("link.width", parseInt(data.width)); box.add(link); } var alarmBox = box.getAlarmBox(); for(var i=0,l=alarms.length; i < l; i++){ newAlarm(alarms[i]); } } function findFirst(name){ return nameFinder.findFirst(name); } function newAlarm(data){ var element = findFirst(data.elementName); var alarmSeverity = twaver.AlarmSeverity.getByName(data.alarmSeverity); if(!element || !alarmSeverity){ return; } addAlarm(element.getId(), alarmSeverity, box.getAlarmBox()); } function addAlarm(elementID,alarmSeverity,alarmBox){ var alarm = new twaver.Alarm(null, elementID,alarmSeverity); alarmBox.add(alarm); } function modeMove(datas){ for(var i=0,l=datas.length; i<l; i++){ var data = datas[i]; var node = findFirst(data.name); if(node){ var x = parseFloat(data.x); var y = parseFloat(data.y); node.setCenterLocation(x, y); } } } ... </script> </head> <body onload="init()" style="margin:0;"></body> </html>
界面效果

后台推送告警,前台实时更新
增加后台推送告警的代码,这里我们在后台起了一个定时器,每隔两秒产生一条随机告警,或者清除所有告警,并将信息推送给所有的客户端
后台代码如下:
public AlarmServlet() { ... Timer timer = new Timer(); timer.schedule(new TimerTask() { @Override public void run() { if(random.nextInt(10) == 9){ alarms.clear(); sendMessage ("alarm.clear", ""); return; } sendMessage("alarm.add", randomAlarm()); } }, 0, 2000); } public void sendMessage(String action, String message) { for(AlarmWebSocket client : clients){ sendMessage(client, action, message); } } private Random random = new Random(); private Data getRandomElement(){ if(random.nextBoolean()){ return nodes.get(random.nextInt(nodes.size())); } return links.get(random.nextInt(links.size())); } String[] alarmSeverities = new String[]{"Critical", "Major", "Minor", "Warning", "Indeterminate"}; private String randomAlarm(){ Alarm alarm = new Alarm(getRandomElement().name, alarmSeverities[random.nextInt(alarmSeverities.length)]); alarms.add(alarm); return alarm.toJSON(); }前台代码:
客户端接收到消息后,需要对应的处理,增加对”alarm.clear”和”alarm.add”的处理,这样告警就能实时更新了
function onmessage(evt){ ... if(action == "alarm.clear"){ box.getAlarmBox().clear(); return; } data = data.data; if(!data){ return; } ... if(action == "alarm.add"){ newAlarm(data) return; } ... }
客户端拖拽节点,同步到其他客户端
最后增加拖拽同步,监听network网元拖拽监听,在网元拖拽放手后,将节点位置信息发送给后台
前台代码:
network.addInteractionListener(function(evt){ var moveEnd = "MoveEnd"; if(evt.kind.substr(-moveEnd.length) == moveEnd){ var nodes = []; var selection = box.getSelectionModel().getSelection(); selection.forEach(function(element){ if(element instanceof twaver.Node){ var xy = element.getCenterLocation(); nodes.push({name: element.getName(), x: xy.x, y: xy.y}); } }); websocket.send(jsonToString({action: "node.move", data: nodes})); } });后台接收到节点位置信息后,首先更新后台数据(节点位置),然后将消息转发给其他客户端,这样各个客户端就实现了同步操作
后台代码:
class AlarmWebSocket implements org.eclipse.jetty.websocket.WebSocket.OnTextMessage { ... @Override public void onMessage(String message) { Object json = JSON.parse(message); if(!(json instanceof Map)){ return; } Map map = (Map)json; Object action = map.get("action"); Object data = map.get("data"); if("node.move".equals(action)){ if(!(data instanceof Object[])){ return; } Object[] nodes = (Object[])data; for(Object nodeData : nodes){ if(!(nodeData instanceof Map) || !((Map)nodeData).containsKey("name") || !((Map)nodeData).containsKey("x") || !((Map)nodeData).containsKey("y")){ continue; } String name = ((Map)nodeData).get("name").toString(); Data element = elementMap.get(name); if(!(element instanceof Node)){ continue; } double x = Double.parseDouble(((Map)nodeData).get("x").toString()); double y = Double.parseDouble(((Map)nodeData).get("y").toString()); ((Node)element).x = x; ((Node)element).y = y; } }else{ return; } for(AlarmWebSocket client : clients){ if(this.equals(client)){ continue; } sendMessage(client, null, message); } } }
完整代码
结构:
发表评论
-
HTML5数据可视化第四弹:交互式地铁线路图
2016-12-13 15:25 985前言 最近特别忙, ... -
HTML5数据可视化第三弹:萌萌哒拓扑图分组
2016-12-02 09:55 2488前言 最近忙着给客户折腾一个复杂的多层嵌套关系。客 ... -
看HTML5如何化解大型复杂网络拓扑图的分组嵌套效果
2015-06-25 14:12 3624忙完3D,最近又开始用HTML5给客户开发网管界面。这次客 ... -
HTML5数据可视化第二弹:打造最美3D机房
2015-06-01 14:25 12785最近项目开发任务告一段落,刚好有时间整理这大半年的一些成果 ... -
HTML5数据可视化第一弹:彩虹爆炸图
2015-05-19 15:23 8032前言 25年过去了,Brooks博士著名的“没有银 ... -
HTML5组织结构图之名片式网元布局
2015-05-07 11:43 2468组织结构图(Organization chart)是企业的 ... -
HTML5流程图之矢量可伸展workflow
2015-05-06 17:17 11462互联网购物成了当今非 ... -
HTML5流程图之复杂关系网
2015-05-06 17:10 2238关系网图的呈现方式多种多样,今天我们给大家分享的是和弦式的 ... -
TWaver GIS制作穹顶之下的雾霾地图
2015-03-18 14:31 759“我不满意,我不想等待,我也不再推诿,我要站出来做一点什么。 ... -
TWaver HTML5之树形布局
2015-03-11 10:53 1431转眼间春节假期已经过完,作为一个职业的程序猿,不知道大家有没 ... -
TWaver 3D应用于大型数据中心(续)
2015-02-04 12:00 962在2014年11月份,我们当时发了一篇有关TWaver HT ... -
TWaver动画之雷达扫描效果
2015-01-26 14:03 1595UI和功能是好的产品的两个重要因素,很多产品往往只注重功能上 ... -
HTML5 实现Link跳线效果
2015-01-15 14:01 1012之前我们推出过Flex版 ... -
在TWaver的Tree节点上画线
2015-01-13 10:27 952论坛上有同学提出如何在tree上画引导线,之前我们Flex已 ... -
22万个木箱!TWaver 3D极限压榨
2014-12-29 11:31 900打开个门户网站都千呼万唤,我们还能期待网页上的3D技术会有 ... -
如何创建TWaver 3D的轮廓选中效果
2014-11-03 10:44 914在一般的游戏中,物体的选中效果会是这样: TWaver 3 ... -
如何创建环型、树型双层布局
2014-09-19 10:58 828TWaver的Demo中有常用的环型布局和树型布局,但是当网 ... -
服务器各项指标的图形化显示
2014-09-11 11:49 694在机房的监控时,经常需要去查看某个机柜中的某一台服务器的各项 ... -
HTML5定制全选列头
2014-09-10 14:57 717随着HTML5产品分支的不断深入使用,HTML5的需求也是越 ... -
TWaver MONO模板库新鲜出炉 精彩纷呈
2014-08-05 11:39 932MONO Design在线3D建模平台网站, www.mon ...
相关推荐
WebSocket是HTML5的一项重要技术,它为Web应用提供了全双工、低延迟的通信方式,使得服务器能够主动向客户端推送数据。WebSocket协议极大地提升了实时Web应用的性能和用户体验。在这个"html5的websocket代码示例包括...
WebSocket是HTML5中引入的一种在单个TCP连接上进行全双工通信的协议,它极大地改进了Web应用程序的实时通信能力。在这个基于Tomcat的WebSocket开发示例中,我们将深入探讨如何利用WebSocket技术来实现服务器与客户端...
标题"Spring4+Hibernate4+HTML5WebSocket简单应用示例"指出,这个示例项目是关于如何在Java后端环境中整合Spring 4、Hibernate 4框架,并利用HTML5的WebSocket技术来实现实时通信。Spring是企业级Java应用的主流框架...
下面将详细介绍.NET中的WebSocket实现以及在给定的示例中需要注意的关键点。 首先,让我们从服务端开始。在.NET控制台程序中,我们可以创建一个WebSocket服务器,监听特定端口并处理连接请求。以下是一个简单的...
在本示例中,我们将探讨如何使用Spring Boot 2.0开发WebSocket应用程序。WebSocket协议是一种在客户端和服务器之间实现全双工通信的标准,允许数据实时双向传输,这在需要实时更新的应用场景,如聊天应用、在线游戏...
### 使用 HTML5 WebSocket 构建实时 Web 应用 #### HTML5 WebSocket 概述与重要性 HTML5 WebSocket 是一种新兴的技术,它被设计用于在客户端(通常是浏览器)与服务器之间建立持久的双向通信连接。这项技术对于...
在HTML5WebSocket权威指南中,你将深入了解到WebSocket的原理、API用法、实战示例、以及最佳实践。这本书将帮助你熟练掌握WebSocket,构建高性能的实时Web应用程序。无论是初学者还是经验丰富的开发者,都能从中获益...
9. **调试工具**:开发WebSocket应用时,可以使用如Wireshark这样的网络嗅探器,或者Chrome开发者工具的网络面板来检查WebSocket通信。 综上所述,"Html5 WebSocket c++实例"涵盖的内容包括WebSocket的基本概念、...
WebSocket是一种在客户端和服务器之间建立长连接的协议,它提供了双向通信的能力,即服务器和客户端都可以主动发送数据。...通过不断实践和优化,你可以创建高效、稳定的WebSocket应用,满足各种实时通信需求。
为了在Jetty服务器上部署WebSocket应用,可以将WebSocket端点类打包到一个Java Web应用程序中,并通过Jetty的Web应用部署机制(如`web.xml`配置或使用Jetty的`Server`和`WebAppContext`类)进行部署。 标签"jetty...
总的来说,这个“html页面测试websocket”项目为我们提供了一个简单的WebSocket客户端示例,展示了如何在HTML和JavaScript中使用WebSocket API与服务器进行双向通信。开发者可以基于这个基础,扩展出更复杂的应用,...
在“h5websocket.zip”这个压缩包中,我们很可能是找到了一个关于HTML5 WebSocket的示例或者教程。让我们深入探讨WebSocket的基本概念、工作原理以及如何在HTML5中使用WebSocket。 WebSocket API是HTML5引入的一种...
1. "WebSocketDemo1.rar"可能包含一个简单的WebSocket应用示例,可能包括HTML、JavaScript和服务器端代码(可能是Node.js或其他语言)。通过运行这个示例,我们可以了解如何在客户端创建WebSocket对象,监听连接状态...
这个实例可能演示了如何在Tomcat环境中配置和部署WebSocket应用程序。用户可以通过编写WebSocket服务器端的Endpoint类,以及HTML5客户端的JavaScript代码,实现服务器推送消息到浏览器的功能。 2. **聊天实例**:...
总的来说,这个"Springboot+Websocket示例"展示了如何使用Spring Boot和WebSocket创建一个简单的实时通信应用。它帮助开发者理解WebSocket的基本用法以及如何在Spring Boot环境中集成WebSocket,从而实现高效、实时...
至此,我们就完成了一个基本的Spring Boot WebSocket应用。用户可以通过发送名字来获取个性化的问候,同时服务器可以广播连接状态。这个简单的示例展示了WebSocket在实时通信中的应用,以及Spring Boot和Stomp协议...
本示例将探讨如何使用C#通过WebSocket实现一个网页聊天室功能。 首先,我们需要创建WebSocket服务器。在C#中,我们可以使用HttpListener类监听WebSocket请求。HttpListener是HTTP服务器的基础,它可以处理包括...
客户端部分,本示例提供的`web.html`是一个简单的HTML页面,包含JavaScript代码来初始化WebSocket连接并发送/接收消息。JavaScript中的WebSocket对象用于与服务器通信,代码可能如下: ```html <!DOCTYPE html> ...