前台页面使用jquery的jsonp来进行轮询。后端node.js监听的端口是8124
index.html
<!Doctype html>
<html>
<head>
<title>Long Polling in node.js</title>
<meta http-equiv='content-type' content='text/html; charset=utf-8'>
<style type='text/css'>
* {margin:0; padding:0;}
body {background-color:#fff;}
#infoContainer{margin:40px; padding:10px; font-size:14px; border:1px solid #eee;}
</style>
</head>
<body>
<div id='infoContainer'>loading...</div>
<script type='text/javascript' src='js/jquery.js'></script>1:
2:
3: <script type='text/javascript'>4:
5: function callPolling() {6: $.ajax({
7: url: 'http://localhost:8124/?callback=pollingCallback',8: dataType : 'jsonp',9: jsonp : 'kk',10: timeout : 70 * 1000,
11: complete : function() {12: callPolling();
13: }
14: })
15: }
16:
17: function pollingCallback(str) {18: $("#infoContainer").html(str);19: }
20:
21: $(callPolling);
22:
</script>
</body>
</html>
后台用node.js实现的polling.js
var http = require('http'),
fs = require('fs');
http.createServer(function(req, res) {
checkFile(req, res);
}).listen(8124);
var filepath = 'E:/Node_app/file/a.txt';
function checkFile(req, res) {
var reqUrl = req.url;
var callbackFnName = null;
if (/callback=([^&]+)/.test(reqUrl)) {
callbackFnName = RegExp['$1'];
}
var date = new Date();
if (date - req.socket._idleStart.getTime() > 60 * 1000) {
res.writeHead(200, {
'Content-Type' : 'text/plain',
'Access-Control-Allow-Origin' : '*'
});
res.write(callbackFnName + "('ok')", 'utf8');
res.end();
}
fs.stat(filepath, function(err, stats) {
if (err) {
res.writeHead(200, {
'Content-Type' : 'text/plain',
'Access-Control-Allow-Origin' : '*'
});
res.write(callbackFnName + "('Error')", 'utf8');
res.end();
return false;
}
//文件是在请求过来之后发生更改的
if (stats.mtime.getTime() > req.socket._idleStart.getTime()) {
fs.readFile(filepath, 'utf8', function(err, data) {
res.writeHead(200, {
'Content-Type' : 'text/plain',
'Access-Control-Allow-Origin' : '*'
});
res.write(callbackFnName + "('" + data + "')", 'utf8');
res.end();
return false;
});
}
});
setTimeout(function() {
checkFile(req, res);
}, 10 * 1000);
}
/**
* 时间对象的格式化;
*/
Date.prototype.format = function (format) {
/*
* eg:format="YYYY-MM-dd hh:mm:ss";
*/
var o = {
"M+": this.getMonth() + 1, //month
"d+": this.getDate(), //day
"h+": this.getHours(), //hour
"m+": this.getMinutes(), //minute
"s+": this.getSeconds(), //second
"q+": Math.floor((this.getMonth() + 3) / 3), //quarter
"S": this.getMilliseconds() //millisecond
}
if (/(Y+)/i.test(format)) {
format = format.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
}
for (var k in o) {
if (new RegExp("(" + k + ")").test(format)) {
format = format.replace(RegExp.$1, RegExp.$1.length == 1 ? o[k] : ("00" + o[k]).substr(("" + o[k]).length));
}
}
return format;
}
setInterval(function() {
fs.writeFileSync(filepath, '当前时间:' + new Date().format('YYYY-MM-dd hh:mm:ss'), 'utf8');
}, 1000 * 1);
监听文件发生改变,就立即输出内容给前台页面请求。
这里有关于文件atime、ctime、mtime三者区别的详细介绍>>
关于File的元信息,可以参考这里(stat详解)>>
在控制台上打印出的req信息
{
socket: {
_handle: {
writeQueueSize: 0,
owner: [Circular],
onread: [Function: onread]
},
_pendingWriteReqs: 0,
_flags: 0,
_connectQueueSize: 0,
destroyed: false,
errorEmitted: false,
bytesRead: 602,
_bytesDispatched: 0,
allowHalfOpen: true,
writable: true,
readable: true,
_paused: false,
server: {
_connections: 1,
connections: [Getter / Setter],
allowHalfOpen: true,
_handle: [Object],
_events: [Object],
httpAllowHalfOpen: false,
_connectionKey: '4:0.0.0.0:8124'
},
_events: {
drain: [Function: ondrain],
timeout: [Object],
error: [Function],
close: [Object]
},
_idleTimeout: 120000,
_idleNext: {
_idleNext: [Circular],
_idlePrev: [Circular],
ontimeout: [Function]
},
_idlePrev: {
_idleNext: [Circular],
_idlePrev: [Circular],
ontimeout: [Function]
},
_idleStart: Tue Jul 24 2012 01: 18: 39 GMT + 0800(中国标准时间),
parser: {
_headers: [],
_url: '',
onHeaders: [Function: parserOnHeaders],
onHeadersComplete: [Function: parserOnHeadersComplete],
onBody: [Function: parserOnBody],
onMessageComplete: [Function: parserOnMessageComplete],
socket: [Circular],
incoming: [Circular],
maxHeaderPairs: 2000,
onIncoming: [Function]
},
ondata: [Function],
onend: [Function],
_httpMessage: {
output: [],
outputEncodings: [],
writable: true,
_last: false,
chunkedEncoding: false,
shouldKeepAlive: true,
useChunkedEncodingByDefault: true,
sendDate: true,
_hasBody: true,
_trailer: '',
finished: false,
socket: [Circular],
connection: [Circular],
_events: [Object]
}
},
connection: {
_handle: {
writeQueueSize: 0,
owner: [Circular],
onread: [Function: onread]
},
_pendingWriteReqs: 0,
_flags: 0,
_connectQueueSize: 0,
destroyed: false,
errorEmitted: false,
bytesRead: 602,
_bytesDispatched: 0,
allowHalfOpen: true,
writable: true,
readable: true,
_paused: false,
server: {
_connections: 1,
connections: [Getter / Setter],
allowHalfOpen: true,
_handle: [Object],
_events: [Object],
httpAllowHalfOpen: false,
_connectionKey: '4:0.0.0.0:8124'
},
_events: {
drain: [Function: ondrain],
timeout: [Object],
error: [Function],
close: [Object]
},
_idleTimeout: 120000,
_idleNext: {
_idleNext: [Circular],
_idlePrev: [Circular],
ontimeout: [Function]
},
_idlePrev: {
_idleNext: [Circular],
_idlePrev: [Circular],
ontimeout: [Function]
},
_idleStart: Tue Jul 24 2012 01: 18: 39 GMT + 0800(中国标准时间),
parser: {
_headers: [],
_url: '',
onHeaders: [Function: parserOnHeaders],
onHeadersComplete: [Function: parserOnHeadersComplete],
onBody: [Function: parserOnBody],
onMessageComplete: [Function: parserOnMessageComplete],
socket: [Circular],
incoming: [Circular],
maxHeaderPairs: 2000,
onIncoming: [Function]
},
ondata: [Function],
onend: [Function],
_httpMessage: {
output: [],
outputEncodings: [],
writable: true,
_last: false,
chunkedEncoding: false,
shouldKeepAlive: true,
useChunkedEncodingByDefault: true,
sendDate: true,
_hasBody: true,
_trailer: '',
finished: false,
socket: [Circular],
connection: [Circular],
_events: [Object]
}
},
httpVersion: '1.1',
complete: true,
headers: {
accept: 'image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash, application/x-silverlight, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, application/x-ms-application, application/x-ms-xbap, application/vnd.ms-xpsdocument, application/xaml+xml, */*',
'accept-language': 'zh-cn',
'user-agent': 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; GTB6; QQDownload 715; .NET CLR 2.0.50727; CIBA; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022)',
'accept-encoding': 'gzip, deflate',
host: 'localhost:8124',
connection: 'Keep-Alive'
},
trailers: {},
readable: false,
_paused: false,
_pendings: [],
_endEmitted: true,
url: '/?callback=abc&abc=1123',
method: 'GET',
statusCode: null,
client: {
_handle: {
writeQueueSize: 0,
owner: [Circular],
onread: [Function: onread]
},
_pendingWriteReqs: 0,
_flags: 0,
_connectQueueSize: 0,
destroyed: false,
errorEmitted: false,
bytesRead: 602,
_bytesDispatched: 0,
allowHalfOpen: true,
writable: true,
readable: true,
_paused: false,
server: {
_connections: 1,
connections: [Getter / Setter],
allowHalfOpen: true,
_handle: [Object],
_events: [Object],
httpAllowHalfOpen: false,
_connectionKey: '4:0.0.0.0:8124'
},
_events: {
drain: [Function: ondrain],
timeout: [Object],
error: [Function],
close: [Object]
},
_idleTimeout: 120000,
_idleNext: {
_idleNext: [Circular],
_idlePrev: [Circular],
ontimeout: [Function]
},
_idlePrev: {
_idleNext: [Circular],
_idlePrev: [Circular],
ontimeout: [Function]
},
_idleStart: Tue Jul 24 2012 01: 18: 39 GMT + 0800(中国标准时间),
parser: {
_headers: [],
_url: '',
onHeaders: [Function: parserOnHeaders],
onHeadersComplete: [Function: parserOnHeadersComplete],
onBody: [Function: parserOnBody],
onMessageComplete: [Function: parserOnMessageComplete],
socket: [Circular],
incoming: [Circular],
maxHeaderPairs: 2000,
onIncoming: [Function]
},
ondata: [Function],
onend: [Function],
_httpMessage: {
output: [],
outputEncodings: [],
writable: true,
_last: false,
chunkedEncoding: false,
shouldKeepAlive: true,
useChunkedEncodingByDefault: true,
sendDate: true,
_hasBody: true,
_trailer: '',
finished: false,
socket: [Circular],
connection: [Circular],
_events: [Object]
}
},
httpVersionMajor: 1,
httpVersionMinor: 1,
upgrade: false
}
参考: