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 相关文章推荐
让iframe框架网页在任何浏览器下自动伸缩
Aug 18 Javascript
Highslide.js是一款基于js实现的网页中图片展示插件
Mar 30 Javascript
纯JavaScript实现HTML5 Canvas六种特效滤镜示例
Jun 28 Javascript
分享20个提升网站界面体验的jQuery插件
Dec 15 Javascript
JavaScript实现常用二级省市级联下拉列表的方法
Mar 25 Javascript
举例讲解AngularJS中的模块
Jun 17 Javascript
12306 刷票脚本及稳固刷票脚本(防挂)
Jan 04 Javascript
Vue网页html转换PDF(最低兼容ie10)的思路详解
Aug 24 Javascript
JS删除数组里的某个元素方法
Feb 03 Javascript
ExtJs使用自定义插件动态保存表头配置(隐藏或显示)
Sep 25 Javascript
jQuery时间戳和日期相互转换操作示例
Dec 07 jQuery
JS实现网站楼层导航效果代码实例
Jun 16 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对象转换为数组函数(递归方法)
2012/02/04 PHP
php猜单词游戏
2015/09/29 PHP
laravel实现图片上传预览,及编辑时可更换图片,并实时变化的例子
2019/11/14 PHP
Aster vs Newbee BO3 第二场2.18
2021/03/10 DOTA
菜单效果
2006/10/14 Javascript
js控制input输入字符解析
2013/12/27 Javascript
javascript 实现子父窗体互相传值的简单实例
2014/02/17 Javascript
JS判断变量是否为空判断是否null
2014/07/25 Javascript
node.js中的fs.read方法使用说明
2014/12/17 Javascript
node.js回调函数之阻塞调用与非阻塞调用
2015/11/13 Javascript
基于javascript实现仿百度输入框自动匹配功能
2016/01/03 Javascript
js获取隐藏元素宽高的实现方法
2016/05/19 Javascript
利用jQuery来动态为属性添加或者删除属性的简单方法
2016/12/02 Javascript
Bootstrap进度条实现代码解析
2017/03/07 Javascript
利用js定义一个导航条菜单
2017/03/14 Javascript
vue Element-ui input 远程搜索与修改建议显示模版的示例代码
2017/10/19 Javascript
vue 不使用select实现下拉框功能(推荐)
2018/05/17 Javascript
vue 解决循环引用组件报错的问题
2018/09/06 Javascript
[01:54]TI珍贵瞬间系列(三):翻盘
2020/08/28 DOTA
python正则表达式及使用正则表达式的例子
2018/01/22 Python
程序员写Python时的5个坏习惯,你有几条?
2018/11/26 Python
Flask之请求钩子的实现
2018/12/23 Python
python 产生token及token验证的方法
2018/12/26 Python
python将txt文档每行内容循环插入数据库的方法
2018/12/28 Python
pycharm打开命令行或Terminal的方法
2019/01/16 Python
python的turtle库使用详解
2019/05/10 Python
python能做什么 python的含义
2019/10/12 Python
OpenCV模板匹配matchTemplate的实现
2019/10/18 Python
Blue Nile中国官网:全球知名的钻石和珠宝网络零售商
2020/03/22 全球购物
中医专业应届生求职信
2013/11/17 职场文书
承租经营合作者协议书
2014/10/01 职场文书
党的群众路线教育实践活动学习笔记范文
2014/11/06 职场文书
初中毕业生感言
2015/07/31 职场文书
Golang实现AES对称加密的过程详解
2021/05/20 Golang
mysql连接查询中and与where的区别浅析
2021/07/01 MySQL
微信小程序中wxs文件的一些妙用分享
2022/02/18 Javascript