<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    TWaver - 專注UI技術

    http://twaver.servasoft.com/
    posts - 171, comments - 191, trackbacks - 0, articles - 2
      BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理

    HTML5 WebSocket 應用示例

    Posted on 2012-05-03 11:52 TWaver 閱讀(4840) 評論(2)  編輯  收藏
    繼續上一篇《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 - WebSocket服務類
  • WebSocket - 對應一個WebSocket客戶端
  • WebSocket.Conllection - 代表一個WebSocket連接
  • 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實例添加到這個列表,當客戶端關閉時,則從這個列表中刪除。
     1 public class AlarmServlet extends org.eclipse.jetty.websocket.WebSocketServlet {
     2     private final Set<AlarmWebSocket> clients;//保存客戶端列表
     3 
     4     public AlarmServlet() {
     5         initDatas();//初始化數據
     6     }
     7 
     8     @Override
     9     public WebSocket doWebSocketConnect(HttpServletRequest request, String protocol) {
    10         return new AlarmWebSocket();
    11     }
    12     //
    13 }

    AlarmWebSocket

    來看看AlarmWebSocket的實現,這里定義的是一個內部類,實現了接口
    org.eclipse.jetty.websocket.WebSocket.OnTextMessage的三個方法:onOpen/onMessage/onClose,這三個方法分別在連接建立,收到客戶端消息,關閉連接時回調,如果需要向客戶端發送消息,可以通過
    Connection#sendMessage(...)方法,消息統一使用JSON格式,下面是具體實現:
     1    class AlarmWebSocket implements org.eclipse.jetty.websocket.WebSocket.OnTextMessage
     2    {
     3        WebSocket.Connection connection;
     4     @Override
     5     public void onOpen(Connection connect) {
     6         this.connection = connect;
     7         clients.add(this);
     8         sendMessage(this, "reload", loadDatas());
     9     }
    10     @Override
    11     public void onClose(int code, String message) {
    12         clients.remove(this);
    13     }
    14     @Override
    15     public void onMessage(String message) {
    16         Object json = JSON.parse(message);
    17         if(!(json instanceof Map)){
    18             return;
    19         }
    20         //解析消息,jetty中json數據將被解析成map對象
    21         Map map = (Map)json;
    22         //通過消息中的信息,更新后臺數據模型
    23         
    24         //處理消息,通知到其他各個客戶端
    25         for(AlarmWebSocket client : clients){
    26             if(this.equals(client)){
    27                 continue;
    28             }
    29             sendMessage(client, null, message);
    30         }
    31     }
    32 }
    33 private void sendMessage(AlarmWebSocket client, String action, String message){
    34     try {
    35         if(message == null || message.isEmpty()){
    36             message = "\"\"";
    37         }
    38         if(action != null){
    39             message = "{\"action\":\"" + action + "\", \"data\":" + message + "}";
    40         }
    41         client.connection.sendMessage(message);
    42     } catch (IOException e) {
    43         e.printStackTrace();
    44     }
    45 }

    后臺配置

    后臺配置如serlvet相同,這里設置的url名稱為/alarmServer 
     1 <?xml version="1.0" encoding="UTF-8"?>
     2 <web-app
     3     xmlns="http://java.sun.com/xml/ns/javaee"
     4     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     5     xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
     6     metadata-complete="false"
     7     version="3.0">
     8     <servlet>
     9         <servlet-name>alarmServlet</servlet-name>
    10         <servlet-class>web.AlarmServlet</servlet-class>
    11         <load-on-startup>1</load-on-startup>
    12     </servlet>
    13 
    14     <servlet-mapping>
    15         <servlet-name>alarmServlet</servlet-name>
    16         <url-pattern>/alarmServer</url-pattern>
    17     </servlet-mapping>
    18 </web-app>

    前臺部分

    看看前臺的大體結構,創建websocket連接,監聽相關事件,比如onmessage事件,可以收到后臺發送的信息(JSON格式),解析后更新到界面,詳細的處理函數將稍后介紹
     1 function init(){
     2     window.WebSocket = window.WebSocket || window.MozWebSocket;
     3     if (!window.WebSocket){
     4         alert("WebSocket not supported by this browser");
     5         return;
     6     }
     7     var websocket = new WebSocket("ws://127.0.0.1:8080/alarm/alarmServer");
     8     websocket.onopen = onopen;
     9     websocket.onclose = onclose;
    10     websocket.onmessage = onmessage;
    11     
    12 }
    13 function onmessage(evt){
    14     var data = evt.data;
    15     if(!data){
    16         return;
    17     }
    18     data = stringToJson(data);
    19     if(!data){
    20         return;
    21     }
    22     
    23 }
    24 function jsonToString(json){
    25     return JSON.stringify(json);
    26 }
    27 function stringToJson(str){
    28     try{
    29         str = str.replace(/\'/g, "\"");
    30         return JSON.parse(str);
    31     }catch(error){
    32         console.log(error);
    33     }
    34 }
     

    WebSocket前后臺流程

    業務實現


    數據模型

    本例需要用到三種業務類型,節點,連線和告警,后臺分別提供了實現類,并定義了名稱,位置,線寬等屬性,此外還提供了導出json數據的功能。
     1   interface IJSON{
     2       String toJSON();
     3   }
     4   class Data{
     5       String name;
     6       public Data(String name){
     7           this.name = name;
     8       }
     9   }
    10   class Node extends Data implements IJSON{
    11       public Node(String name, double x, double y){
    12           super(name);
    13           this.x = x;
    14           this.y = y;
    15       }
    16       double x, y;
    17       public String toJSON(){
    18           return "{\"name\":\"" + name + "\", \"x\":\"" + x + "\",\"y\":\"" + y + "\"}";
    19       }
    20   }
    21   class Link extends Data implements IJSON{
    22       public Link(String name, String from, String to, int width){
    23           super(name);
    24           this.from =from;
    25           this.to = to;
    26           this.width = width;
    27       }
    28       String from;
    29       String to;
    30       int width = 2;
    31       public String toJSON(){
    32           return "{\"name\":\"" + name + "\", \"from\":\"" + from + "\", \"to\":\"" + to + "\", \"width\":\"" + width + "\"}";
    33       }
    34   }
    35   class Alarm implements IJSON{
    36       public Alarm(String elementName, String alarmSeverity){
    37           this.alarmSeverity = alarmSeverity;
    38           this.elementName = elementName;
    39       }
    40       String alarmSeverity;
    41       String elementName;
    42 @Override
    43 public String toJSON() {
    44     return "{\"elementName\": \"" + elementName + "\", \"alarmSeverity\": \"" + alarmSeverity + "\"}";
    45 }
    46   }
    后臺維持三個數據集合,分別存放節點,連線和告警信息,此外elementMap以節點名稱為鍵,便于節點的快速查找 
    1 Map<String, Data> elementMap = new HashMap<String, AlarmServlet.Data>();
    2 List<Node> nodes = new ArrayList<AlarmServlet.Node>();
    3 List<Link> links = new ArrayList<AlarmServlet.Link>();
    4 List<Alarm> alarms = new ArrayList<AlarmServlet.Alarm>();

    初始化數據

    在servlet構造中,我們添加了些模擬數據,在客戶端建立連接時(AlarmWebSocket#onOpen(Connection connection)),后臺將節點連線和告警信息以JSON格式發送到前臺(sendMessage(this, "reload", loadDatas());)
     1  public AlarmServlet() {
     2     initDatas();
     3     
     4 }
     5 
     6 public void initDatas() {
     7     int i = 0;
     8     double cx = 350, cy = 230, a = 250, b = 180;
     9     nodes.add(new Node("center", cx, cy));
    10     double angle = 0, perAngle = 2 * Math.PI/10;
    11     while(i++ < 10){
    12         Node node = new Node("node_" + i, cx + a * Math.cos(angle), cy + b * Math.sin(angle));
    13         elementMap.put(node.name, node);
    14         nodes.add(node);
    15         angle += perAngle;
    16     }
    17     i = 0;
    18     while(i++ < 10){
    19         Link link = new Link("link_" + i, "center", "node_" + i, 1 + random.nextInt(10));
    20         elementMap.put(link.name, link);
    21         links.add(link);
    22     }
    23 }
    24 
    25 private String loadDatas(){
    26     StringBuffer result = new StringBuffer();
    27    result.append("{\"nodes\":");
    28     listToJSON(nodes, result);
    29     result.append(", \"links\":");
    30     listToJSON(links, result);
    31     result.append(", \"alarms\":");
    32     listToJSON(alarms, result);
    33     result.append("}");
    34     return result.toString();
    35 }
    36 
    37    class AlarmWebSocket implements org.eclipse.jetty.websocket.WebSocket.OnTextMessage
    38    {
    39            
    40     @Override
    41     public void onOpen(Connection connect) {
    42         this.connection = connect;
    43         clients.add(this);
    44         sendMessage(this, "reload", loadDatas());
    45    }
    46            
    47    }

    初始數據前臺展示

    初始數據通過后臺的sendMessage(...)方法推送到客戶端,客戶端可以在onmessage回調函數中收到,本例我們使用twaver html5組件來展示這些信息。TWaver組件的使用流程一如既往,先作數據轉換,將JSON數據轉換成TWaver的網元類型,然后填充到ElementBox數據容器,最后關聯上Network拓撲圖組件,代碼如下:
      1 <!DOCTYPE html>
      2 <html>
      3 <head>
      4     <title>TWaver HTML5 Demo - Alarm</title>
      5     <script type="text/javascript" src="./twaver.js"></script>
      6     <script type="text/javascript">
      7         var box, network, nameFinder;
      8         function init(){
      9             network = new twaver.network.Network();
     10             box = network.getElementBox();
     11             nameFinder = new twaver.QuickFinder(box, "name");
     12 
     13             var networkDom = network.getView();
     14             networkDom.style.width = "100%";
     15             networkDom.style.height = "100%";
     16             document.body.appendChild(networkDom);
     17 
     18             window.WebSocket = window.WebSocket || window.MozWebSocket;
     19             if (!window.WebSocket){
     20                 alert("WebSocket not supported by this browser");
     21                 return;
     22             }
     23             var websocket = new WebSocket("ws://127.0.0.1:8080/alarm/alarmServer");
     24             
     25             websocket.onmessage = onmessage;
     26 
     27         }
     28         
     29         function onmessage(evt){
     30             var data = evt.data;
     31             if(!data){
     32                 return;
     33             }
     34             data = stringToJson(data);
     35             if(!data){
     36                 return;
     37             }
     38             var action = data.action;
     39             if(!action){
     40                 return;
     41             }
     42             if(action == "alarm.clear"){
     43                 box.getAlarmBox().clear();
     44                 return;
     45             }
     46             data = data.data;
     47             if(!data){
     48                 return;
     49             }
     50             if(action == "reload"){
     51                 reloadDatas(data);
     52                 return;
     53             }
     54             if(action == "alarm.add"){
     55                 newAlarm(data)
     56                 return;
     57             }
     58             if(action == "node.move"){
     59                 modeMove(data);
     60                 return;
     61             }
     62         }
     63 
     64         function reloadDatas(datas){
     65             box.clear();
     66             var nodes = datas.nodes;
     67             var links = datas.links;
     68             var alarms = datas.alarms;
     69 
     70             for(var i=0,l=nodes.length; i < l; i++){
     71                 var data = nodes[i];
     72                 var node = new twaver.Node();
     73                 node.setName(data.name);
     74                 node.setCenterLocation(parseFloat(data.x), parseFloat(data.y));
     75                 box.add(node);
     76             }
     77 
     78             for(var i=0,l=links.length; i < l; i++){
     79                 var data = links[i];
     80                 var from = findFirst(data.from);
     81                 var to = findFirst(data.to);
     82                 var link = new twaver.Link(from, to);
     83                 link.setName(data.name);
     84                 link.setStyle("link.width", parseInt(data.width));
     85                 box.add(link);
     86             }
     87 
     88             var alarmBox = box.getAlarmBox();
     89             for(var i=0,l=alarms.length; i < l; i++){
     90                 newAlarm(alarms[i]);
     91             }
     92         }
     93         function findFirst(name){
     94             return nameFinder.findFirst(name);
     95         }
     96         function newAlarm(data){
     97             var element = findFirst(data.elementName);
     98             var alarmSeverity = twaver.AlarmSeverity.getByName(data.alarmSeverity);
     99             if(!element || !alarmSeverity){
    100                 return;
    101             }
    102             addAlarm(element.getId(), alarmSeverity, box.getAlarmBox());
    103         }
    104         function addAlarm(elementID,alarmSeverity,alarmBox){
    105             var alarm = new twaver.Alarm(null, elementID,alarmSeverity);
    106             alarmBox.add(alarm);
    107         }
    108         function modeMove(datas){
    109             for(var i=0,l=datas.length; i<l; i++){
    110                 var data = datas[i];
    111                 var node = findFirst(data.name);
    112                 if(node){
    113                     var x = parseFloat(data.x);
    114                     var y = parseFloat(data.y);
    115                     node.setCenterLocation(x, y);
    116                 }
    117             }
    118         }
    119         
    120     </script>
    121 </head>
    122 <body onload="init()" style="margin:0;"></body>
    123 </html>

    界面效果

    ?

    后臺推送告警,前臺實時更新

    增加后臺推送告警的代碼,這里我們在后臺起了一個定時器,每隔兩秒產生一條隨機告警,或者清除所有告警,并將信息推送給所有的客戶端
    后臺代碼如下:
     1 public AlarmServlet() {
     2     
     3     Timer timer = new Timer();
     4     timer.schedule(new TimerTask() {
     5         @Override
     6         public void run() {
     7             if(random.nextInt(10) == 9){
     8                 alarms.clear();
     9                 sendMessage ("alarm.clear", "");
    10                 return;
    11             }
    12             sendMessage("alarm.add", randomAlarm());
    13         }
    14     }, 0, 2000);
    15 }
    16 public void sendMessage(String action, String message) {
    17     for(AlarmWebSocket client : clients){
    18         sendMessage(client, action, message);
    19     }
    20 }
    21 private Random random = new Random();
    22 private Data getRandomElement(){
    23     if(random.nextBoolean()){
    24         return nodes.get(random.nextInt(nodes.size()));
    25     }
    26     return links.get(random.nextInt(links.size()));
    27 }
    28 String[] alarmSeverities = new String[]{"Critical", "Major", "Minor", "Warning", "Indeterminate"};
    29 private String randomAlarm(){
    30     Alarm alarm = new Alarm(getRandomElement().name, alarmSeverities[random.nextInt(alarmSeverities.length)]);
    31     alarms.add(alarm);
    32     return alarm.toJSON();
    33 }
    34 

    前臺代碼:
    客戶端接收到消息后,需要對應的處理,增加對"alarm.clear"和"alarm.add"的處理,這樣告警就能實時更新了
     1 function onmessage(evt){
     2     
     3     if(action == "alarm.clear"){
     4         box.getAlarmBox().clear();
     5         return;
     6     }
     7     data = data.data;
     8     if(!data){
     9         return;
    10     }
    11     
    12     if(action == "alarm.add"){
    13         newAlarm(data)
    14         return;
    15     }
    16     
    17 }

    客戶端拖拽節點,同步到其他客戶端

    最后增加拖拽同步,監聽network網元拖拽監聽,在網元拖拽放手后,將節點位置信息發送給后臺
    前臺代碼:
     1 network.addInteractionListener(function(evt){
     2     var moveEnd = "MoveEnd";
     3     if(evt.kind.substr(-moveEnd.length) == moveEnd){
     4         var nodes = [];
     5         var selection = box.getSelectionModel().getSelection();
     6         selection.forEach(function(element){
     7             if(element instanceof twaver.Node){
     8                 var xy = element.getCenterLocation();
     9                 nodes.push({name: element.getName(), x: xy.x, y: xy.y});
    10             }
    11         });
    12         websocket.send(jsonToString({action: "node.move", data: nodes}));
    13     }
    14 });
    后臺接收到節點位置信息后,首先更新后臺數據(節點位置),然后將消息轉發給其他客戶端,這樣各個客戶端就實現了同步操作
    后臺代碼
     1    class AlarmWebSocket implements org.eclipse.jetty.websocket.WebSocket.OnTextMessage
     2    {
     3            
     4     @Override
     5     public void onMessage(String message) {
     6         Object json = JSON.parse(message);
     7         if(!(json instanceof Map)){
     8             return;
     9         }
    10         Map map = (Map)json;
    11         Object action = map.get("action");
    12         Object data = map.get("data");
    13         if("node.move".equals(action)){
    14             if(!(data instanceof Object[])){
    15                 return;
    16             }
    17             Object[] nodes = (Object[])data;
    18             for(Object nodeData : nodes){
    19                 if(!(nodeData instanceof Map) || !((Map)nodeData).containsKey("name") || !((Map)nodeData).containsKey("x") || !((Map)nodeData).containsKey("y")){
    20                     continue;
    21                 }
    22                 String name = ((Map)nodeData).get("name").toString();
    23                 Data element = elementMap.get(name);
    24                 if(!(element instanceof Node)){
    25                     continue;
    26                 }
    27                 double x = Double.parseDouble(((Map)nodeData).get("x").toString());
    28                 double y = Double.parseDouble(((Map)nodeData).get("y").toString());
    29                 ((Node)element).x = x;
    30                 ((Node)element).y = y;
    31             }
    32 
    33         }else{
    34             return;
    35         }
    36         for(AlarmWebSocket client : clients){
    37             if(this.equals(client)){
    38                 continue;
    39             }
    40             sendMessage(client, null, message);
    41         }
    42     }
    43 }

    完整代碼

    代碼:webSocketDemo

    結構: 

    評論

    # re: HTML5 WebSocket 應用示例  回復  更多評論   

    2012-05-07 12:09 by 條碼打印機
    大體結構還是不錯的

    只有注冊用戶登錄后才能發表評論。


    網站導航:
     
    主站蜘蛛池模板: 亚洲AV无码乱码麻豆精品国产| 亚洲啪啪综合AV一区| 激情综合亚洲色婷婷五月| 成人浮力影院免费看| 亚洲av无码国产综合专区| 18勿入网站免费永久| 中文字幕在线观看亚洲视频| 嫩草视频在线免费观看| 亚洲成AV人片高潮喷水| 亚洲第一区在线观看| 中文成人久久久久影院免费观看| 亚洲人成伊人成综合网久久久| a毛片免费全部播放完整成| 久久精品国产亚洲AV麻豆王友容| 久久w5ww成w人免费| 亚洲一级大黄大色毛片| 男女啪啪永久免费观看网站| 黄色网址免费在线| 国产亚洲日韩一区二区三区| 午夜影院免费观看| 亚洲综合校园春色| 亚洲AV无码乱码精品国产| 在线免费视频你懂的| 中文字幕亚洲综合久久| 免费观看一级毛片| 国产人成网在线播放VA免费| 亚洲永久永久永久永久永久精品| 一个人免费观看在线视频www| 美女被吸屁股免费网站| 亚洲av成人无码久久精品 | 成全在线观看免费观看大全| 久久亚洲sm情趣捆绑调教| 四虎成人免费观看在线网址| 久久精品成人免费国产片小草| 亚洲精品在线播放| 又粗又硬免费毛片| 91高清免费国产自产| 小说区亚洲自拍另类| 亚洲国产高清人在线| 国产a不卡片精品免费观看| 久久久99精品免费观看|