概述
- comet
- websocket
- sse
1. comet
之前参与项目里,见到过有人用 ajax 长轮询来做实时数据推送。但是这样做有一个弊端就是会建立很多 TCP 连接,这样会给系统带来比较大的 IO 负担。
有没有一种方式,我们只进行一次 TCP 连接,在这一次 TCP 连接中,服务器不断给客户端吐数据。下面我们就来介绍一下 comet 推送方式:
我们用 php 来做服务端
<?php
header("Content-type:appliacation/json;charset=utf-8");
header("Cache-Control:max-age=0"); // 让前端接收没有缓冲,因为我们要实时获取数据
// 我们用一种方式,让它连上之后不释放
$i=0;
while($i<9){
$i++;
$radom = rand(1,999);
sleep(1);
echo $radom;
echo "<br>";
ob_flush(); // 把当前资源释放掉
flush(); // 拿到释放掉的资源,吐浏览器
// 一直让它输出,而且是一个流式输出
}
?>
js 怎样捕捉一直在发送阶段
<script>
var getXmlHttpRequest = function(){
if(window.XMLHttpRequest){
return new XMLHttpRequest();
}
else if(window.ActiveXObject){
return new ActiveXObject("Microsoft.XMLHTTP");
}
}
var xhr = getXmlHttpRequest();
xhr.onreadystatechange = function (){
console.log(xhr.readyState);
// 检测readyState为3的时候,我们就能把responseText输出出来
// 而且每次一都能把上次全部内容都打印出来
if(xhr.readyState ===3 &&xhr.status === 200){
console.log(xhr.responseText);
}
}
xhr.open("get","data.php",true);
xhr.send("");
</script>
js 用普通 ajax 去请求
<script>
function conn(){
$.ajax({
url: 'data.php',
dataType: 'json',
success: function(data){
console.log(data);
conn();
}
})
}
conn();
</script>
服务端我们不去断掉连接
<?php
header("Content-type:appliacation/json;charset=utf-8");
header("Cache-Control:max-age=0");
sleep(1);
$res = array('success'=>'ok','test'=>'我是测试文本');
echo json_encode($res);
?>
或者我们用下面这种方式,用一个 while 循环,我们可以通过前端给来的参数去判断执行过少次。
<?php
header("Content-type:appliacation/json;charset=utf-8");
header("Cache-Control:max-age=0");
while(true){
sleep(1);
$res = array('success'=>'ok','test'=>'我是测试文本');
echo json_encode($res);
exit(); // 切记要退出,不然前端拿不到数据
}
?>
因为只要不断掉,我们再去请求还是连接的上一次请求,只不过这是用前端实现了轮询效果,刚才我们用后端 flush()来实现的这件事
2. websocket
websocket 它是一种长连接,通过 websocket 我们能实现后端向前端推送数据,前端也可以向后端推送数据。这里我们主要讲前端 H5 websocket 怎样和 nodejs 配合。
为什么要用到 socket.io 呢?
因为它承载了 socket 大部分功能,而且相对稳定
服务器端
- 第一步我们建一个 express 项目
$ npm install express --save
$ npm install -g express-generator
express
- 第二步我们要在项目文件里安装 socket.io
$ npm install socket.io --save
- 第三步我们在 node_module 里面找到 socket.io.js 这个文件,这是给前端用的
- 文件在 node_modules/socket.io-client/dist/socket.io.js
接下来我们在 app.js 中引入 socket.io
var express = require('express');
var path = require('path');
var app = express();
var http = require('http');
var server = http.createServer(app);
var io = require('socket.io').listen(server);
app.set('port', 3000);
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
app.use(express.static(path.join(__dirname, 'public')));
io.on('connection', function(socket) {
socket.emit('open'); // 通知客户端已连接
// 对message事件的监听
socket.on('message', function(msg) {
console.log('this is msg:', msg);
socket.emit('test', 'server ready');
});
// 监听退出事件
socket.on('disconnect', function() {});
})
app.get('/', function(req, res) {
res.sendfile('views/index.html');
});
server.listen(app.get('port'), function() {
console.log("socket server listen" + app.get('port'));
});
在 views 文件夹里建 index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
hello
<script src="javascripts/socket.io.js"></script>
<script src="javascripts/main.js"></script>
</body>
</html>
在静态文件 javascripts 文件夹下建 main.js
(function() {
var i = 0;
// 建立websocket连接
var socket = io.connect("http://localhost:3000");
// 收到server的连接确认
socket.on('open', function() {
console.log('已连接');
socket.send("ok");
})
socket.on('test', function(json) {
console.log('test', json);
})
})();
具体步骤:
- 在 app.js 文件中用 socket.io 监听 server 端口
- 在 main.js 文件中建立 websocket 连接
- app.js 会在 connect 事件中监听到连接,并触发 open(自己定义的)事件
- 此时前端监听到 open 事件
- 同时前端也可以用 socket 连接用 send 方法向后端推送消息
- 后端会在 message 事件中监听到前端推送过来的消息
参考资料:socket.io 官网 api
3. sse
SSE 是一种能让浏览器通过 http 连接自动收到服务器端更新的技术,SSE EventSource 接口被 W3C 制定为 HTML5 的一部分。
它是能完成服务器端向客户端单向推送消息,但是 IE 不支持
服务器端
- php 代码
<?php
// 以event-steam的方式输出
header("Content-Type:text/event-stream;charset='utf-8'");
// 指定哪个域来访问
header("Access-Control-Allow-Origin:http://127.0.0.1/");
echo "data:现在北京时间是".date('H:i:s')."\r\n\r\n";
?>
- html 代码
<script>
var source;
function init(argument) {
source = new EventSource('http://localhost/sse/data.php');
// 建立连接
source.onopen = function() {
console.log('连接已建立', this.readyState)
}
// 后端实时推送的数据
source.onmessage = function(event) {
console.log('从服务器实时获取的数据', event.data);
}
// 出错的监听
source.onerror = function() {
}
}
init();
</script>
这里主要是 new 一个 EventSource 实例,然后监听 onopen 事件,这个事件是监听连接已建立,然后通过 onmessage 事件来监听后端推送过来的消息。