摘要

在各种BS架构的应用程序中,往往都希望服务端能够主动地向客户端推送各种消息,以达到类似于邮件、消息、待办事项等通知。

往BS架构本身存在的问题就是,服务器一直采用的是一问一答的机制。这就意味着如果客户端不主动地向服务器发送消息,服务器就无法得知如何给客户端推送消息。

随着HTML、浏览器等各项技术、标准的发展,依次生成了不同的手段与方法能够实现服务端主动推送消息,它们分别是:AJAX,Comet,ServerSent以及WebSocket。

本篇文章将对上述提及到的各种技术手段进行直白化的解释。


AJAX

正常的一个页面在浏览器中是这样工作的:

  1. 用户向给予浏览器一个需要访问的地址
  2. 浏览器根据这个地址访问服务器,并与服务器之间创建一个TCP连接(HTTP请求)
  3. 服务器根据这个地址和一些其它数据,组建一段HTML文本,将写入TCP连接,然后关闭连接
  4. 浏览器得到了来自服务器的HTML文本,解析并呈现了浏览器上给用户浏览

此时,用户点击了网站上任何一个<a>或触发任何一个<form>提交时:

  1. 浏览器根据form的参数或者a的参数,作为访问的地址
  2. 与服务器创建TCP连接
  3. 服务器组建HTML文本,然后关闭连接
  4. 浏览器将当前显示的页面摧毁,并按照新的HTML文本呈现一个新的页面给用户

我们不难发现的是整个过程中间,一旦建立了一个连接,页面就无法再维护住了。整个过程看上去有点强买强卖,也许我只要一杯新的可乐,但你非要给我一整个套餐组合。

此时我们可以了解一下XmlHttpRequest组件,这个组件提供我们手动创建一个HTTP请求,发送我们想要的数据,服务器也可以只返回我们想要的结果,最大的好处是,当我们收到服务器的响应时,原来的页面没有被摧毁。这就好比,我喊一句"我的咖啡喝完了,我要续杯",然后服务员就拿了一杯咖啡过来,而不是会把我没吃完的套餐全部倒掉。

当我们利用AJAX实现服务器推送时,其实质是客户端不停地向服务器询问"有没有给我的消息呀?",然后服务器回答"有"或"没有"来达到的实现效果。它的实现方法也很简单,利用jQuery框架封装好的AJAX调用也很方便:

function getMessage(fn) {
$.ajax({
url: "Handler.ashx", //一个能够提供消息的页面
dataType: "text", //响应类型,可以是JSON,XML等其它类型
type: "get", //HTTP请求类型,还可以是post
success: function (d, s) {
fn(d); //得到了正常的响应时,利用回调函数通知外部
},
complete: function (x, s) {
setTimeout(function () {
getMessage(fn);
}, 5000); //无论响应成功或失败,在若干秒后再询问一次服务器
}
});
}

通过上面的代码,可以每隔5秒询问一次服务器是否有需要处理的消息,通过这种方式可以达到推送的效果,但是会存在一个问题:

  1. 间隔时间越快,推送的及时性越好,服务器的消费越大;
  2. 间隔时间越慢,推送的及时性越低,服务器的消费越小。

而且严格地来说,这种实际方式,并不是真正意义上的服务器主动推送消息,但由于早期技术手段缺乏,所以AJAX轮循成为了一种很普遍的手段。


Comet

我们知道HTTP请求其实是基于TCP连接实现的,再看看之前说的HTTP请求处理过程:

  1. 客户端与服务器建立TCP连接
  2. 服务器根据客户端提交的报文处理并生成HTML文本
  3. 将HTML封闭成为HTTP协议报文并返回给客户端
  4. 关闭链接。

看到这个处理过程,我们不难联想到,如果把第4步——关闭连接给省掉,那不就相当于有一个长连接一直被维持住了么。通过对服务端的一些操作,我们可以直接将数据从这个TCP连接发送客户端了。

通过这种技术,我们可以大大提高服务器推送的实时性,还可以减去服务端不停地建立、施放连接所形成的开销。

目前市面上有不少基于AJAX实现的Comet机制,但主要有两种方式:

  1. 建立连接后依然使用"询问"+"应答"的模式。虽然工作方式没变,但是因为减去了每次建立与施放连接的工作,所以性能上提升了很多。而且服务器对TCP连接可以有上下文的定义,而不像以前的AJAX完全是无状态的。
  2. 通过对Stream的写入实现服务器将数据主动发送到客户端。因为是TCP连接,所以通过对服务器的编程,我们可以主动的把数据从服务端发送给客户端,从模式上真正建立起了推送的概念。

Server-Sent

Server-Sent是HTML5提出一个标准,它延用了Comet的思路,并对其进行了一些规范。使得Comet这项技术由原来的分支衍生技术转成了正统的官方标准。

它的原理与Comet相同,由客户端发起与服务器之间创建TCP连接,然后并维持这个连接,至到客户端或服务器中的做任何一放断开,ServerSent使用的是"问"+"答"的机制,连接创建后浏览器会周期性地发送消息至服务器询问,是否有自己的消息。

