精华内容
下载资源
问答
  • 展开全部WebSocket protocol 是HTML5一种新的协议(protocol)。它是实...现在,很多网站为了实现即时通讯(real-time),所用的技术都是轮询(polling)。轮询是在特定的的时间间隔(time i...

    展开全部

    WebSocket protocol 是HTML5一种新的协议(protocol)。它是实32313133353236313431303231363533e59b9ee7ad9431333363373232现了浏览器与服务器全双工通信(full-duplex)。

    现在,很多网站为了实现即时通讯(real-time),所用的技术都是轮询(polling)。轮询是在特定的的时间间隔(time interval)(如每1秒),由浏览器对服务器发出HTTP request,然后由服务器返回最新的数据给客服端的浏览器。这种传统的HTTP request d的模式带来很明显的缺点 – 浏览器需要不断的向服务器发出请求(request),然而HTTP request 的header是非常长的,里面包含的数据可能只是一个很小的值,这样会占用很多的带宽。

    而最比较新的技术去做轮询的效果是Comet – 用了AJAX。但这种技术虽然可达到全双工通信,但依然需要发出请求(reuqest)。

    在 WebSocket API,浏览器和服务器只需要要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。在此WebSocket 协议中,为我们实现即使服务带来了两大好处:

    1. Header

    互相沟通的Header是很小的-大概只有 2 Bytes

    2. Server Push

    服务器可以主动传送数据给客户端

    下面实现一个简单PUSH例子如下:

    服务端代码:

    public class InitServlet extends HttpServlet {

    /**

    *

    */

    private static final long serialVersionUID = 1L;

    private static List socketList;

    public void init(ServletConfig config) throws ServletException {

    InitServlet.socketList = new ArrayList();

    super.init(config);

    System.out.println("Server start============");

    }

    public static List getSocketList() {

    return InitServlet.socketList;

    }

    }

    public class TestWebSocketServlet extends WebSocketServlet{

    private static final Logger log = Logger.getLogger(TestWebSocketServlet.class);

    /**

    *

    */

    private static final long serialVersionUID = 1L;

    //存储链接的容器

    private static List connsList = new ArrayList();

    @Override

    protected StreamInbound createWebSocketInbound(String subProtocol,HttpServletRequest request) {

    // TODO Auto-generated method stub

    return new WebSocketMessageInbound();

    }

    public class WebSocketMessageInbound extends MessageInbound{

    @Override

    protected void onClose(int status) {

    // InitServlet.getSocketList().remove(this);

    super.onClose(status);

    log.debug("onClose");

    InitServlet.getSocketList().remove(this);

    }

    @Override

    protected void onOpen(WsOutbound outbound) {

    log.debug("onOpen");

    super.onOpen(outbound);

    InitServlet.getSocketList().add(this);

    }

    @Override

    protected void onBinaryMessage(ByteBuffer message) throws IOException {

    // TODO Auto-generated method stub

    log.debug("onBinaryMessage");

    }

    @Override

    protected void onTextMessage(CharBuffer message) throws IOException {

    // TODO Auto-generated method stub

    log.debug("onTextMessage="+message);

    // this.getWsOutbound().writeTextMessage(CharBuffer.wrap("===="));

    // this.getWsOutbound().writeTextMessage(message);

    //发送给所有链接的

    for (MessageInbound messageInbound : InitServlet.getSocketList()) {

    CharBuffer buffer = CharBuffer.wrap(message);

    WsOutbound outbound = messageInbound.getWsOutbound();

    outbound.writeTextMessage(buffer);

    outbound.flush();

    }

    }

    }

    }

    web.xml配置

    initServlet

    com.demo.websocket.InitServlet

    1

    websocket

    com.demo.websocket.TestWebSocketServlet

    websocket

    /websocket

    前台代码:

    WebSoket Demo

    //验证浏览器是否支持WebSocket协议

    if (!window.WebSocket) {

    alert("WebSocket not supported by this browser!");

    }

    var ws;

    function display() {

    //var valueLabel = document.getElementById("valueLabel");

    //valueLabel.innerHTML = "";

    ws=new WebSocket("ws://localhost:8082/springmvc/websocket");

    //监听消息

    ws.onmessage = function(event) {

    //valueLabel.innerHTML+ = event.data;

    log(event.data);

    };

    // 打开WebSocket

    ws.onclose = function(event) {

    //WebSocket Status:: Socket Closed

    };

    // 打开WebSocket

    ws.onopen = function(event) {

    //WebSocket Status:: Socket Open

    发送一个初始化消息

    ws.send("Hello, Server!");

    };

    ws.onerror =function(event){

    //WebSocket Status:: Error was reported

    };

    }

    var log = function(s) {

    if (document.readyState !== "complete") {

    log.buffer.push(s);

    } else {

    document.getElementById("contentId").innerHTML += (s + "\n");

    }

    }

    function sendMsg(){

    var msg=document.getElementById("messageId");

    //alert(msg.value);

    ws.send(msg.value);

    }

    Send

    < /html>

    已赞过

    已踩过<

    你对这个回答的评价是?

    评论

    收起

    展开全文
  • 我们上一篇《基于 WebSocket 实现 WebGL 3D 拓扑图实时数据通讯同步(一)》主要讲解了如何搭建一个实时数据通讯服务器,客户端与服务端是如何通讯的,相信通过上一篇的讲解,再配合上数据库的数据储存,我们就可以...

    我们上一篇《基于 WebSocket 实现 WebGL 3D 拓扑图实时数据通讯同步(一)》主要讲解了如何搭建一个实时数据通讯服务器,客户端与服务端是如何通讯的,相信通过上一篇的讲解,再配合上数据库的数据储存,我们就可以实现一个简易版的 Web 聊天工具了,有空的朋友可以自己尝试下实现,那么我们今天的主要内容真的是实现 WebGL 3D 拓扑图实时数据通讯了,请大家接着往下看。

     

    有了前面的知识储备,我们就可以来真正实现我们 3D 拓扑图组件上节点位置信息的实时数据同步了,毋庸置疑,节点的位置信息必须是在服务端统筹控制,才能达到实时数据同步,也就是说,我们必须在服务端创建 DataModel 来管理节点,创建 ForceLayout 弹力布局节点位置,并在节点位置改变的过程中,实时地将位置信息推送到客户端,让每个客户端都更新各自页面上面的节点位置。

    在服务端我们该如何创建 HT 的 DataModel 和 ForceLayout 呢?其实也很简单,我们可以看看下面的代码:

     

    var ht = global.ht = this.ht = require('../../../build/ht-debug.js').ht,
        dataModel = new ht.DataModel(),
        reloadModel = require("../util.js").reloadModel;
    reloadModel(dataModel, { A: 3, B: 5 });
    
    require("../../../build/ht-forcelayout-debug.js");
    var forceLayout = new ht.layout.Force3dLayout(dataModel);
    forceLayout.onRelaxed = function() {
        var result = {};
        dataModel.each(function(data) {
            if (data instanceof ht.Node) {
                result[data.getTag()] = data.p3();
            }
        });
        io.emit('result', result);
    };
    forceLayout.start();

     

    我们通过 require 将非 Node.js 模块包引入到程序中,并加以使用。在上面的代码中,我们确实创建了 HT 的拓扑节点,是通过 util.js 文件中的 relowdModel 方法创建的节点,那这个文件中到底是怎么实现创建 HT 拓扑节点的呢?接下来就来看看具体的实现:

     

    function createNode(dataModel, id){
        var node = new ht.Node();
        node.setId(id);
        node.setTag(id);
        node.s3(40, 40, 40);
        node.s({
            'shape3d': 'sphere',
            'note': id,
            'note.position': 17,
            'note.background': 'yellow',
            'note.color': 'black',
            'note.autorotate': true,
            'note.face': 'top'
        });
        dataModel.add(node);
        return node;
    }
    
    function createEdge(dataModel, source, target){
        var edge = new ht.Edge(source, target);
        edge.s({
            'edge.width': 10,
            'shape3d.color': '#E74C3C',
            'edge.3d': true
        });
        dataModel.add(edge);
        return edge;
    }
    
    function reloadModel(dataModel, info){
        dataModel.clear();
    
        var ip = "192.168.1.";
        var count = 0;
        var root = createNode(dataModel, ip + count++);
    
        for (var i = 0; i < info.A; i++) {
            var iNode = createNode(dataModel, ip + count++);
            createEdge(dataModel, root, iNode);
    
            for (var j = 0; j < info.B; j++) {
                var jNode = createNode(dataModel, ip + count++);
                createEdge(dataModel, iNode, jNode);
            }
        }
    }
    
    this.reloadModel = reloadModel;

     

    在这个文件中,封装了创建节点的方法 createNode,和创建连线的方法 createEdge,最后是通过 reloadModel 方法将前面的两个方法连接起来,在这个文件的最后,我们可以看到,只公开了 reloadModel 的函数接口。

    当然光这些是不够的,这些还不能够达成实时数据通讯的功能,我们还需要监听和派发一些事件才能够达到效果,那么我们都监听了什么借口,派发了什么事件呢?

     

    io.on('connection', function(socket) {
        socket.emit('ready', dataModel.serialize(0));
    
        console.log('a user connected');
        socket.on('disconnect', function() {
            console.log('user disconnected');
        });
    
        socket.on('moveMap', function(moveMap) {
            dataModel.sm().cs();
            for (var id in moveMap) {
                var data = dataModel.getDataByTag(id);
                if (data) {
                    data.p3(moveMap[id]);
                    dataModel.sm().as(data);
                }
            }
        });
    });

     

    上面那串代码是我们的事件监听,我们通过监听 moveMap 的事件,并获取从客户端传递上来的移动的节点坐标信息,根据参数的内容,我们将其改变服务端的 DataModel 中对应节点的坐标,改变后 ForceLayout 就会根据当前的状态去调整整个拓扑上所有节点的位置。那么在调节的过程中,我们是怎么知道 ForceLayout 是正在调整的呢?在前面介绍如何在 Node.js 上面创建 HT 相关的组件时贴出来的代码中就告诉我么怎么做了。 

    在创建 ForceLayout 组件的代码后面,紧跟着就是重载 ForceLayout 组件的 onRelaxed 方法,每次布局玩后,都会调用这个方法,这样我们就可以在这个方法中,编辑获取到 DataModel 中的所有节点的当前位置,并通过 io.emit 方法通知给所有的客户端,让客户端去实时更新对应节点的坐标位置。

    但是还有一个问题,我们要怎么样让客户端显示的节点和服务端上的节点一一对应呢?首先不能让客户端自己创建节点,我们的做法其实也很简单,虽然不能保证客户端的节点 ID 会和服务端的节点 ID 一模一样,但是我们可以保证其他关键属性是一样,因为我们利用了 HT 的序列化功能,当有客户端连接到服务器时,就会向客户端派发 ready 事件,将 DataModel 序列化的结果返回到客户端,让客户端反序列化,从而达到数据基本一致的效果。

    那么客户端和服务端的节点是如何保持一一对应的呢?首先我们得了解 HT 在获取节点对象上提供了几个方法,熟悉的朋友应该知道,有 getDataById 和 getDataByTag 两个方法,其中 ID 是 HT 系统自己维护的属性,Tag 是提供给用户自己维护其唯一性的属性,一般不建议使用 ID 作为业务上面的唯一标识,因为在序列化和反序列化时候可能会有细微的差别,很难保证反序列话后的节点 ID 和序列化前的 ID 是一样的。因此在本文中,我们是通过 Tag 属性来控制服务器和客户端的节点一一对应的。

    接下来我们来看看客户端的实现吧:

     

    <!DOCTYPE html>
    <html>
        <head>
            <meta charset="utf-8">
            <title></title>
            <script src="/socket.io/socket.io.js"></script>
            <script src="/build/ht-debug.js"></script>
            <script>
                var socket = io();
                var init = function() {
                    var dm = window.dataModel = new ht.DataModel(),
                        sm = dm.sm(),
                        g3d = new ht.graph3d.Graph3dView(dm);
                    g3d.setGridSize(100);
                    g3d.setGridGap(100);
                    g3d.setGridVisible(true);
                    g3d.addToDOM();
    
                    var moveNodes = null;
                    g3d.mi(function(evt){
                        if ( evt.kind === 'beginMove'){
                            moveNodes = sm.getSelection();
                        }
                        else if (evt.kind === 'betweenMove'){
                            moveMap = {};
                            g3d.sm().each(function(data){
                                if(data instanceof ht.Node){
                                    moveMap[data.getTag()] = data.p3();
                                    console.info(data.p3());
                                }
                            });
                            socket.emit('moveMap', moveMap);
                        }
                        else if (evt.kind === 'endMove') {
                            moveNodes = null;
                        }
                    });
    
                    socket.on('ready', function(json) {
                        dm.clear();
                        dm.deserialize(json);
                    });
    
                    socket.on('result', function (result) {
                        for(var id in result){
                            var data = dm.getDataByTag([id]);
                            if (!data)
                                continue;
                            if (moveNodes && moveNodes.indexOf(data) >= 0)
                                continue;
                            data.p3(result[id]);
                        }
                    });
                };
            </script>
        </head>
        <body οnlοad="init();">
    
        </body>
    </html>

     

    代码并不长,我来介绍下具体的实现。首先是创建 3D 拓扑图组件,并做一些设置,让场景上出现线条,然后就是监听拓扑图上面的操作,当监听到 betweenMove 时,或许当前被移动的节点位置信息,向服务器派发该信息;接下来是监听服务器的 ready 事件,在事件回调中做了反序列化的操作,但是在反序列化之前,为什么要将场景中的所有节点 Clear 掉呢?是因为页面有可能是断线重连,如果是断线重连的话,没有将场景中的节点都 Clear 掉的话,反序列化后就会有节点重叠了,而且 Tag 属性也不再是唯一的了,所以这时候操作节点的话,将会很混乱;最后呢,就是监听服务器的 result 事件,在事件的回调中,跟新回调参数中对应节点的位置信息,但是其中做了些过滤,这是过滤正在移动的节点,因为正在移动的节点位置是认为控制的,所有不需要更新其节点位置信息。

    那么实时数据通讯系列到这里就介绍完了,如有什么问题,欢迎批评指正。

    展开全文
  • 今天没有延续上一篇讲的内容,穿插一段小插曲,WebSocket 实时数据通讯同步的问题,今天我们并不是很纯粹地讲 WebSocket 相关知识,我们通过 WebGL 3D 拓扑图来呈现一个有趣的 Demo。接下来我们就看看这个实时数据...

    今天没有延续上一篇讲的内容,穿插一段小插曲,WebSocket 实时数据通讯同步的问题,今天我们并不是很纯粹地讲 WebSocket 相关知识,我们通过 WebGL 3D 拓扑图来呈现一个有趣的 Demo。接下来我们就看看这个实时数据通讯是一个什么样的套路。

    我们先来聊聊这次 Demo 的思路吧,首先我要有一个 3D 的拓扑图组件,在上面创建几个节点,然后通过拉力布局(ForceLayout)将这些节点自动布局,但是有一定,需要在不同的网页窗口下,对应节点的位置是一样的,简单地说就是不同网页窗口所呈现的节点布局是一样,而且拖动不同网页窗口中的任意的节点,都将更新所有页面窗口,让所有窗口的呈现都是一样的。

    根据上面的思路,我们该如何去规划呢?既然需要实时数据通讯,那么就需要使用 WebSocket,WebSocket 又是什么呢?WebSocket 是 HTML5 一种新的协议,它没有标准的 API,各个实现都有自己的一套 API,在这里我们就不去详细研究 WebSocket 的具体实现,我也讲不了,至少现在讲不了。

    在这里我们用比较易上手的 Node.js 的 Socket.IO 做通讯框架,Socket.IO 让长连接通讯变得无比简单,服务器再也不用等待客户端的请求就可以直接给客户端发送消息,根据这样的特性就可以实现数据通讯同步的问题。

    我们来写一个最简单的例子,将任何一个客户端发送到服务器的消息,原封不动的转发到所有连接到服务器的客户端,我们来看看要实现这样的一个功能,服务端要怎么设计。

    首先我们得搭建一个简易的 web 服务器。

    var app = require('express')();
    var http = require('http').Server(app);
    
    app.get('/', function(req, res) {
        res.end('<h1>Hello Message!</h1>');
    });
    
    http.listen(4000, function() {
        console.log('listening on *:4000');
    });

    以上的代码的 Node.js 的代码,将这串代码贴到一个 js 文件中,比如命名为 server.js 然后在 Terminal 中 cd 到 server.js 对应的文件夹下,如果 node server.js 后回车,如果发现报了 Cannot find module ‘xxx’ 的字样,那么说明你在当前目录下没有安装程序用到的相关包。那么我们在当前目录下创建一个叫 package.json 的文件,然后把下面的程序拷贝到该文件中,然后在 Terminal 中输入 npm install,等安装完后,就可以正常启动服务器了。

    {
      "name": "socket-example",
      "version": "0.0.1",
      "description": "my first socket.io app",
      "dependencies": {
        "express": "^4.10.2",
        "socket.io": "^1.4.8"
      }
    }

    启动后,你在浏览器上输入 localhost:4000 就可以看到 Hello Message! 的字样。这是最简单的 HTTP 服务器,那么我们如何在上面加上 WebSocket 的功能呢呢?眼尖的同学可能已经发现上面的 package.json 的内容已经包含了 Socket.IO,那么 Socket.IO 要怎么用呢,怎么样才能达到实时数据通讯的效果呢?

    var io = require('socket.io')(http);
    io.on('connection', function(socket) {
        console.log('a user connected');
        socket.on('disconnect', function() {
            console.log('user disconnected');
        });
    
        socket.on('message', function(msg) {
            io.emit('message', msg);
        });
    });

    在 server.js 中加入上面那串代码,就可以实现客户端之间的实时数据通讯问题。但是在浏览器输入 localhost:4000 你看到的是 Hello Message! 的字样,要怎样才能访问到具体的 html 网页内容呢?这个时候就需要稍微修改下我们的服务器了。

    app.get('/', function(req, res) {
        res.sendFile(__dirname + '/index.html');
    });

    也就是将前面提到的 res.end('<h1>Hello Message!</h1>’); 代码换成 res.sendFile(__dirname + ‘/index.html'); 做个页面跳转,从而达到访问具体 html 网页的目的,在这边是是为了满足 Demo 而做的方案,要搭建一个真正的 http 静态服务器肯定不是这样子的,搭建 http 静态服务器我在这边就不介入研究了,大家刚兴趣的话,可以自己到网上搜索学习。

    那么客户端该如何实现来展现服务端的实时通讯呢?

    <!DOCTYPE html>
    <html>
        <head>
            <meta charset="utf-8">
            <title>Socket.IO Message</title>
            <style media="screen">
                #send { font-size: 14px; }
                #msgList { list-style-type: none; margin: 10px 0px; padding: 0; }
                #msgList li { padding: 5px 10px; }
                #msgList li:nth-child(odd) { background: #eee; }
            </style>
            <script src="/socket.io/socket.io.js"></script>
            <script>
                var socket = io();
                var init = function() {
                    var input = document.getElementById('message'),
                        sendFunc = function() {
                            var msg = input.value;
                            if (!msg) return;
                            socket.emit('message', input.value);
                            input.value = '';
                        };
    
                    input.addEventListener('keyup', function(e) {
                        if (e.keyCode === 13) {
                            sendFunc();
                        }
                    });
    
                    var list = document.getElementById('msgList');
                    socket.on('message', function(msg) {
                        var li = document.createElement('li');
                        li.innerHTML = msg;
                        list.insertBefore(li, list.childNodes[0]);
                    });
    
                    var btn = document.getElementById('send');
                    btn.addEventListener('click', sendFunc);
                };
            </script>
        </head>
        <body οnlοad="init();">
            Message: <input id="message" />
            <button type="button" id="send">Send</button><br/>
            <ul id="msgList"></ul>
        </body>
    </html>

    以上代码就可以做到数据同步了,具体我来解释下。

    页面很简单,有一个 input 文本框,和一个 Send 按钮,还有一个 ul 无序列表用来显示用户发送的内容,当用户在 input 文本框中输入内容后,按下 enter 键或者点击 Send 按钮都会想服务器发送文本框中填入的内容,并且服务器会将这条消息原封不动地推送到所有的客户端中,在客户端接收到消息后,就会想 ul 无序列表中填入消息。

    这个 Demo 在 http://socket.io/get-started/chat/ 这上面比我讲得清楚,大家可以到上面详细阅读,会理解得更全面一点。

    由于篇幅的问题,我今天就介绍到这了,下一篇,我们将重点介绍前面说到的结合 HT for Web 的 3D 拓扑图组件来展现实时数据通讯的效果,让每个客户端都同步操作,效果图如上。

     

    展开全文
  • 我们上一篇《基于 WebSocket 实现 WebGL 3D拓扑图实时数据通讯同步(一)》主要讲解了如何搭建一个实时数据通讯服务器,客户端与服务端是如何通讯的,相信通过上一篇的讲解,再配合上数据库的数据储存,我们就可以...

    我们上一篇《基于 WebSocket 实现 WebGL 3D 拓扑图实时数据通讯同步(一)》主要讲解了如何搭建一个实时数据通讯服务器,客户端与服务端是如何通讯的,相信通过上一篇的讲解,再配合上数据库的数据储存,我们就可以实现一个简易版的 Web 聊天工具了,有空的朋友可以自己尝试下实现,那么我们今天的主要内容真的是实现 WebGL 3D 拓扑图实时数据通讯了,请大家接着往下看。

     

    有了前面的知识储备,我们就可以来真正实现我们 3D 拓扑图组件上节点位置信息的实时数据同步了,毋庸置疑,节点的位置信息必须是在服务端统筹控制,才能达到实时数据同步,也就是说,我们必须在服务端创建 DataModel 来管理节点,创建 ForceLayout 弹力布局节点位置,并在节点位置改变的过程中,实时地将位置信息推送到客户端,让每个客户端都更新各自页面上面的节点位置。

    在服务端我们该如何创建 HT 的 DataModel 和 ForceLayout 呢?其实也很简单,我们可以看看下面的代码:

    var ht = global.ht = this.ht = require('../../../build/ht-debug.js').ht,
        dataModel = new ht.DataModel(),
        reloadModel = require("../util.js").reloadModel;
    reloadModel(dataModel, { A: 3, B: 5 });
    
    require("../../../build/ht-forcelayout-debug.js");
    var forceLayout = new ht.layout.Force3dLayout(dataModel);
    forceLayout.onRelaxed = function() {
        var result = {};
        dataModel.each(function(data) {
            if (data instanceof ht.Node) {
                result[data.getTag()] = data.p3();
            }
        });
        io.emit('result', result);
    };
    forceLayout.start();

    我们通过 require 将非 Node.js 模块包引入到程序中,并加以使用。在上面的代码中,我们确实创建了 HT 的拓扑节点,是通过 util.js 文件中的 relowdModel 方法创建的节点,那这个文件中到底是怎么实现创建 HT 拓扑节点的呢?接下来就来看看具体的实现:

    function createNode(dataModel, id){
        var node = new ht.Node();
        node.setId(id);
        node.setTag(id);
        node.s3(40, 40, 40);
        node.s({
            'shape3d': 'sphere',
            'note': id,
            'note.position': 17,
            'note.background': 'yellow',
            'note.color': 'black',
            'note.autorotate': true,
            'note.face': 'top'
        });
        dataModel.add(node);
        return node;
    }
    
    function createEdge(dataModel, source, target){
        var edge = new ht.Edge(source, target);
        edge.s({
            'edge.width': 10,
            'shape3d.color': '#E74C3C',
            'edge.3d': true
        });
        dataModel.add(edge);
        return edge;
    }
    
    function reloadModel(dataModel, info){
        dataModel.clear();
    
        var ip = "192.168.1.";
        var count = 0;
        var root = createNode(dataModel, ip + count++);
    
        for (var i = 0; i < info.A; i++) {
            var iNode = createNode(dataModel, ip + count++);
            createEdge(dataModel, root, iNode);
    
            for (var j = 0; j < info.B; j++) {
                var jNode = createNode(dataModel, ip + count++);
                createEdge(dataModel, iNode, jNode);
            }
        }
    }
    
    this.reloadModel = reloadModel;

    在这个文件中,封装了创建节点的方法 createNode,和创建连线的方法 createEdge,最后是通过 reloadModel 方法将前面的两个方法连接起来,在这个文件的最后,我们可以看到,只公开了 reloadModel 的函数接口。

    当然光这些是不够的,这些还不能够达成实时数据通讯的功能,我们还需要监听和派发一些事件才能够达到效果,那么我们都监听了什么借口,派发了什么事件呢?

    io.on('connection', function(socket) {
        socket.emit('ready', dataModel.serialize(0));
    
        console.log('a user connected');
        socket.on('disconnect', function() {
            console.log('user disconnected');
        });
    
        socket.on('moveMap', function(moveMap) {
            dataModel.sm().cs();
            for (var id in moveMap) {
                var data = dataModel.getDataByTag(id);
                if (data) {
                    data.p3(moveMap[id]);
                    dataModel.sm().as(data);
                }
            }
        });
    });

     

    上面那串代码是我们的事件监听,我们通过监听 moveMap 的事件,并获取从客户端传递上来的移动的节点坐标信息,根据参数的内容,我们将其改变服务端的 DataModel 中对应节点的坐标,改变后 ForceLayout 就会根据当前的状态去调整整个拓扑上所有节点的位置。那么在调节的过程中,我们是怎么知道 ForceLayout 是正在调整的呢?在前面介绍如何在 Node.js 上面创建 HT 相关的组件时贴出来的代码中就告诉我么怎么做了。 

    在创建 ForceLayout 组件的代码后面,紧跟着就是重载 ForceLayout 组件的 onRelaxed 方法,每次布局玩后,都会调用这个方法,这样我们就可以在这个方法中,编辑获取到 DataModel 中的所有节点的当前位置,并通过 io.emit 方法通知给所有的客户端,让客户端去实时更新对应节点的坐标位置。

    但是还有一个问题,我们要怎么样让客户端显示的节点和服务端上的节点一一对应呢?首先不能让客户端自己创建节点,我们的做法其实也很简单,虽然不能保证客户端的节点 ID 会和服务端的节点 ID 一模一样,但是我们可以保证其他关键属性是一样,因为我们利用了 HT 的序列化功能,当有客户端连接到服务器时,就会向客户端派发 ready 事件,将 DataModel 序列化的结果返回到客户端,让客户端反序列化,从而达到数据基本一致的效果。

    那么客户端和服务端的节点是如何保持一一对应的呢?首先我们得了解 HT 在获取节点对象上提供了几个方法,熟悉的朋友应该知道,有 getDataById 和 getDataByTag 两个方法,其中 ID 是 HT 系统自己维护的属性,Tag 是提供给用户自己维护其唯一性的属性,一般不建议使用 ID 作为业务上面的唯一标识,因为在序列化和反序列化时候可能会有细微的差别,很难保证反序列话后的节点 ID 和序列化前的 ID 是一样的。因此在本文中,我们是通过 Tag 属性来控制服务器和客户端的节点一一对应的。

    接下来我们来看看客户端的实现吧:

     

    <!DOCTYPE html>
    <html>
        <head>
            <meta charset="utf-8">
            <title></title>
            <script src="/socket.io/socket.io.js"></script>
            <script src="/build/ht-debug.js"></script>
            <script>
                var socket = io();
                var init = function() {
                    var dm = window.dataModel = new ht.DataModel(),
                        sm = dm.sm(),
                        g3d = new ht.graph3d.Graph3dView(dm);
                    g3d.setGridSize(100);
                    g3d.setGridGap(100);
                    g3d.setGridVisible(true);
                    g3d.addToDOM();
    
                    var moveNodes = null;
                    g3d.mi(function(evt){
                        if ( evt.kind === 'beginMove'){
                            moveNodes = sm.getSelection();
                        }
                        else if (evt.kind === 'betweenMove'){
                            moveMap = {};
                            g3d.sm().each(function(data){
                                if(data instanceof ht.Node){
                                    moveMap[data.getTag()] = data.p3();
                                    console.info(data.p3());
                                }
                            });
                            socket.emit('moveMap', moveMap);
                        }
                        else if (evt.kind === 'endMove') {
                            moveNodes = null;
                        }
                    });
    
                    socket.on('ready', function(json) {
                        dm.clear();
                        dm.deserialize(json);
                    });
    
                    socket.on('result', function (result) {
                        for(var id in result){
                            var data = dm.getDataByTag([id]);
                            if (!data)
                                continue;
                            if (moveNodes && moveNodes.indexOf(data) >= 0)
                                continue;
                            data.p3(result[id]);
                        }
                    });
                };
            </script>
        </head>
        <body οnlοad="init();">
    
        </body>
    </html>

    代码并不长,我来介绍下具体的实现。首先是创建 3D 拓扑图组件,并做一些设置,让场景上出现线条,然后就是监听拓扑图上面的操作,当监听到 betweenMove 时,或许当前被移动的节点位置信息,向服务器派发该信息;接下来是监听服务器的 ready 事件,在事件回调中做了反序列化的操作,但是在反序列化之前,为什么要将场景中的所有节点 Clear 掉呢?是因为页面有可能是断线重连,如果是断线重连的话,没有将场景中的节点都 Clear 掉的话,反序列化后就会有节点重叠了,而且 Tag 属性也不再是唯一的了,所以这时候操作节点的话,将会很混乱;最后呢,就是监听服务器的 result 事件,在事件的回调中,跟新回调参数中对应节点的位置信息,但是其中做了些过滤,这是过滤正在移动的节点,因为正在移动的节点位置是认为控制的,所有不需要更新其节点位置信息。

    那么实时数据通讯系列到这里就介绍完了,如有什么问题,欢迎批评指正。

     

    展开全文
  • 今天没有延续上一篇讲的内容,穿插一段小插曲,WebSocket 实时数据通讯同步的问题,今天我们并不是很纯粹地讲 WebSocket 相关知识,我们通过 WebGL 3D 拓扑图来呈现一个有趣的 Demo。接下来我们就看看这个实时数据...
  • websocket(即时通讯) 制作一款在Java环境下运行的网页版机器人。 主要实现功能,聊天功能,自动回复功能,自动结算,自动下单功能 需要用到的技术 websocket mysql java,微信同步登陆。 界面图 , 2. ...
  • 这个特性意味着我们可以做一些实时同步或者协作的小程序。 这篇文章分享了一个基于WebSocket长连接的微信小程序——简单的剪刀石头布小游戏的制作过程,希望能对想要在微信小程序中使用 WebSocket 的开发者有所帮助...
  • 采用Netty信息加载实现长连接实时通讯系统,客户端可以在任何值的场景下,支持实时HTTP通讯webSocket通讯,tcp协议通讯,udp协议通讯,广播协议等通过http协议,rpc协议。包结构,实现自定义网络栈。 1:支持...
  • wensocket极易曹成同步堵塞,这里我们采用netty整合websocket,提升效率。 上篇博客介绍,netty整合websocket实现客户端向服务端发送消息,这篇博客讲解客户端如何与客户端进行通讯? ...
  • websocket往往容易发生同步阻塞,导致效率低。采用Netty整合websocket,极...上篇文章讲解了如何使用websocket实现客户端与客户端通讯,这篇文章讲解客户端消息如何存储到redis和mysql中。以及如何实现离线消息的处理。
  • WebSocket 协议提供了通过一个套接字实现全双工通信的功能。一次连接以后,会建立tcp连接,后续客户端与服务器交互为全双工方式的交互方式,客户端可以发送消息到服务端,服务端也可将消息发送给客户端。 ...
  • WebSocket总结

    2017-11-18 21:49:33
    实现了浏览器和服务器全工通信,能更好的节省服务资源和宽带并达到实时通讯的目的。 websocket是html5出的协议作用 经过一次Http请求过后,服务端可以一直给客户端发送信息,保证了实时同步,资源消耗低。不需要...

空空如也

空空如也

1 2 3 4
收藏数 77
精华内容 30
热门标签
关键字:

websocket实现同步通讯