我一直在开发一个nodejs服务器,为我在HTML5中开发的新网站提供服务器端事件。
当我telnet到服务器时,它正常工作,向我发送所需的HTTP响应标头,然后是我目前每2或3秒生成一次事件流,以证明它有效。
我已经尝试了最新版本的FireFox,Chrome和Opera,他们创建了EventSource对象并连接到nodejs服务器,但是没有一个浏览器生成任何事件,包括onopen,onmessage和onerror。
但是,如果我停止我的nodejs服务器,终止与浏览器的连接,他们都会突然发送所有消息,并显示我的所有事件。然后,浏览器都会尝试按照规范重新连接到服务器。
我在网络服务器上托管所有内容。没有任何东西在本地文件中运行。
我已经阅读了我在网上找到的所有内容,包括我购买的书籍,没有任何迹象表明存在此类问题。我有什么遗失的东西吗?
示例服务器实施
var http = require('http');
var requests = [];
var server = http.Server(function(req, res) {
var clientIP = req.socket.remoteAddress;
var clientPort = req.socket.remotePort;
res.on('close', function() {
console.log("client " + clientIP + ":" + clientPort + " died");
for(var i=requests.length -1; i>=0; i--) {
if ((requests[i].ip == clientIP) && (requests[i].port == clientPort)) {
requests.splice(i, 1);
}
}
});
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Access-Control-Allow-Origin': '*',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive'});
requests.push({ip:clientIP, port:clientPort, res:res});
res.write(": connected.\n\n");
});
server.listen(8080);
setInterval(function test() {
broadcast('poll', "test message");
}, 2000);
function broadcast(rtype, msg) {
var lines = msg.split("\n");
for(var i=requests.length -1; i>=0; i--) {
requests[i].res.write("event: " + rtype + "\n");
for(var j=0; j<lines.length; j++) {
if (lines[j]) {
requests[i].res.write("data: " + lines[j] + "\n");
}
}
requests[i].res.write("\n");
}
}
示例html页面
<!DOCTYPE html>
<html>
<head>
<title>SSE Test</title>
<meta charset="utf-8" />
<script language="JavaScript">
function init() {
if(typeof(EventSource)!=="undefined") {
var log = document.getElementById('log');
if (log) {
log.innerHTML = "EventSource() testing begins..<br>";
}
var svrEvents = new EventSource('/sse');
svrEvents.onopen = function() {
connectionOpen(true);
}
svrEvents.onerror = function() {
connectionOpen(false);
}
svrEvents.addEventListener('poll', displayPoll, false); // display multi choice and send back answer
svrEvents.onmessage = function(event) {
var log = document.getElementById('log');
if (log) {
log.innerHTML += 'message: ' + event.data + "<br>";
}
// absorb any other messages
}
} else {
var log = document.getElementById('log');
if (log) {
log.innerHTML = "EventSource() not supported<br>";
}
}
}
function connectionOpen(status) {
var log = document.getElementById('log');
if (log) {
log.innerHTML += 'connected: ' + status + "<br>";
}
}
function displayPoll(event) {
var html = event.data;
var log = document.getElementById('log');
if (log) {
log.innerHTML += 'poll: ' + html + "<br>";
}
}
</script>
</head>
<body onLoad="init()">
<div id="log">testing...</div>
</body>
</html>
这些示例是基本的,但与我在书籍和在线中看到的每个其他演示相同。如果我结束客户端连接或终止服务器,eventSource似乎只能工作,但这将是轮询而不是SSE,我特别想使用SSE。
有趣的是,当我在网上使用这些演示时,来自html5rock的演示似乎也没有按预期工作..
答案 0 :(得分:36)
破解了! :)
感谢Tom Kersten帮助我进行测试的一些帮助。原来代码不是问题。
警告..如果您的客户端使用任何拦截Web请求的防病毒软件,可能会导致此处出现问题。在这种情况下,提供企业级防病毒和防火墙保护的Sophos Endpoint Security具有称为Web保护的功能。在此功能中可以选择扫描下载;似乎SSE连接被视为下载,因此在关闭连接并接收流以进行扫描之前,不会将其释放到浏览器。禁用此选项可以解决问题。我提交了一份错误报告,但其他反病毒系统也可能会这样做。
感谢您的建议并帮助每个人:)
答案 1 :(得分:2)
http://www.w3.org/TR/eventsource/#parsing-an-event-stream
由于为这些资源建立了与远程服务器的连接 预计是长寿的,UAs应该确保适当的 使用缓冲。特别是,线条缓冲线是 定义为以单个U + 000A结束LINE FEED(LF)字符是安全的, 使用不同的预期行结尾阻止缓冲或行缓冲 可能导致事件发送延迟。
尝试使用行结尾("\r\n"
代替"\n"
)。
http://www.w3.org/TR/eventsource/#notes
作者也应注意,HTTP分块可能会出乎意料 对该协议可靠性的负面影响。在可能的情况, 除非费率,否则应禁用服务事件流的分块 消息足够高,不重要。
答案 2 :(得分:0)
我修改了您的服务器端脚本,“似乎”部分适用于Chrome 但是每2个广播连接断开连接;只有1个可以在客户端上显示。
Firefox适用于第一次广播并因此错误而停止:
错误:加载页面时,/ sse的连接中断。
Chrome会尝试重新连接并接收第3次广播。
我认为它也与防火墙设置有关,但无法解释为什么有时可行。
注意:对于响应的事件监听器(第10行),'close'&amp; '结束'有不同的结果,
你可以尝试一下,我的结果是[关闭:1成功/ 2广播]&amp; [结束:1成功/ 8广播]
var http = require('http'), fs = require('fs'), requests = [];
var server = http.Server(function(req, res) {
var clientIP = req.socket.remoteAddress;
var clientPort = req.socket.remotePort;
if (req.url == '/sse') {
var allClient="";for(var i=0;i<requests.length;i++){allClient+=requests[i].ip+":"+requests[i].port+";";}
if(allClient.indexOf(clientIP+":"+clientPort)<0){
requests.push({ip:clientIP, port:clientPort, res:res});
res.on('close', function() {
console.log("client " + clientIP + ":" + clientPort + " died");
for(var i=requests.length -1; i>=0; i--) {
if ((requests[i].ip == clientIP) && (requests[i].port == clientPort)) {
requests.splice(i, 1);
}
}
});
}
}else{
res.writeHead(200, {'Content-Type': 'text/html'});
res.write(fs.readFileSync('./test.html'));
res.end();
}
});
server.listen(80);
setInterval(function test() {
broadcast('poll', "test message");
}, 500);
var broadcastCount=0;
function broadcast(rtype, msg) {
if(!requests.length)return;
broadcastCount++;
var lines = msg.split("\n");
for(var i = requests.length - 1; i >= 0; i--) {
requests[i].res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive'
});
requests[i].res.write("event: " + rtype + "\n");
for(var j = 0; j < lines.length; j++) {
if(lines[j]) {
requests[i].res.write("data: " + lines[j] + "\n");
}
}
requests[i].res.write("data: Count\: " + broadcastCount + "\n");
requests[i].res.write("\n");
}
console.log("Broadcasted " + broadcastCount + " times to " + requests.length + " user(s).");
}