服务器 Push 技术表示服务器可以主动推送消息给客户端浏览器。
实现方式
- Comet
- Ajax 轮询
- iframe / htmlfile
- script tag (不中断的连续请求)
- Flash 通讯
- WebSocket
Comet 本意是彗星,彗星尾巴痕迹很像长连接工作方式,所以 Comet 指代长连接。 在 Ajax 轮询的分类上,存在一些分歧,这里我把轮询认为一种 Comet 方式。
ps:「长连接」在一些场景下,是另外一种意义「HTTP: Keep alive」。不在本文讨论的范围中。
参考链接:
浏览器支持情况
WebSocket 属于 HTML5 规范,需要「先进」浏览器支持, Flash 通讯需要浏览器安装 Flash 插件,其他方式都可以适应常见浏览器。
参考连接:
- HTTP持久链接
- [Comet (programming)](http://en.wikipedia.org/wiki/Comet_(programming))
- 一个误解: 单个服务器程序可承受最大连接数「理论」上是「65535」
- How to implement COMET with PHP
各大网站连接情况
可以通过 url 请求来揣测一些东西,比如说,它们没有用 WebSocket, 否则 FireBug 是无法监测的,WebSocket 可以双向通讯。
新浪微博
未读信息链接: http://rm.api.weibo.com/remind/unread_count.json?target=api&_pid=10001&count=2&source=3818214747&callback=STK_133834300664875
未读信息大约每20秒触发一次,像是 Ajax 轮询。
IM 长连接: http://4.46.web1.im.weibo.com/im?jsonp=parent.org.cometd.script._callback5&message=%5B%7B%22channel%22%3A%22%2Fmeta%2Fconnect%22%2C%22connectionType%22%3A%22callback-polling%22%2C%22id%22%3A6%2C%22clientId%22%3A%22b02qp9qw9cgiuxxyn3%22%7D%5D&1338343019008
可以看出新浪在使用 JSONP 跨域做 IM 长连接,FireBug 中也始终有链接请求, 看上去像 Script Tag 请求方式。
知乎
请求链接: http://comet.zhihu.com/update?loc=http%3A%2F%2Fwww.zhihu.com%2F&channel=13781e6817833300f0a70f19&callback=zhp13781e6a6f22349b9865b47c8
依然能在 FireBug 中看到请求地址,说明客户端请求数据还是走 HTTP 方式, 并且会出现 update 动作链接一直出于请求状态,猜测知乎仍然使用 Script Tag 请求。
框架支持
orbited2
http://labs.gameclosure.com/orbited2/
- 跨浏览器
- 容易集成:IRC / XMPP / ActiveMQ / RabbitMQ
- Python
StreamHub
- 免费版仅支持 10 个在线
- 支持 Java / .net / iPhone
socket.io
- NodeJS
- 推送方式:
- WebSocket
- Adobe® Flash® Socket
- AJAX long polling
- AJAX multipart streaming
- Forever Iframe
- JSONP Polling
- 支持浏览器:
- Internet Explorer 5.5+
- Safari 3+
- Google Chrome 4+
- Firefox 3+
- Opera 10.61+
- iPhone Safari
- iPad Safari
- Android WebKit
- WebOs WebKit
sockjs-client
https://github.com/sockjs/sockjs-client
- 支持 Node.js / Erlang / Lua / Python-Tornado
- 跨浏览器
实战 Socket.io
考虑到上述候选框架的使用场景,这里选择 Socket.IO 作为 Comet 框架。
尴尬的 Pylons
Pylons 和 Comet 配合有问题,问题处在标准 WSGI 是非异步的。 (看邮件列表里面,似乎新的标准准备支持)。
- http://stackoverflow.com/a/3090118
- http://mail.python.org/pipermail/web-sig/2008-July/003545.html
- Spawning
这样的话,我就直接选择使用 Node.JS 做 Comet 服务器,Nginx 负责转发。
简单Demo
node.js 代码
```js app.js / global __dirname, console /
var app = require('http').createServer(handler), io = require('socket.io').listen(app), fs = require('fs');
app.listen(8080);
function handler(req, res) { fs.readFile(__dirname + '/index.html', function (err, data) { if (err) { res.writeHead(500); return res.end('Error loading index.html'); }
res.writeHead(200);
res.end(data);
});
}
io.sockets.on('connection', function (socket) { 'use strict'; socket.emit('news', {hello: 'world, for everyone!'}); socket.on('my other event', function (data) { console.log(data); }); socket.on('private message', function (from, msg) { console.log('I received a private message by ', from, ' saying ', msg); }); });
页面代码
``` html index.html
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<title>Socket.io Demo</title>
<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io.connect('/');
socket.on('news', function (data) {
console.log(data);
socket.emit('my other event', { my: 'data' });
});
</script>
</head>
<body>
</body>
</html>
连接成功之后,在浏览器控制台里面,可以使用 socket.emit('my other event', {biu: 'biu'});
向服务器发送消息。
服务器也可以通过 socket.emit()
来向客户端推送消息。
私有信息发送,使用 socket.emit('private message', 'James', {some: 'message'});
。
跨平台
实测看来,在 IE8 下面, Socket.io 会降级使用 htmlfile
来实现 Comet。
而 Firefox 中会有 websocket / htmlfile / xhr-polling / jsonp-polling
依次备选, 首选 websocket。
安全性
问题:提交数据的身份认证过程,以前在后台由 Web 框架自动完成,而现在流程是 Socket.IO -> RabbitMQ -> Web App,身份验证的复杂度增加了。
思路:Socket.IO 使用 Nginx 代理转发,从而保留同一域名下面的 cookie 信息, 这样能够提交到 Socket.IO 服务器,每次 RabbitMQ Message 都记录 cookie 信息, 后台从 RabbitMQ 读取信息时候,再进行认证。
实际操作:由于 Comet 中的数据流仅负责推送,客户端继续使用原始 POST 方式发送数据到服务器,所以暂时不会产生身份认证问题。
Node AMPQ 驱动
Socket.IO 提供了一个通用的 Comet 解决方案,下面就需要点润滑剂,将整个数据流跑通。 消息队列 RabbitMQ 正好适合用来做这个。
Rabbit 官网提到了一个套件 rabbit.js 。 遗憾的是这个库是混合了 RabbitMQ 和 Node.JS,提供了一个封装好的 Node.JS 库, 而我想要的仅仅是一个 AMPQ 协议驱动。node-amqp 则是我们需要的驱动。
Demo
服务器接收者脚本:
``` js app-amqp.js / global __dirname, console /
var conn = require('amqp').createConnection({ url: 'amqp://localhost'});
console.log('socket works'); conn.on('ready', function() { console.log('conn ready'); conn.queue('socket.io', {passive: true}, function(queue){ queue.subscribe(function (json, headers, deliveryInfo) { console.log('#json:') view(json); console.log('#headers:') view(headers); console.log('#deliveryInfo:') view(deliveryInfo); }); }); });
conn.on('error', function() { console.error('error'); });
function view(obj) { for (var i in obj) { if(obj.hasOwnProperty(i)) { console.log(i + ': ' + obj[i]); } } }
用 Python 写的发送者脚本:
``` python producter.py
# coding=utf-8
#! /usr/bin/env python2
import pika
import json
import logging
import time
logger = logging.getLogger()
def main():
conn = pika.BlockingConnection(
pika.ConnectionParameters('localhost'))
chan = conn.channel()
chan.queue_declare(queue='socket.io')
count = 10
while (count > 0):
message = {'no': count, 'some': 'Message', u'比如': u'中文信息'}
publish_text(chan, 'socket.io', u'text %d' %count)
publish_json(chan, 'socket.io', message)
logger.info('add one message to RabbitMQ')
#time.sleep(5) # sleep 5 sec
count -= 1
def publish_text(channel, queue, message):
channel.basic_publish(exchange='',
routing_key=queue,
body=json.dumps(message),
properties=pika.BasicProperties(
content_type='text/plain',
content_encoding='utf-8',
delivery_mode=1)
)
def publish_json(channel, queue, message):
channel.basic_publish(exchange='',
routing_key=queue,
body=json.dumps(message),
properties=pika.BasicProperties(
content_type='application/json',
content_encoding='utf-8',
delivery_mode=1)
)
if __name__ == '__main__':
main()
使用 node ./app-amqp.js
运行 Node.JS 服务器,然后运行 producter.py
产生 RabbitMQ Message,我使用的数据格式是序列化的 JSON 字串, 还有 JSON, Thrift, Protocol Buffers, MessagePack
这些格式可供选择。运行结果如下:
#json:
data: "text 1"
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-3: ordinal not icontentType: text/plain
#headers:
#deliveryInfo:
contentType: text/plain
contentEncoding: utf-8
deliveryMode: 1
queue: socket.io
deliveryTag: 19
redelivered: false
exchange:
routingKey: socket.io
consumerTag: node-amqp-10880-0.06487216474488378
#json:
比如: 中文信息
some: Message
no: 1
#headers:
#deliveryInfo:
contentType: application/json
contentEncoding: utf-8
deliveryMode: 1
queue: socket.io
deliveryTag: 20
redelivered: false
exchange:
routingKey: socket.io
consumerTag: node-amqp-10880-0.06487216474488378
里面有两个 Message,发送数据格式为 text/plain
和 application/json
。
参考链接:
Socket.IO + RabbitMQ
最后提供 Socket.IO + RabbitMQ 的完整 Demo,客户端会实时接受到来自消息发送者的消息。
``` js app-amqp.socket.js / global __dirname, console /
var app = require('http').createServer(handler), io = require('socket.io').listen(app), fs = require('fs');
app.listen(8080);
function handler(req, res) { fs.readFile(__dirname + '/index.html', function (err, data) { if (err) { res.writeHead(500); return res.end('Error loading index.html'); }
res.writeHead(200);
res.end(data);
});
}
io.sockets.on('connection', function (socket) { console.log('io ready');
var conn = require('amqp').createConnection({ url: 'amqp://localhost'});
conn.on('ready', function () {
console.log('conn ready');
conn.queue('socket.io', {passive: true}, function(queue){
queue.subscribe(function (json, headers, deliveryInfo) {
console.log(json);
console.log(deliveryInfo.contentType);
if (deliveryInfo.contentType == 'application/json') {
socket.emit('news', json);
}
if (deliveryInfo.contentType == 'text/plain') {
socket.emit('news', json.data.toString());
}
});
});
});
});
在运行 `producter.py` 后,Python 脚本持续产生 Message 到 RabbitMQ,
`app-amqp-socket.js` 订阅读取 Message 并推送到浏览器端。
浏览器可以在 Console 里面看到日志:
``` text
Object { 比如="中文信息", some="Message", no=1}
至此,我们可以完成 WebApp -> RabbitMQ -> Socket.IO -> Browser 的实时推送。
相关推荐
【标题】:“Web聊天服务器Push技术使用Servlet实现” 在Web开发中,实时通信是一个重要的需求,比如在线聊天、股票更新、新闻推送等场景。传统的HTTP协议是无状态的,基于请求-响应模型,无法实现服务器主动向...
在Web开发中,服务器推送(Server Push)是一种技术,它允许服务器主动将数据发送到客户端,而无需等待客户端的请求。这种机制打破了传统的HTTP协议的请求-响应模型,提高了实时性和交互性。在ASP.NET框架下实现...
PUSH技术可以实现服务器主动向客户机发送相关信息,在信息推送的过程中,服务器是一个具体的PUSH事件的发起者。 在J2ME平台下,PUSH技术可以实现JAVA移动应用程序的自动启动运行。PUSH注册机制是大多A MS应用管理...
Server Push技术主要依赖于HTTP协议的一些扩展功能,特别是MIME类型和响应头的特定配置。 - **MIME类型**:为了支持Server Push,HTTP协议引入了特殊的MIME类型,如`multipart/mixed`或`multipart/x-mixed-replace`...
Server Push技术则是一种使服务器能够主动向客户端发送数据的技术,而不仅仅等待客户端的请求。在Servlet中实现Server Push技术,可以实现如实时消息推送、股票价格更新等功能,其中聊天室是一个典型的例子。 ### ...
服务器推技术是网络应用中的一种重要机制,它与传统的客户端请求、服务器响应的HTTP协议模型不同,服务器推技术允许服务器主动地将数据推送给客户端,而无需客户端发起新的请求。这种技术在实时性要求较高的场景中,...
PUSH技术的核心是服务器主动向客户端推送信息,而不是等待客户端发起请求后再响应。这种模式与传统的HTTP的Pull模式相反,它减少了网络延迟,使得信息能够更快地到达用户终端,特别适用于新闻更新、天气预报、股票...
在手机软件开发中,Pull与Push技术是两个关键的概念,它们主要用于数据同步和更新,确保用户能在正确的时间接收到最新的信息。本文将深入探讨这两种技术的原理、应用场景以及它们之间的区别。 首先,让我们来理解...
服务器推送技术,也称为Server Push,是Web开发中一种创新的通信模式,旨在解决传统HTTP协议下服务器无法主动向客户端发送信息的问题。该技术源于Ajax技术的广泛应用,它改变了Web应用仅能通过用户触发请求获取数据...
WAP Push技术是一种在无线应用协议(WAP)框架下,用于向移动设备发送数据的服务。这个技术允许服务器端将消息、铃声、图片、应用程序等推送到支持WAP的手机上,用户无需主动请求就能接收到信息。在Java环境下实现...
Push 系统总体架构图 Push 系统是一种基于云端服务的消息推送系统...同时,Push 系统还使用了多种技术,包括 Spring 框架、MyBatis 框架、Shiro 框架、长连接技术、JSON 格式传参等,实现了高效、可靠的消息推送服务。
在.NET开发环境中,"Server Push"技术是一种允许服务器主动向客户端发送数据,而不仅仅是响应客户端请求的方法。这种技术常用于实时应用,如聊天系统、股票报价、在线游戏等,能够提高用户体验并降低延迟。以下是对...
Comet 有时也称反向 Ajax 或服务器端推技术(server-side push)。其思想很简单:将数据直接从服务器推到浏览器,而不必等到浏览器请求数据。听起来简单,但是如果熟悉 Web 应用程序,尤其是 HTTP 协议,那么您就会...
【服务器推技术与DWR中的push机制】 在Web开发中,传统的Ajax技术主要是基于“拉”(Pull)模型,即浏览器(Browser)发起请求,服务器(Server)被动响应。然而,这种模型无法满足实时性需求,例如股票行情、即时...
本文将详细解析"考勤+安防push文档+Demo_中控设备接口_服务器_"的相关知识点,主要关注如何直接与硬件设备进行交互,避免通过第三方服务器。 首先,考勤系统和安防系统的数据交换通常涉及实时性和安全性。"考勤PUSH...
在这个场景中,我们将探讨如何使用Java Applet来实现服务器推(Server-Side Push)技术,这是一个使得服务器能够主动向客户端发送数据而非等待客户端请求的机制,特别适用于实时性要求高的应用,如温度监控。...
在DWR 2.x Push技术中,主要涉及以下几个关键组件: 1. **DWR Engine**:这是DWR的核心,负责处理JavaScript与服务器之间的通信。它管理所有远程调用,并处理Push事件。 2. **Push Channel**:在DWR中,Push ...
在Android应用开发中,Push服务是一种常见的实时通信技术,它允许服务器向客户端设备发送消息,无需客户端持续连接或轮询。这里的"安卓push服务器java代码"指的是使用Java语言实现的Android Push服务后端代码。通常...