这项标准不仅要求了支持的浏览器能够原生态的创建与服务器的长连接,更要求了对JavaScript脚本的统一性,使得兼程该功能的浏览器可以使用同一套代码完成Server-Sent的编码工作。

创建代码非常简单:

//定义一个ServerSent对象
var s = new EventSource("Handler.ashx");
//当收到一个非自定义事件时的回调函数
s.onmessage = function (e) {
alert(e.data);
};
//当收到一个被服务器命名为MyEvent事件消息时的回调函数
s.addEventListener("MyEvent", function (e) {
alert(e.data);
});

而服务器的代码也很简单:

public class Handler : IHttpHandler
{ public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "text/event-stream";
context.Response.Expires = -;
context.Response.Write("event: MyEvent\r\n"); //事件类型,使用\r\n结尾
context.Response.Write("data: HelloWorld!\r\n"); //事件数据,换行时使用\r\n,并在新行再加上data:
context.Response.Write("data: I'm server!\n\n"); //事件数据结束,使用\n\n
context.Response.Flush(); //这里不能用End,否则是关闭连接的
} public bool IsReusable
{
get
{
return true;
}
} }

两小段代码,就已经具备了服务器消息推送了。

总得来说SeverSent就是HTML5规范下的Comet,具有更好的统一性,而且简单好用。


WebSocket

看名字就知道了,这是一个可以用在浏览器上的Socket连接。

这也是一个HTML5标准中的一项内容,他要求浏览器可以通过JavaScript脚本手动创建一个TCP连接与服务端进行通讯。

WebSocket不包含太多的额外功能,仅仅就是TCP连接的几项基本功能:建立,临时以及发送。

另外WebSocket使用了ws和wss协议,需要服务器有与之握手的算法才能将连接打开。

所以WebSocket相对于之前几种手段来说,其编码量是最大的,但由于没有其它的约束,因此它也可以自由地实现所有可能的功能。

即可以满足"问"+"答"的响应机制,也可以实现主动推送的功能。

与ServerSent相同,HTML5也对WebSocket调用的JavaScript进行规范,我们可以弄过很简单的一代码构建一个WebSocket连接

var ws = new WebSocket("ws://192.168.0.105:10080"); //连接服务器        

ws.onopen = function (event) { alert("已经与服务器建立了连接\r\n当前连接状态:" + this.readyState); };
ws.onmessage = function (event) { alert("接收到服务器发送的数据:\r\n" + event.data); };
ws.onclose = function (event) { alert("已经与服务器断开连接\r\n当前连接状态:" + this.readyState); };
ws.onerror = function (event) { alert("WebSocket异常!"); };

还可以通过send的方式发送消息

ws.send("Hello World");

WebSocket具有较为复杂的协议,需要在服务端做额外编程才能进行数据通讯。有关协议的详细内容,我会在以后的文章中进行解释。

WebSocket + MessageQueue

MessageQueue,简称MQ,也就是消息列队。是一种常常用于Tcp服务端的技术。通过生产和访问各种消息类型,MQ服务器会将生产者所生成的消息发给感兴趣的客户端。市面上有很多的MQ框架,比如:ActiveMQ。

ActiveMQ已经支持了WebSocket协议,也就意味着,WebSocket已经可以作为一个生产者或一个消费者,与MQ服务器连接。

开发者可以通过MQTT的JS脚本,连接上MQ服务器,同时将Web服务器也连上MQ服务器,从此可以告别了Http通讯协议,完完全全使用Socket通讯来完成数据的交换。


总结:

总得来说,在HTML5规范下,最推荐使用ServerSent和WebSocket的方式进行服务器消息的推送。

对比这两种方式。

ServerSent的方式,可以使服务端的开发依然依用以前的方式,但是其工作方式与Comet类似。

而WebSocket的方式,则对服务端的开发有着较高的要求,但其工作方式是完全的推送。

我本人其实挺偏向WebSocket + MQ的工作方式,但是对于老项目的翻新,还是用SeverSent比较好


结尾

本文为作者原创,转载请注明出处:http://www.zizhusoft.com/note/show.aspx?id=db723a7c-17ce-4c46-9935-aef07f4f371e

文章中的相关代码可以在 http://j.zizhusoft.com/Develop/Explorer.aspx 中的ServerSent目录中查看

