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 相关文章推荐
基于jQuery的固定表格头部的代码(IE6,7,8测试通过)
May 18 Javascript
IE8下String的Trim()方法失效的解决方法
Nov 08 Javascript
动态添加option及createElement使用示例
Jan 26 Javascript
jQuery操作表格(table)的常用方法、技巧汇总
Apr 12 Javascript
jquery 隐藏与显示tr标签示例代码
Jun 06 Javascript
JavaScript的this关键字的理解
Jun 18 Javascript
快速搭建React的环境步骤详解
Nov 06 Javascript
JavaScript重复元素处理方法分析【统计个数、计算、去重复等】
Dec 14 Javascript
使用Angular CLI生成 Angular 5项目教程详解
Mar 18 Javascript
vue计算属性computed、事件、监听器watch的使用讲解
Jan 21 Javascript
JS学习笔记之闭包小案例分析
May 29 Javascript
javascript实现左右缓动动画函数
Nov 25 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
终于听上了直流胆调频
2021/03/02 无线电
ThinkPHP中的三大自动简介
2014/08/22 PHP
重载toString实现JS HashMap分析
2011/03/13 Javascript
在JavaScript并非所有的一切都是对象
2013/04/11 Javascript
js父窗口关闭时子窗口随之关闭完美解决方案
2014/04/29 Javascript
js实现温度计时间样式代码分享
2015/08/21 Javascript
Javascript实现的简单右键菜单类
2015/09/23 Javascript
Node.js实现文件上传
2016/07/05 Javascript
基于jQuery实现淡入淡出效果轮播图
2020/07/31 Javascript
用nodeJS搭建本地文件服务器的几种方法小结
2017/03/16 NodeJs
bootstrap table服务端实现分页效果
2017/08/10 Javascript
Angular4开发解决跨域问题详解
2017/08/28 Javascript
jquery如何实现点击空白处隐藏元素
2017/12/05 jQuery
vue实现验证码按钮倒计时功能
2018/04/10 Javascript
D3.js 实现带伸缩时间轴拓扑图的示例代码
2020/01/20 Javascript
Node.js API详解之 V8模块用法实例分析
2020/06/05 Javascript
原生JS利用transform实现banner的无限滚动示例代码
2020/06/15 Javascript
Vue使用CDN引用项目组件,减少项目体积的步骤
2020/10/30 Javascript
[01:51]开启你的城市传奇 完美世界城市挑战赛开始报名
2018/10/09 DOTA
Python 调用VC++的动态链接库(DLL)
2008/09/06 Python
使用Python构建Hopfield网络的教程
2015/04/14 Python
Python抓取淘宝下拉框关键词的方法
2015/07/08 Python
Python编程实现输入某年某月某日计算出这一天是该年第几天的方法
2017/04/18 Python
Python Numpy计算各类距离的方法
2019/07/05 Python
Python实现RGB与HSI颜色空间的互换方式
2019/11/27 Python
Python for循环与getitem的关系详解
2020/01/02 Python
Python 存取npy格式数据实例
2020/07/01 Python
pycharm-professional-2020.1下载与激活的教程
2020/09/21 Python
浅谈HTML5 Web Worker的使用
2018/01/05 HTML / CSS
美国在线肉类和海鲜配送:Crowd Cow
2020/10/02 全球购物
介绍一下grep命令的使用
2015/06/12 面试题
人力资源管理专业应届生求职信
2014/04/24 职场文书
班训口号大全
2014/06/18 职场文书
青涩记忆观后感
2015/06/18 职场文书
Mysql数据库中datetime、bigint、timestamp来表示时间选择,谁来存储时间效率最高
2021/08/23 MySQL
win10输入法不见了只能打出字母怎么解决?
2022/08/05 数码科技