• websocket与node.js完美结合


    之所以写下此文,是我觉得越是简单的技术往往能发挥越重要的作用,随着各种新的技术的诞生,实时web技术已经走进我们。websocket和node.js使开发实时应用非常简单,同时性能也非常高。

    关于websocket

    websocket是html5的重要feature,它直接在浏览器上对与socket的支持,这给了web开发无限的想象,虽然以前也有 flash socket+js的实现,不过毕竟不稳定,而且兼容性有很多问题,当然websocket的普及也依赖于支持html5标准的浏览器的更新,目前只有 chrome、safari、firefox 4.0等少数浏览器可以支持,不过大势所驱,加上智能移动设备的普及,websocket可以有更大的作为。

    他解决了web实时化的问题,相比传统http有如下好处:

    • 一个WEB客户端只建立一个TCP连接
    • Websocket服务端可以推送(push)数据到web客户端.
    • 有更加轻量级的头,减少数据传送量

    本文来重点来分析下。

    websocket的原理和应用

    在继续本文之前,让我们了解下websocket的原理:

    websocket通信协议实现的是基于浏览器的原生socket,这样原先只有在c/s模式下的大量开发模式都可以搬到web上来了,基本就是通过浏览器的支持在web上实现了与服务器端的socket通信。

    WebSocket没有试图在HTTP之上模拟server推送,而是直接在TCP之上定义了帧协议,因此WebSocket能够支持双向的通信。

    首先来介绍下websocket客户端与服务端建立连接的过程:

    先用js创建一个WebSocket实例,使用ws协议建立服务器连接,ws://www.cnodejs.org:8088

    ws开头是普通的websocket连接,wss是安全的websocket连接,类似于https。

    客户端与服务端建立握手,发送如下信息:
    GET /echo HTTP/1.1
    Upgrade: WebSocket
    Connection: Upgrade
    Host: http://www.cnodejs.org:8088
    Origin: http://www.cnodejs.com

    服务端会发回如下:
    HTTP/1.1 101 Web Socket Protocol Handshake
    Upgrade: WebSocket
    Connection: Upgrade
    WebSocket-Origin: http://www.cnodejs.org
    WebSocket-Location: ws://www.cnodejs.org:8088/echo

    具体的ws协议,可以参考: http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76

    我们在开发过程中不需要考虑协议的细节,因为websocket API已经帮我们封装好了。

    需要注意的是所有的通信数据都是以”x00″开头以”xFF”结尾的,并且都是UTF-8编码的。

    这个过程类似于http的建立连接过程,不同的是,建立连接之后,接下来客户端和服务端的任何交互都需要再有这个动作。客户端通过websocket API提供的如下4个事件进行编程:

    • onopen 建立连接后触发
    • onmessage 收到消息后触发
    • onerror 发生错误时触发
    • onclose 关闭连接时触发

    让我们全面了解一下websocket API,他其实非常简单,下面是所有的API:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    [Constructor(in DOMString url, in optional DOMString protocols)]
    [Constructor(in DOMString url, in optional DOMString[] protocols)]
    interface WebSocket {
      readonly attribute DOMString url;
    
      // ready state
      const unsigned short CONNECTING = 0;
      const unsigned short OPEN = 1;
      const unsigned short CLOSING = 2;
      const unsigned short CLOSED = 3;
      readonly attribute unsigned short readyState;
      readonly attribute unsigned long bufferedAmount;
    
      // networking
               attribute Function onopen;
               attribute Function onmessage;
               attribute Function onerror;
               attribute Function onclose;
      readonly attribute DOMString protocol;
      void send(in DOMString data);
      void close();
    };
    WebSocket implements EventTarget;

    详细的websocket API,可以参考此文: http://dev.w3.org/html5/websockets/

    node.js与websocket的结合

    终于讲到了正题了,node.js如何与websocket结合,websocket API是基于事件的,他是对于客户端而言,而对于服务端来说,如何来处理呢?其实可以简单的理解为实现websocket协议的socket server开发。

    node.js天生就是一个高效的服务端语言,可以直接使用 javascript直接来处理来自客户端的请求,这样如果服务端这边需要大量的业务逻辑开发,则可以直接使用node开发。通过node和 websocket的结合可以开发出很多实时性要求很高的web应用,如游戏、直播、股票、监控、IM等等。

    而node.js如何实现websocket的支持,已经有一个比较成熟的开源系统node-websocket-server: https://github.com/miksago/node-websocket-server,让我们来探究一二:

    其实原理也是很简单就是用node实现了websocket draft-76的协议,同时他对外提供了api,可以方便其他应用程序简化编程。

    它继承了node的http.Server的事件和方法,这样它简化了服务端的编程,同时可以处理http的请求。

    为了实现连接之间的通信和消息的广播,它实现了一个manager类,给每一个连接创建一个id,然后在内存中维护一个连接链表,并提供了上线和下线的自动管理。

    它还提供对以下几个事件的接口:

    • listening 当服务器准备好接受客户端请求时
    • request 当一个http 请求发生时触发
    • stream
    • close
    • clientError
    • error

    让我们看看一个node-websocket-server提供的一个server的例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    var sys = require("sys")
      , ws = require('../lib/ws/server');
    
    var server = ws.createServer({debug: true});
    
    // Handle WebSocket Requests
    server.addListener("connection", function(conn){
      conn.send("Connection: "+conn.id);
    
      conn.addListener("message", function(message){
        conn.broadcast("<"+conn.id+"> "+message);
    
        if(message == "error"){
          conn.emit("error", "test");
        }
      });
    });
    
    server.addListener("error", function(){
      console.log(Array.prototype.join.call(arguments, ", "));
    });
    
    server.addListener("disconnected", function(conn){
      server.broadcast("<"+conn.id+"> disconnected");
    });
    
    server.listen(8000);
    这个例子非常的简单,可以看到对于websocket的server端开发,我们已经不需要考虑 websocket协议的实现,他几乎有着和客户端浏览器上websocket API一样的事件,只有对连接、断开连接、消息、错误等事件进行处理,这样应用的开发就非常的灵活了。

    实例:用websocket和node.js搭建实时监控系统

    通过websocket打通了浏览器和服务端之后,我们就可以尝试搭建一个实际的应用,这里以实时监控系统为例。

    直接与linux自身监控工具的结合,将监控结果通过websocket直接更到网页上,由于建立了socket长连接,绑定iostat的标准输 出的事件,做到了真正的实时。同时可以支持对监控结果的讨论,增加了一个简单的chat,基于事件的通讯中,chat和监控同时发送完全不受影响,所以还 可以把更多的事件加入进来。

    让我们来看看这个过程:

    首先是用node.js捕获iostat的输出:

    1
    2
    3
    4
    5
    6
    var sys = require("sys")
      , ws = require('../lib/ws/server');
    
    var sys = require('sys');
    var spawn = require('child_process').spawn;
    var mon = spawn("iostat",["-I","5"]);

    spawn可以根据参数启动一个进程,同时可以对stdout, stderr, exit code进行捕获,当这些事件触发时,可以绑定我们的函数,同时捕获其输出。
    这里是iostat的标准输出:

    disk0 cpu load average
    KB/t tps MB/s us sy id 1m 5m 15m
    14.64 4 0.06 7 5 88 0.76 0.95 0.90

    我们捕获他的输出,将其发送到客户端去:

    1
    2
    3
    4
    5
      mon.stdout.on('data',function(data) {
        data = format_string(data);
        sys.puts(data);
        conn.send("#mon:"+data+"");
      });

    客户端也就是浏览器,在收到消息后,对其进行简单的字符串处理,然后就可以直接在网页中输出了。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    w.onmessage = function(e) {
        var msg = e.data;
        if(msg.match(/#mon:/)) {
            var monarr = msg.split(":")[1].split(" ");
            var body = "";
            for(var item in monarr) {
                body += "<td id='io_"+item+"'>"+monarr[item]+"</td>"
            }
            $("#iobody").html(body);
            //log(monarr[0]);
    
        }
        else
            log(e.data);
    }

    这里自定义了一个#mon的简单协议,这样可以对更多类型的输出分开处理。

    服务端和客户端总共100多行的代码,就已经实现了一个实时服务器性能监控系统。
    全部代码下载地址: http://cnodejs.googlecode.com/svn/trunk/monsocket/examples/
    (注:本程序仅在mac osx下测试通过)

    如果加上RGraph(基于html5),则可以打造更加精美的实时展现:  http://www.rgraph.net/docs/dynamic.html

    总结

    这篇文章适合node.js的初学者或者对于websocket不够了解的人,总结起来,就是以下几个点:

    • 使用websocket API可以开发web应用实时
    • websocket api和 node.js可以很完美的配合
    • node-websocket-server 封装了websocket协议,使服务端进行websocket的开发,非常的简单
    • node的易用性,使其在服务端略加编程,即可以打造一个完美的后台服务
    • node的事件驱动的机制保证了系统的高效,可以使用EventEmitter定义自己的事件触发
    • 对于命令行输出可以使用spawn来捕获,通过在web应用中充分利用linux的各种系统工具

    参考资料

    这个例子非常的简单,可以看到对于websocket的server端开发,我们已经不需要考虑 websocket协议的实现,他几乎有着和客户端浏览器上websocket API一样的事件,只有对连接、断开连接、消息、错误等事件进行处理,这样应用的开发就非常的灵活了。

  • 相关阅读:
    禅道的安装
    项目管理必看的几个网站
    禅道管理中的项目管理--组织进行任务分解
    Redis--Springboot使用
    Mapper.xml--配置map<String, List<String>>输入
    SpringBoot-Dubbo、Zk
    高并发--并发编程的三大辅助类
    笔试-2020年西山居Java笔试题(补上,一直忘记补上了)
    HttpWebRequest.GetRequestStream方法timeout【第3次操作时就超时】的原因及解决办法
    洛谷 P2613 【模板】有理数取余(高精度取余,逆元)
  • 原文地址:https://www.cnblogs.com/zhaolizhe/p/6936902.html
Copyright © 2020-2023  润新知