zoukankan      html  css  js  c++  java
  • HTML5新功能之六 《Web通信、WebSockets和跨文档消息传输》

    一、WebSockets介绍

    一、什么是WebSockets

      它可以让我们无需AJAX请求即可与服务器端对话,WebSockets是在一个(TCP)接口进行双向通信的技术,PUSH技术类型。

      同时WebSockets仍将基于W3C标准,目前为止,Chrome和Safari的最新版本浏览器已经支持WebSockets了。

     

        WebSockets可以在服务器与客户端之间建一个非HTTP的双向链接,这个链接是实时的,也是永久的,除非被显示关闭,

      就是如果服务器端向客户端发送数据,无须重新建立连接,

     

    二、什么是push技术

      所谓PUSH技术是一种基于客户服务器机制,由服务器主动的将信息发往客户端的技术

      同传统的拉技术(PULL)相比,两者最为主要的区别在于前者的是由服务器主动发送信息,而后者则是由客户机主动请求信息。

      在PUSH应用中,在服务器发送内容给客户机之前,没有明显的客户机请求,也就是说, PUSH事务是由服务器发起的。

     

    三、WebSockets  API

      创建一个webSocket对象:new WebSocket("ws://localhost:8080/socket.php");

      url必须以“ws”或“wss”文字作为开头

     

      方法:

      1、send(data):向服务器发送数据,可以是JSON

      2、close():关闭socket链接

     

      属性:

      1、readyState:获取socket对象的状态,存在以后同个值 

            * 0:表示正在链接  * 1:表示已建立链接   * 2:表示正在关闭链接   * 3:已关半链接

     

      事件:

      1、onmessage:接收服务器端向客户传回的数据 oWebSocke.onmessage = function(event) { var sData = event.data; }

      2、onopen:侦听socket的打开事件

      3、onclose:侦听socke关闭事件

     

    示例:

     1 <!DOCTYPE HTML>
     2 <html>
     3 <head>
     4 <meta charset="utf-8">
     5 <title>无标题文档</title>
     6     <style>
     7         #box { background-color: #00ffff; }
     8     </style>
     9 </head>
    10 
    11 <body>
    12 <div id="box"></div>
    13 <input type="button" id="createSocket" value="链接Socket" onClick="connent()">
    14 <input type="button" id="closeSocket" value="关闭Socket" onClick="closeSocket()">
    15 
    16 <script>
    17     var oSocket = null;
    18 
    19     function connent(){
    20         if(!window.WebSocket){
    21             return false;
    22         }
    23 
    24         // 存储socket状态信息
    25         var arrReadyState = ["正在链接", "已建立链接", "正在关闭链接", "已关闭链接"];
    26 
    27         // 创建一个WebSocket
    28         oSocket = new WebSocket("ws://localhost:8080/socket.php");
    29 
    30         var box = document.getElementById("box");
    31         box.innerHTML += "<p>状态:" + arrReadyState[oSocket.readyState] + "</p>";
    32 
    33 
    34         // 侦听是否打开
    35         oSocket.onopen = function(){
    36             box.innerHTML += "<p>状态:" + arrReadyState[oSocket.readyState] + "</p>";
    37         }
    38 
    39         // 侦听关闭socket
    40         oSocket.onclose = function(){
    41             box.innerHTML += "<p>状态:" + arrReadyState[oSocket.readyState] + "</p>";
    42         }
    43 
    44         // 侦听服务器端返回的数据
    45         oSocket.onmessage = function(event){
    46             box.innerHTML += "<p>数据:"+ event.data +"</p>";
    47         }
    48     }
    49 
    50     function closeSocket(){  // 关闭Socket()
    51         oSocket.close();
    52     }
    53 
    54 
    55 </script>
    56 </body>
    57 </html>

    二、跨文档消息传输

       跨文档消息传输功能,你可以在不同网页文档、不同端口、不同域之间进行消息的传递

      比如与子页面(iframe)之间的通信

      

      事件

      1、message:接收其它窗口消息使用message事件进行监视,window.addEventListener("message", function(){ ... }, false);

      2、

     

      属性:

      1、origin:可以获取消息的发源

      2、data:可以取得消息内容。

      3、source:可以获取消息发送源的窗口对象

     

      方法:

      1、postMessage(message,  targetOrigin):向其它窗口发消息使用该方法  window.postMessage(message,  targetOrigin)

            message:发送消息的文本      targetOrigin:接收消息的对象窗口的Url,

     

     1 <script>
     2     window.addEventListener("message", function(evt){
     3         if(evt.origin != "http://www.blur.com"){
     4             return ;
     5         }
     6         // 显示消息
     7         alert("" + evt.origin + "哪里传过来的消息:" + evt.data);
     8     }, false);
     9     
    10     function hello(){
    11         var oBox = document.getElementById("box");
    12         // 传递消息
    13         box.postMessage("你好", "http://www.blur.com");
    14     }
    15 </script>

     同域下窗口之间的通讯-iframe或新窗口打开

     1 <iframe id="ifm" src="iframe.html"></iframe>
     2 <input type="button" id="but" value="改变iframe">
     3 
     4 <script>
     5     function getId(id){
     6         return document.getElementById(id);
     7     }
     8 
     9     var oIfm = getId("ifm");
    10     var oBut = getId("but");
    11 
    12     oBut.onclick = function(){
    13         console.log("a", oIfm.contentWindow);
    14 
    15         // 如果我们要操作一个iframe的DOM元素使用contentWindow来引用
    16         // 也可以作用于一个新窗口的操作
    17         oIfm.contentWindow.document.body.style.background = "red";
    18     }
    19 </script>

     

    web通信的方法

      web通信,一个特别大的topic,涉及面也是很广的。因最近学习了 javascript 中一些 web 通信知识,在这里总结下。文中应该会有理解错误或者表述不清晰的地方,还望斧正!

    一、前言

    1. comet技术

    浏览器作为 Web 应用的前台,自身的处理功能比较有限。浏览器的发展需要客户端升级软件,同时由于客户端浏览器软件的多样性,在某种意义上,也影响了浏览器新技术的推广。在 Web 应用中,浏览器的主要工作是发送请求、解析服务器返回的信息以不同的风格显示。AJAX 是浏览器技术发展的成果,通过在浏览器端发送异步请求,提高了单用户操作的响应性。但 Web 本质上是一个多用户的系统,对任何用户来说,可以认为服务器是另外一个用户。现有 AJAX 技术的发展并不能解决在一个多用户的 Web 应用中,将更新的信息实时传送给客户端,从而用户可能在“过时”的信息下进行操作。而 AJAX 的应用又使后台数据更新更加频繁成为可能。

    随着互联网的发展,web 应用层出不穷,也不乏各种网站监控、即时报价、即时通讯系统,为了让用户得到更好的体验,服务器需要频繁的向客户端推送信息。开发者一般会采用基于 AJAX 的长轮询方式或者基于 iframe 及 htmlfile 的流方式处理。当然有些程序需要在客户端安装各种插件( Java applet 或者 Flash )来支持性能比较良好的“推”信息。

    2. HTTP协议中的长、短连接

    短连接的操作步骤是:建立连接——数据传输——关闭连接...建立连接——数据传输——关闭连接
    长连接的操作步骤是:建立连接——数据传输...(保持连接)...数据传输——关闭连接

    长连接与短连接的不同主要在于client和server采取的关闭策略不同。短连接在建立连接以后只进行一次数据传输就关闭连接,而长连接在建立连接以后会进行多次数据数据传输直至关闭连接(长连接中关闭连接通过Connection:closed头部字段)。

    二、web 通信

    首先要搞清楚,xhr 的 readystate 各种状态。

    属性 描述
    onreadystatechange 存储函数(或函数名),每当 readyState 属性改变时,就会调用该函数。
    readyState 存有 XMLHttpRequest 的状态。从 0 到 4 发生变化。

    0: 请求未初始化
    1: 服务器连接已建立
    2: 请求已接收
    3: 请求处理中
    4: 请求已完成,且响应已就绪
    status 200: "OK"
    404: 未找到页面

     

    1.轮询

    轮询是一种“拉”取信息的工作模式。设置一个定时器,定时询问服务器是否有信息,每次建立连接传输数据之后,链接会关闭。

    前端实现:

    复制代码
    var polling = function(url, type, data){
        var xhr = new XMLHttpRequest(), 
            type = type || "GET",
            data = data || null;
    
        xhr.onreadystatechange = function(){
            if(xhr.readyState == 4) {
                receive(xhr.responseText);
                xhr.onreadystatechange = null;
            }
        };
    
        xhr.open(type, url, true);
        //IE的ActiveXObject("Microsoft.XMLHTTP")支持GET方法发送数据,
        //其它浏览器不支持,已测试验证
        xhr.send(type == "GET" ? null : data);
    };
    
    var timer = setInterval(function(){
        polling();
    }, 1000);
    复制代码

    在轮询的过程中,如果因为网络原因,导致上一个 xhr 对象还没传输完毕,定时器已经开始了下一个询问,上一次的传输是否还会在队列中,这个问题我没去研究。如果感兴趣可以自己写一个ajax的请求管理队列。

    2.长轮询(long-polling)

    长轮询其实也没啥特殊的地方,就是在xhr对象关闭连接的时候马上又给他接上~ 看码:

    复制代码
    var longPoll = function(type, url){
        var xhr = new XMLHttpRequest();
    
        xhr.onreadystatechange = function(){
            // 状态为 4,数据传输完毕,重新连接
            if(xhr.readyState == 4) {
                receive(xhr.responseText);
                xhr.onreadystatechange = null;
    
                longPoll(type, url);
            }
        };
    
        xhr.open(type, url, true);
        xhr.send();
    }
    复制代码

    只要服务器断开连接,客户端马上连接,不让他有一刻的休息时间,这就是长轮询。

    3.数据流

    数据流方式,在建立的连接断开之前,也就是 readystate 状态为 3 的时候接受数据,但是麻烦的事情也在这里,因为数据正在传输,你拿到的 xhr.response 可能就是半截数据,所以呢,最好定义一个数据传输的协议,比如前2个字节表示字符串的长度,然后你只获取这个长度的内容,接着改变游标的位置。

    假如数据格式为: data splitChar   data为数据内容,splitChar为数据结束标志(长度为1)。 那么传输的数据内容为 data splitChar data splitChar data splitChar...

    复制代码
    var dataStream = function(type, url){
        var xhr = new XMLHttpRequest();
    
        xhr.onreadystatechange = function(){
    
            // 状态为 3,数据接收中
            if(xhr.readyState == 3) {
                var i, l, s;
    
                s = xhr.response; //读取数据
                l = s.length;     //获取数据长度
    
                //从游标位置开始获取数据,并用分割数据
                s = s.slice(p, l - 1).split(splitChar);
    
                //循环并操作数据
                for(i in s) if(s[i])  deal(s[i]);
    
                p = l;  //更新游标位置
    
            }
    
            // 状态为 4,数据传输完毕,重新连接
            if(xhr.readyState == 4) {
                xhr.onreadystatechange = null;
    
                dataStream(type, url);
            }
        };
    
        xhr.open(type, url, true);
        xhr.send();
    };
    复制代码

    这个代码写的是存在问题的,当readystate为3的时候可以获取数据,但是这时获取的数据可能只是整体数据的一部分,那后半截就拿不到了。readystate在数据传输完毕之前是不会改变的,也就是说他并不会继续接受剩下的数据。我们可以定时去监听readystate,这个下面的例子中可以看到。

    这样的处理不算复杂,但是存在问题。上面的轮询和长轮询是所有浏览器都支持的,所以我就没有写兼容IE的代码,但是这里,低版本IE不允许在readystate为3的时候读取数据,所以我们必须采用其他的方式来实现。

    在ajax还没有进入web专题之前,我们已经拥有了一个法宝,那就是iframe,利用iframe照样可以异步获取数据,对于低版本IE可以使用iframe来接受数据流。

    复制代码
    if(isIE){
        var dataStream = function(url){
            var ifr = document.createElement("iframe"), doc, timer;
    
            ifr.src = url;
            document.body.appendChild(ifr);
    
            doc = ifr.contentWindow.document;
    
            timer = setInterval(function(){
    
                if(ifr.readyState == "interactive"){
                    // 处理数据,同上
                }
    
                // 重新建立链接
                if(ifr.readyState == "complete"){
                    clearInterval(timer);
    
                    dataStream(url);
                }
            }, 16);
        };
    };
    复制代码

    定时去监听iframe的readystate的变化,从而获取数据流,不过,上面的处理方式还是存在问题。数据流实现“服务器推”数据的原理是什么呢,简单点说,就是文档(数据)还没有加载完,这个时候浏览器的工作就是去服务器拿数据完成文档(数据)加载,我们就是利用这点,给浏览器塞点东西过去~ 所以上述利用iframe的方式获取数据,会使浏览器一直处于加载状态,title上的那个圈圈一直在转动,鼠标的状态也是loading,这看着是相当不爽的。幸好,IE提供了HTMLFile对象,这个对象就相当于一个内存中的Document对象,它会解析文档。所以我们创建一个HTMLFile对象,在里面放置一个IFRAME来连接服务器。这样,各种浏览器就都支持了。

    复制代码
    if(isIE){
        var dataStream = function(url){
            var doc = new ActiveXObject("HTMLFile"), 
                ifr = doc.createElement("iframe"), 
                timer, d;
    
            doc.write("<body/>");
    
            ifr.src = url;
            doc.body.appendChild(ifr);
    
            d = ifr.contentWindow.document;
    
            timer = setInterval(function(){
    
                if(d.readyState == "interactive"){
                    // 处理数据,同上
                }
    
                // 重新建立链接
                if(d.readyState == "complete"){
                    clearInterval(timer);
    
                    dataStream(url);
                }
            }, 16);
        };
    };
    复制代码

    4.websocket

    websocket是前端一个神器,ajax用了这么久了,相关技术也是很成熟,不过要实现个数据的拉取确实十分不易,从上面的代码中也看到了,各种兼容性问题,各种细节处理问题,自从有了websocket,哈哈,一口气上五楼...

    复制代码
    var ws = new WebSocket("ws://www.example.com:8888");
    
    ws.onopen = function(evt){};
    ws.onmessage = function(evt){
        deal(evt.data);
    };
    ws.onclose  = function(evt){};
    
    //ws.close();
    复制代码

    新建一个WebSocket实例,一切就OK了,ws:// 是websocket的连接协议,8888为端口号码。onmessage中提供了data这个属性,相当方便

    5.EventSource

    HTML5中提供的EventSource这玩意儿,这是无比简洁的服务器推送信息的接受函数。

    new EventSource("test.php").onmessage=function(evt){
        console.log(evt.data);
    };

    简洁程度和websocket是一样的啦,只是这里有一个需要注意的地方,test.php输出的数据流应该是特殊的MIME类型,要求是"text/event-stream",如果不设置的话,你试试~ (直接抛出异常)

    6.ActionScript

    情非得已就别考虑这第六种方式了,虽说兼容性最好,要是不懂as,出了点bug你也不会调试。

    具体实现方法:在 HTML 页面中内嵌入一个使用了 XMLSocket 类的 Flash 程序。JavaScript 通过调用此 Flash 程序提供的套接口接口与服务器端的套接口进行通信。JavaScript 在收到服务器端以 XML 格式传送的信息后可以很容易地控制 HTML 页面的内容显示。

    7.Java Applet套接口

    这玩意儿原理和Flash类似,不过我不懂,就不细说了。

    三、后端处理方式

    本文主要是总结Javascript的各种通讯方式,后端配合node来处理,应该是挺给力的。

    复制代码
    var conns = new Array();
    
    var ws = require("websocket-server");
    var server = ws.createServer();
    
    server.addListener("connection", function(connection){
      console.log("Connection request on Websocket-Server");
      conns.push(connection);
      connection.addListener('message',function(msg){
            console.log(msg);
            for(var i=0; i<conns.length; i++){
                if(conns[i]!=connection){
                    conns[i].send(msg);
                }
            }
        });
    });
    server.listen(8888);
    复制代码

    下面是一个php的测试demo。

    header('Content-Type:text/html; charset=utf-8');
    while(1){
        echo date('Y-m-d H:i:s');
        flush();
        sleep(1);
    };

    四、web 通信方式利弊分析

    • 轮询,这种方式应该是最没技术含量的,操作起来最方便,不过是及时性不强,把定时器的间隔时间设置的短一些可以稍微得到缓和。
    • 长轮询,算是比较不错的一个web通讯方式,不过每次断开连接,比较耗服务器资源,客户端到无所谓。
    • 数据流,他和长轮询不同之处是接受数据的时间不一样,数据流是readystate为3的时候接受,低版本IE不太兼容,处理起来略麻烦,而且还要自己设计数据传输协议。不过他对资源的消耗比上面几种都可观。
    • websocket和EventSource,两个利器,不过,没几个浏览器支持,这是比较让人伤心~
    • ActionScript和Java Applet,两者都是需要在客户端安装插件的,一个是Flash插件,一个是Java插件,而且搞前端的人一般对这东西不太熟悉,如果没有封装比较好的库可以使用,那建议还是别用了。

    一、项目总结

      比赛直播项目,使用ajax来定时请求数据会对带宽负载较大,所以使用了websocket,

     

    问题:兼容性的websocket只有chrom和火狐下还可以支持,在其它浏览器就不可以用

    解决方法:如果浏览器兼容webSocket就使用webSocket来获取数据,如果不兼容就使用flash,通过Flash的socket去与服务器端建立链接,服务器端有数据推送过来就接收并处理

     

    一、web_socket.js

      一个对火狐、chrom和Flash的scoket做兼容的js文件

     

    二、WebSocketMain.swf

      flash的成生文件,需要修改flash-src文件夹中WebSocketMain.as,修改请求的服务器地址和端口,在使用flax生成出一个swf文件

     

    三、调用

      通过onmessage来返回数据,通过onopen和onclose来侦听链接状态

     1 function ConnentSocket(url){
     2     this.oSocket = null;
     3     this._restTiem = 10000;       // 如果断开十秒后重链
     4     this.url = url;
     5     this.box = $("#box");
     6     // 存储socket状态信息
     7     // arrReadyState = ["正在链接", "已建立链接", "正在关闭链接", "已关闭链接"];
     8     this.arrReadyState = [0, 1, 2, 3];
     9     this.connent();
    10     this.basketNavTag();
    11 }
    12 
    13 // 创建socket链接
    14 ConnentSocket.prototype.connent = function(){
    15     var _this = this;
    16     if(!window.WebSocket){
    17         alert("您的浏览器不支持WebSocket");
    18         return false;
    19     }
    20 
    21     // 创建一个WebSocket
    22     this.oSocket = new WebSocket("ws://192.168.12.24:8089/mywebsocket?play_id=100");
    23 
    24     // 侦听是否打开
    25     this.oSocket.onopen = function(){
    26         console.log("<p>状态2:" + _this.arrReadyState[_this.oSocket.readyState] + "</p>");
    27     }
    28 
    29     // 侦听关闭socket
    30     this.oSocket.onclose = function(){
    31         console.log("<p>状态3:" + _this.arrReadyState[_this.oSocket.readyState] + "</p>");
    32         var timer = null;
    33         if(_this.arrReadyState[_this.oSocket.readyState] === 3){
    34             timer = setTimeout(function(){
    35                 _this.connent();
    36             }, _this._restTiem);
    37         }
    38     }
    39 
    40     // 侦听服务器端返回的数据
    41     this.oSocket.onmessage = function(event){
    42         _this.box.html("<p>数据:"+ event.data +"</p>");;
    43         _this.insbasketData(event.data);
    44     }
    45 
    46     // 显示状态
    47     console.log("<p>状态1:" + _this.arrReadyState[_this.oSocket.readyState] + "</p>");
    48 }
    49 
    50 // 关闭socket
    51 ConnentSocket.prototype.close = function(){
    52     this.oSocket.close();
    53 }
  • 相关阅读:
    2017.2.27学习笔记-----开发板,PC,虚拟机三者ping通
    2017.2.25学习笔记
    vue基础之计算属性和侦听器
    vue基础之组件通信
    vue基础之组件创建
    vue基础之监听器
    vue基础之生命周期
    vue基础之条件渲染
    vue基础之插槽
    vue总结
  • 原文地址:https://www.cnblogs.com/couxiaozi1983/p/3548584.html
Copyright © 2011-2022 走看看