HTML5服务器推送消息的各种解决办法的更多相关文章

  1. HTML5服务器推送消息的各种解决办法,html5服务器

    HTML5服务器推送消息的各种解决办法,html5服务器 摘要 在各种BS架构的应用程序中,往往都希望服务端能够主动地向客户端推送各种消息,以达到类似于邮件.消息.待办事项等通知. 往BS架构本身存在 ...

  2. SSE技术详解:一种全新的HTML5服务器推送事件技术

    前言 一般来说,Web端即时通讯技术因受限于浏览器的设计限制,一直以来实现起来并不容易,主流的Web端即时通讯方案大致有4种:传统Ajax短轮询.Comet技术.WebSocket技术.SSE(Ser ...

  3. C# 实现HTML5服务器推送事件

    为什么需要服务器推送事件: 因为如果需要保持前台数据的实时更新例如,IM聊天,股票信息, 1.可以在客户端不断地调用服务端的方法来获得新数据,但是这样会很消耗服务器资源,导致系统变慢! 2 html5 ...

  4. HTML5 服务器推送事件(Server-sent Events)实战开发

    转自:http://www.ibm.com/developerworks/cn/web/1307_chengfu_serversentevent/ http://www.ibm.com/develop ...

  5. HTML5 服务器推送事件(Server-sent Events)

    服务器推送事件(Server-sent Events)WebSocket 协议的一种服务器向客户端发送事件&数据的单向通讯.目前所有主流浏览器均支持服务器发送事件,当然除了 Internet ...

  6. [wxpusher]分享一个服务器推送消息到微信上的小工具,可以用于微信推送提醒和告警。

    背景 作为一个程序员,业余搞点自己的东西很正常,一般程序员都会有一两台自己的服务器,谁叫今天xx云搞活动,明天yy云搞活动呢. 自家的服务器用来跑爬虫,跑博客,或者跑一些个人业务,但当服务有新状态,抢 ...

  7. HTML5服务器推送事件

    目前客户端(浏览器)和服务端交互大致有以下几种方式: 1)form表单提交方式,适合访问量不大,对用户体验要求不高的web系统开发,或者页面整体刷新无伤大雅的场合,通信方向是客户端提交给服务端,是客户 ...

  8. C# .NET 微信开发-------当微信服务器推送消息时如何接收处理

    最近一直在看微信,整整一个月了,看到现在说实话还有很多没看的,从前两周一点看不懂到现在单个功能的一步步实现,不知道这样的速度是否太慢了. 不过现在往下看还是有思路了,目前整个文档完成学习只有1/3左右 ...

  9. HTML5服务器端推送事件 解决PHP微信墙推送问题

    问题描述 以前的文章中<PHP微信墙制作,开源>已经用PHP搭建了一个微信墙获取信息的服务器,然后我就在想推送技术应该怎么解决,上一篇已经用了.NET 的signalr做了一个微信墙,PH ...

随机推荐

  1. web前后端框架整理

    nodejs <——> Express <———> bootstrap <———> angularJS <————> jquery <—————& ...

  2. C#中的Excel操作【1】——设置Excel单元格的内容,打开Excel文件的一种方式

    前言 作为项目管理大队中的一员,在公司里面接触最多的就是Excel文件了,所以一开始就想从Excel入手,学习简单的二次开发,开始自己的编程之路! 程序界面 功能说明 打开文件按钮,可以由使用者指定要 ...

  3. mysql 数据库迁移

    公司的视频转码服务 使用mysql作为 任务队列, 其中mysql 是我们自己维护的 单例. 后来应业务部门建议,我们计划将现有的mysql 向dba进行迁移,以下记录一下 数据库迁移过程中的东西. ...

  4. safari 日期对象新建new Date( timeStr ) 参数TimeStr格式

    这是一个浏览器兼容的问题,在此总结一下,别老在这掉坑. 先坐下测试 var timeStrArray = [ '2016-10-04', '2016.10.04', '2016/10/04', '10 ...

  5. JavaScript模块化编程

    为什么模块化很重要?因为有了模块,我们就可以更方便的使用别人的代码,想要什么功能,就加载什么模块. 万事都有规矩,就是规范化! 目前,通行的Javascript模块规范有两种: CommonJS和AM ...

  6. WebView 载入本地的html

    1.可以是用loadData,这种方法需要先将html文件读取出来,以字符串传入loadData,可以展示页面,但是不会引用css.js等文件. 2.使用loadUrl,不过需要注意,这里因为是使用本 ...

  7. 初学JAVA的 感想 尹鑫磊

    开始学习任何一门课(包括java),兴趣最重要.一直觉得自己在学计算机编程语言,学习了很多,但每门语言都停留在知识边缘地带,都没深入到它们的精华部分,对它们的精华部分知之甚少,于是趁学校开设这门课,并 ...

  8. cache manifest 格式

    下面说明书写 cache manifest 文件需要遵循的格式.1 首行必须是 CACHE MANIFEST.2 其后,每一行列出一个需要缓存的资源文件名.3 可根据需要列出在线访问的白名单.白名单中 ...

  9. git初学习体会

    github:项目版本控制器 git和传统的版本控制器相比,最大的一点是,界面简单,给与非线性开发模式的强有力的支持,完全分布式等. 对于完全分布式的实现,我的理解是这个样子的.这多少要涉及到一点它的 ...

  10. ftplib模块

    Python中的ftplib模块 Python中默认安装的ftplib模块定义了FTP类,其中函数有限,可用来实现简单的ftp客户端,用于上传或下载文件 FTP的工作流程及基本操作可参考协议RFC95 ...