从零天梯:web 实时通信与跨文档通信

临近年底越忙碌,前段时间为年会活动开发一个项目,不想到需求越加越多最后写了个webapp,功能类似于微信朋友圈(能发状态、点赞、评论&回复),开发过程中填了不少坑,今天聊一聊消息实时通信那些事,整理了一些学习笔记.

1 什么是实时通信

实时通信: 在网络连接的时候,需要及时的把服务端上的消息回传到消息。比如在线游戏、微博、电子邮件等。

实时通信分类:

  1. Pull技术,轮询(polling)
  2. Push,反向Ajax(Reverse Ajax)或者叫Comet.
    2.1 长轮询(Long Polling)
    XHR Long Polling (ajax方式),
    Jsonp Long Polling (jsonp方式)
    
    2.2 流推送(Comet Streaming)
    Multi-part XMLHttpRequest,
    Hidden IFrame(Forever IFrames)
    
  3. WebSocket

2 实时web的演变

Ajax盛行之前

自动刷新整页使页面呈现最新内容:

  • 设置meta页面自动刷新机制
1
<meta  http-equiv=“refresh”  content=“20;url=xxx” />
  • 定时修改Location.href

页面刷新缺点:

  • 用户体验极差(每隔一段时间就刷新页面)
  • 流量浪费(整个页面刷新)

2.1 轮询(polling)

原理:客户端定时轮询请求(setInterval),服务器端立刻返回.

示例代码:

1
interval = setInterval(polling, 1000);//1000为心跳值,每隔1s请求

  • 优点
    简单易用,容易实现
    短链接(简单的ajax请求,可以使get、post,也可以是jsonp)
    服务器处理方便
    支持跨域(使用jsonp时)
    所有浏览器都支持
    每次请求,立刻返回
  • 缺点
    可能会出现延迟(每隔几秒模拟,并不能保证是实时消息)
    浪费流量(并不知道服务器有木有最新数据,要不断向服务器请求)
    建立大量链接,(用户数越多与服务器连接的请求也越多,服务器并发量大,处理变慢)

微博未读微博数和未读消息(评论,@)就是用polling实现的。
应用场景:对实时性要求不高的应用,如新微博提示,评论提示,回复提示等。

2.2 Comet:基于HTTP长连接的“服务器推”技术

(也有人叫做反向ajax)

原理:客户端发起连接,服务器端会阻塞请求(服务器端hold网络连接)直到有数据传递或超时(心跳时间)才返回.

实现方式主要有2种:长轮询(Long Polling)流推送(Comet Streaming)

两种实现方式:

长轮询(long polling)

1 XHR Long Polling (ajax方式)

  • 原理:客户端发起一个XHR ajax request,服务器端会阻塞请求直到有数据传递或超时才返回
    客户端收到response之后马上再发起一个新的request,周而复始。(当客户端处理接收的数据、重新建立连接时,服务器端可能有新的数据到达;这些信息会被服务器端保存直到客户端重新建立连接,客户端会一次把当前服务器端所有的信息取回。)

2 Jsonp Long Polling (jsonp方式)

  • 原理:跟XHR Long Polling类似,只是结合long polling和jsonp,用来支持跨域(cross-domain)请求。

连接需要后端靠hold住(让进程休眠),对服务器端的架构考验比较大

优点:减少轮询,低延迟, 各大浏览器均支持;
缺点:服务器需要hold住大量connection,ajax方式不支持跨域.

ps:在这种长轮询方式下,客户端是在 XMLHttpRequest 的 readystate 为 4(即数据传输结束)时调用回调函数,进行信息处理。当 readystate 为 4 时,数据传输结束,连接已经关闭。Mozilla Firefox 提供了对 Streaming AJAX 的支持,
即 readystate 为 3 时(数据仍在传输中),客户端可以读取数据,从而无须关闭连接,就能读取处理服务器端返回的信息.IE 在 readystate 为 3 时,不能读取服务器返回的数据,目前 IE 不支持基于 Streaming AJAX。

微博私信提示就是用Jsonp Long Polling实现。
Long Polling的应用场景:对实时性要求较高和浏览器覆盖面广的应用,如私信等一些简单即时聊天应用。

流推送(comet streaming )

  • Multi-part XMLHttpRequest
  • Hidden IFrame(Forever IFrames)

1 Multi-part XMLHttpRequest

优点:客户端一次连接,服务器数据可多次推送。
缺点:并非所有的浏览器都支持 multi-part 标志。

-----对他不是很了解,欢迎在大家在评论区补充------

2 Hidden IFrame(Forever IFrames)
建立一个隐藏的iframe,利用iframe建立长连接。
(iframe src 属性设为对一个长连接的请求,服务器端就能源源不断地往客户端输入数据。)

优点:客户端一次连接,服务器数据可多次推送。几乎所有支持iframe 的浏览器上都可用。
缺点:不可跨域,错误处理可控性不强

流推送的应用场景:实时监控系统,即时聊天


http实时通信缺陷

http通信特点:

  • 无状态连接(需要对每个客户端的连接维持)
  • 单向通信
  • 每个请求都包括header信息(cookie, Cache-Control,content-type, Expires)
  • 服务器被迫为每个客户端使用一些不同的底层TCP连接:
  • 一个用于发送信息到客户端和一个新的用于每个传入消息。
  • 线路层协议有较高的开销,因为每个客户端-服务器消息都有一个HTTP头信息。
  • 客户端脚本被迫维护一个传出的连接到传入的连接的映射来跟踪回复。

3 websocket介绍

3.1 websocket的诞生

定义了一个浏览器和服务器之间的全双工的单一的socket连接取代现有的HTTP作为传输层的双向通信技术W3C将其纳入规范,写入HTML5版本

3.2 websocket接口

onopen 在WS客户端和WS服务器建立连接成功后调用
onmessage 在WS服务器给WS客户端发送数据时调用
onerror 如果连接失败,发送、接收数据失败或者处理数据出现错误,则会被调用
onclose 在WS客户端接收到WS服务器关闭时进行调用

3.3 websocket 实例

浏览器端实现

  1. Websocket 实例化
  2. 定义open\message\cloes\error事件

服务端(nodejs)实现

  1. 构建websocket服务器
  2. 处理connection事件,实现与客户端通信

思考:如何做技术选型?


4 跨文档通信实现方式

4.1 Window命名空间进行传递:

window.name = “hello”;

优点:简单
缺点:

  • 无法跨域
  • 无法实现不同窗体之间的通信

示例代码window.name实现的跨域数据传输

4.2 postmessage

信差大使——postmessage: 监听message事件,通过event对象获取消息

data 包含任意字符串数据,由原始脚本发送
origin 一个字符串,包含原始文档的方案、域名以及端口(如:http://domain.example:80)
lastEventId 一个字符串,包含了当前的消息事件的唯一标识符。
source 原始文件的窗口的引用。更确切地说,它是一个WindowProxy对象。
ports 一个数组,包含任何MessagePort对象发送消息。

1
2
3
4
5
6
7
8
9
var messageHandle = function() {
alert(1);
}

if(window.addEventListener) {
window.addEventListener('message', messageHandle, false);
}else if (window.attachEvent) {//IE的写法
window.attachEvent('onmessage',messageHandle);
}
1
window.parent.frames[1].postMessage(message,'*');

示例代码请postMessage跨文档实现方案


5 参考文档

HTML5即时通讯
JavaScript跨域总结与解决办法
JSONP原理优缺点
基于WEB的实时通信方案
window.name实现的跨域数据传输
html5 postMessage解决跨域、跨窗口消息传递