JavaScript实现与web通信的方法详解


Posted in Javascript onAugust 07, 2020

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插件,而且搞前端的人一般对这东西不太熟悉,如果没有封装比较好的库可以使用,那建议还是别用了。

五、参考资料

http://www.ibm.com/developerworks/cn/web/wa-lo-comet/  Comet:基于 HTTP 长连接的“服务器推”技术

http://blog.csdn.net/yankai0219/article/details/8208776  HTTP协议中长连接、短连接

http://www.web-tinker.com/  comet系列文章

到此这篇关于JavaScript实现与web通信的方法详解的文章就介绍到这了,更多相关JavaScript与web通信内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Javascript 相关文章推荐
(推荐一个超好的JS函数库)S.Sams Lifexperience ScriptClassLib
Apr 29 Javascript
JavaScript 对象模型 执行模型
Dec 06 Javascript
js文本框输入点回车触发确定兼容IE、FF等
Nov 19 Javascript
jQuery中$.get、$.post、$.getJSON和$.ajax的用法详解
Nov 19 Javascript
JS的数组迭代方法
Feb 05 Javascript
10个很棒的jQuery代码片段
Sep 24 Javascript
JavaScript基础篇(3)之Object、Function等引用类型
Nov 30 Javascript
jQuery自定义滚动条完整实例
Jan 08 Javascript
Bootstrap学习笔记之css样式设计(1)
Jun 07 Javascript
原生js实现可拖动的登录框效果
Jan 21 Javascript
React中使用async validator进行表单验证的实例代码
Aug 17 Javascript
vue项目使用$router.go(-1)返回时刷新原来的界面操作
Jul 26 Javascript
解决Vue-Router升级导致的Uncaught (in promise)问题
Aug 07 #Javascript
JavaScript实现多文件下载方法解析
Aug 07 #Javascript
基于javascript的无缝滚动动画1
Aug 07 #Javascript
vue-router重写push方法,解决相同路径跳转报错问题
Aug 07 #Javascript
手把手带你搭建一个node cli的方法示例
Aug 07 #Javascript
Vue两种组件类型:递归组件和动态组件的用法
Aug 06 #Javascript
vue数据更新UI不刷新显示的解决办法
Aug 06 #Javascript
You might like
php从右向左/从左向右截取字符串的实现方法
2011/11/28 PHP
利用php实现一周之内自动登录存储机制(cookie、session、localStorage)
2016/10/31 PHP
用js做一个小游戏平台 (一)
2009/12/29 Javascript
JS 对象介绍
2010/01/20 Javascript
jQuery 1.4 15个你应该知道的新特性(译)
2010/01/24 Javascript
js实现动态改变字体大小代码
2014/01/02 Javascript
JavaSacript中charCodeAt()方法的使用详解
2015/06/05 Javascript
javascript作用域问题实例分析
2015/07/13 Javascript
jQuery进行组件开发完整实例
2015/12/15 Javascript
AngularJS指令中的绑定策略实例分析
2016/12/14 Javascript
jquery Ajax实现Select动态添加数据
2017/06/08 jQuery
Js经典案例的实例代码
2018/05/10 Javascript
vue返回上一页面时回到原先滚动的位置的方法
2018/12/20 Javascript
解决vue admin element noCache设置无效的问题
2019/11/12 Javascript
vue导入.md文件的步骤(markdown转HTML)
2020/12/31 Vue.js
[49:08]OpTic vs Serenity 2018国际邀请赛小组赛BO2 第一场 8.18
2018/08/19 DOTA
flask中主动抛出异常及统一异常处理代码示例
2018/01/18 Python
详解tensorflow载入数据的三种方式
2018/04/24 Python
python+pyqt5实现KFC点餐收银系统
2019/01/24 Python
pandas 空数据处理方法详解
2019/11/02 Python
Win系统PyQt5安装和使用教程
2019/12/25 Python
Python通过Pillow实现图片对比
2020/04/29 Python
在keras中model.fit_generator()和model.fit()的区别说明
2020/06/17 Python
windows系统Tensorflow2.x简单安装记录(图文)
2021/01/18 Python
Origins悦木之源英国官网:雅诗兰黛集团高端植物护肤品牌
2017/11/06 全球购物
彪马加拿大官网:PUMA加拿大
2018/10/04 全球购物
迪奥美国官网:Dior美国
2019/12/07 全球购物
戴森香港官方网站:Dyson香港
2021/02/11 全球购物
法律顾问服务方案
2014/05/15 职场文书
计划生育标语
2014/06/23 职场文书
企业领导对照检查材料
2014/08/20 职场文书
关于感谢信的范文
2015/01/23 职场文书
幼儿园五一劳动节活动总结
2015/02/09 职场文书
导游词之湖北武当山
2019/09/23 职场文书
Pygame Event事件模块的详细示例
2021/11/17 Python
Redis 限流器
2022/05/15 Redis