服务器推技术

最近在做前端页面渲染的时候,有的组件需要跟随数据的变化而实时的变化,例如:一个线上报名系统,总人数有一定限制,所以要实时的展现已经报名的人数,应该怎么实现呢?最基本解决思路如下:

技术方案:

1.Ajax轮询

2.Ajax长轮询

3.WebSocket

Ajax轮询

实现简单,利用XHR,通过setInterval定时向后端发送请求,优点是,实现非常简单;缺点是会造成数据在一小段时间内不同步和大量无效的请求,增加后端处理压力.
setInterval(function() {
    $.ajax({
        url: 'https://www.baidu.com/',
        success: function() {
            //success code
        }
    });
}, 3000);

Ajax长轮询 (Comet)

在Ajax轮询的基础上做的一点改进,在后端数据没有更新的时候不再返回空响应,而且后端一直保存连接,直到后端有数据变化,则相应请求并且关闭连接,前端收到数据,马上再次向后端发起请求,并处理刚刚收到的数据.

function async() {
    $.ajax({
        url: 'http://api.3g.qq.com',
        success: function() {
            async();
            //success code
        }
    });
}

通常的做法是,在服务器的程序中加入一个死循环,在循环中监测数据的变动。当发现新数据时,立即将其输出给浏览器并断开连接,浏览器在收到数据后,再次发起请求以进入下一个周期。

基于 Iframe 及 htmlfile 的流(streaming)方式

iframe 是很早就存在的一种 HTML 标记, 通过在 HTML 页面里嵌入一个隐蔵帧,然后将这个隐蔵帧的 SRC 属性设为对一个长连接的请求,服务器端就能源源不断地往客户端输入数据。

图1基于流方式的服务器推模型https://www.ibm.com/developerworks/cn/web/wa-lo-comet/fig003.jpg

上节提到的 AJAX 方案是在 JavaScript 里处理 XMLHttpRequest 从服务器取回的数据,然后 Javascript 可以很方便的去控制 HTML 页面的显示。同样的思路用在 iframe 方案的客户端,iframe 服务器端并不返回直接显示在页面的数据,而是返回对客户端 Javascript 函数的调用,如“<script type=”text/javascript”>js_func(“data from server ”)</script>”。服务器端将返回的数据作为客户端 JavaScript 函数的参数传递;客户端浏览器的 Javascript 引擎在收到服务器返回的 JavaScript 调用时就会去执行代码。

从上图可以看到,每次数据传送不会关闭连接,连接只会在通信出现错误时,或是连接重建时关闭(一些防火墙常被设置为丢弃过长的连接, 服务器端可以设置一个超时时间, 超时后通知客户端重新建立连接,并关闭原来的连接)。

使用 iframe 请求一个长连接有一个很明显的不足之处:IE、Morzilla Firefox 下端的进度栏都会显示加载没有完成,而且 IE 上方的图标会不停的转动,表示加载正在进行。Google 的天才们使用一个称为“htmlfile”的 ActiveX 解决了在 IE 中的加载显示问题,并将这种方法用到了 gmail+gtalk 产品中。Alex Russell 在 “What else is burried down in the depth’s of Google’s amazing JavaScript?”文章中介绍了这种方法。Zeitoun 网站提供的 comet-iframe.tar.gz,封装了一个基于 iframe 和 htmlfile 的 JavaScript comet 对象,支持 IE、Mozilla Firefox 浏览器,可以作为参考。

WebSocket

WebSocket是html5出来的东西(协议),也就是说http协议没有变化,或者说没关系,但http是不支持久链接的,WebSocket其实是一个新协议,跟http协议基本没有关系,只是为了兼容现有浏览器的握手规范而已。

WebSocket通信协议包含两个部分,一是开放性HTTP握手连接协商连接参数,二是二进制消息分帧机制(接收消息的文本和二进制数据传输)。它是一个独立完善的协议,也可以在浏览器之外实现。

HTTP升级协商

WebSocket协议提供了很多强大的特性:基于消息的通信、自定义的二进制分帧层、子协议协商、可选的协议扩展,等等。即在交换数据之前,客户端必须与服务器协商适当的参数以建立连接。

利用HTTP完成握手有几个好处。首先,让WebSockets与现有HTTP基础设施兼容:WebSocket服务器可以运行在80和443 端口上,这通常是对客户端唯一开放的端口。其次,让我们可以重用并扩展HTTP的Upgrade流,为其添加自定义的WebSocket首部,以完成协商。

请求头信息

Connection:Upgrade Sec-WebSocket-Key:eDCPPyPQZq7PiwRcx8SPog== Sec-WebSocket-Version:13 Upgrade:websocket 响应头信息

HTTP/1.1 101 Switching Protocols Connection:Upgrade Sec-WebSocket-Accept:/ZVAP3n6XuqDUoDp416PYUO+ZJc= Upgrade:websocket 最后,前述握手完成后,如果握手成功,该连接就可以用作双向通信信道交换WebSocket消息。到此,客户端与服务器之间不会再发生HTTP通信,一切由WebSocket 协议接管。

具体使用方法,本文采用node.js中的Socket.IO来进行说明:

1.服务端创建socket.io的实例

复制代码 var app = require(‘express’)(); var http = require(‘http’).Server(app); //创建实例 var io = require(‘socket.io’)(http);

app.get(‘/’, function(req, res){ res.sendfile(‘index.html’); });

//监听前端连接 io.on(‘connection’, function(socket){ console.log(‘a user connected’); });

http.listen(3000, function(){ console.log(‘listening on *:3000’); }); 复制代码 前端创建websocket连接:

2.数据传输

前端向后端发送数据:

socket.emit(‘chat message’, $(‘#m’).val()); 后端接收数据:

io.on(‘connection’, function(socket){ socket.on(‘chat message’, function(msg){ console.log(‘message: ‘ + msg); }); }); ———————————————————————————————————————————-

后端向前端发送数据:

io.on(‘connection’, function(socket){ socket.on(‘chat message’, function(msg){ io.emit(‘chat message’, msg); }); }); 前端接收数据:

socket.on(‘chat message’, function(msg){ console.log(msg); });

原文参考:https://www.ibm.com/developerworks/cn/web/wa-lo-comet/

Table of Contents