JavaScript EventSource SSE不在浏览器中触发

我一直在开发nodejs服务器,为我在HTML5中开发的新网站提供服务器端事件。

当我远程login到服务器时,它工作正常,向我发送所需的HTTP响应头,然后是一个事件stream,我现在每隔2或3秒就会生成一个事件stream,以certificate它是有效的。

我已经尝试过最新版本的FireFox,Chrome和Opera,他们创build了EventSource对象并连接到nodejs服务器,但没有一个浏览器生成任何事件,包括onopen,onmessage和onerror。

但是,如果我停止我的nodejs服务器,终止从浏览器的连接,他们都突然调度所有的消息,我所有的事件都显示出来。 浏览器然后按照规范尝试重新连接到服务器。

我在networking服务器上托pipe所有东西。 没有在本地文件中运行。

我已经阅读了所有我可以在网上find的东西,包括我已经购买的书籍,没有任何信息表明有这样的问题 有什么我失踪?

一个示例服务器实现

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的thouse的演示似乎也不像预期的那样工作。

破解它! 🙂

感谢Tom Kersten帮助我进行testing的一些帮助。 原来的代码是不是问题。

被警告..如果你的客户使用任何拦截Web请求的反病毒软件,这可能会导致问题在这里。 在这种情况下,提供企业级防病毒和防火墙保护的Sophos Endpoint Security具有一项称为Web保护的function。 在这个function是一个选项来扫描下载; 似乎SSE连接被视为一个下载,因此不会被释放到浏览器,直到连接closures和接收到的stream扫描。 禁用此选项可以解决问题。 我已经提交了一个错误报告,但其他反病毒系统也可能会这样做。

感谢您的build议,并帮助大家:)

http://www.w3.org/TR/eventsource/#parsing-an-event-stream

由于build立到这些资源的远程服务器的连接预计将是长期的,所以UA应确保使用适当的缓冲。 特别是,当线路缓冲行被定义为以单个U + 000A LINE FEED(LF)字符结束时,用不同的期望行结束的块缓冲或行缓冲可能导致事件调度中的延迟。

尝试播放行尾( "\r\n"而不是"\n" )。

http://www.w3.org/TR/eventsource/#notes

作者也警告说,HTTP分块会对这个协议的可靠性产生意想不到的负面影响。 在可能的情况下,应该禁用分块处理事件stream,除非消息的速率足够高,否则无关紧要。

我修改了您的服务器端脚本,这“似乎”部分适用于Chrome。
但是每2个广播的连接中断只有1个可以在客户端显示。

火狐工作第一次广播,并停止此错误:

错误:在加载页面时,到/ sse的连接中断。

Chrome会尝试重新连接并收到第三个广播。

我认为这也与防火墙设置有关,但无法解释为什么有时会起作用。

注意 :对于响应的事件监听器(第10行),“close”和“end”有不同的结果,
您可以尝试一下,我的结果是[closures:1次成功/ 2次广播]和[结束: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)."); }