2012-10-19 10 views
20

Ho sviluppato un server nodejs per fornire eventi lato server per un nuovo sito Web che sto sviluppando in HTML5.JavaScript EventSource SSE non attiva nel browser

Quando eseguo il telnet sul server, funziona correttamente, inviando le intestazioni di risposta HTTP necessarie seguite da un flusso di eventi che sto generando ogni 2 o 3 secondi solo per dimostrare che funziona.

Ho provato l'ultima versione di FireFox, Chrome e Opera e creano l'oggetto EventSource e si connettono al server nodejs OK ma nessuno dei browser genera nessuno degli eventi, inclusi onopen, onmessage e onerror.

Tuttavia, se interrompo il mio server nodejs, interrompendo la connessione dai browser, tutti improvvisamente inviano tutti i messaggi e tutti i miei eventi sono mostrati. I browser cercano quindi di riconnettersi al server come da specifiche.

Sto ospitando tutto su un server web. niente è in esecuzione nei file locali.

Ho letto tutto quello che posso trovare online, compresi i libri che ho acquistato e niente indica alcun problema del genere. C'è qualcosa che mi manca?

Un'implementazione server di esempio

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"); 
    } 
} 

Un campione pagina 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> 

Questi esempi sono essenziali ma della stessa varietà come ogni altro demo che ho visto nei libri e in linea. L'eventSource sembra funzionare solo se si termina una connessione client o si termina il server, ma questo sarebbe il polling invece di SSE e in particolare desidero utilizzare SSE.

È interessante notare che, demo, come thouse da html5rock sembrano anche non funzionare del tutto come previsto quando li uso online ..

+0

Dopo aver utilizzato le console Javascript dei vari browser, sembrerebbe che l'EventSource emette un GET/SSE HTTP/1.1 al mio assistente, che si prevede, ma una risposta è non mostrato fino a quando la connessione è chiusa. È questo in base alla progettazione? lo stato della connessione dovrebbe essere keep-alive. EventSource() esegue semplicemente il buffering di tutto ciò che viene inviato fino a quando la pagina viene chiusa o è in attesa di una sorta di marker nei dati che sta ricevendo? – Dan

+2

Oltre al nodo sever, ho anche provato PHP come molti esempi sembrano illustrare quello. Di nuovo, a meno che non chiudi la connessione, questo mostra lo stesso comportamento già menzionato. Sono sorpreso che nessun altro abbia avuto problemi come questo? O sto facendo qualcosa di completamente sbagliato? – Dan

+1

Sembra simile a http://stackoverflow.com/questions/6068820/node-js-problems-with-response-write o http://stackoverflow.com/questions/6258210/how-can-i-output-data- before-i-end-the-response – Victor

risposta

33

incrinato esso! :)

Grazie all'aiuto di Tom Kersten che mi ha aiutato con i test. Risulta il codice non è il problema.

Attenzione .. se il client utilizza qualsiasi tipo di software anti-virus che intercetta richieste web, potrebbe causare problemi qui. In questo caso, Sophos Endpoint Security, che fornisce protezione anti-virus e firewall di livello enterprise, ha una funzionalità chiamata protezione web. All'interno di questa funzionalità è un'opzione per scansionare i download; sembra che la connessione SSE sia considerata come un download e quindi non rilasciata nel browser fino a quando la connessione non viene chiusa e lo stream ricevuto per la scansione. Disabilitando questa opzione si cura il problema. Ho inviato una segnalazione di errore ma altri sistemi antivirus potrebbero fare lo stesso.

grazie per i vostri suggerimenti e aiutare tutti :)

+0

Confermato anche questo problema con "Sophos Home". Disattivare la protezione Web nella loro dashboard Web ha risolto il problema. Ho twittato anche il loro canale di supporto al riguardo ... speriamo che un aggiornamento possa consentire il passaggio di testo/eventi senza bloccare le comunicazioni. –

2

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

Since connections established to remote servers for such resources are expected to be long-lived, UAs should ensure that appropriate buffering is used. In particular, while line buffering with lines are defined to end with a single U+000A LINE FEED (LF) character is safe, block buffering or line buffering with different expected line endings can cause delays in event dispatch.

provare a giocare con il fine riga ("\r\n" invece di "\n").

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

Authors are also cautioned that HTTP chunking can have unexpected negative effects on the reliability of this protocol. Where possible, chunking should be disabled for serving event streams unless the rate of messages is high enough for this not to matter.

+0

grazie per il suggerimento vincitore; cambiare i terminatori di linea non fa differenza. lo stesso problema si verifica ancora se interrompo il chunking. – Dan

+0

Dipende dal sistema operativo su cui viene eseguito il server. Per Linux, è '\ n', per Windows -' \ r \ n'. Per le app indipendenti dalla piattaforma, devi utilizzare la costante 'PHP_EOL'. – Soaku

0

ho modificato lo script lato server, che 'sembra' in parte lavora per Chrome.
Ma l'interruzione di connessione per ogni trasmissione 2 & solo 1 può essere visualizzata sul client.

Firefox funziona per il 1 ° broadcast e fermata da questo errore:

Error: The connection to /sse was interrupted while the page was loading.

e Chrome tenterà di riconnettersi e ha ricevuto 3 ° broadcast.

Penso che sia correlato all'impostazione del firewall, ma non può spiegare perché a volte funzionerà.

Nota: Per listener di eventi di risposta (linea 10), 'stretta' & 'fine' avere risultati diversi,
si può provare e il mio risultato è [close: 1 successo/2 trasmissione] & [ fine: 1 successo/8 trasmissione]

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)."); 
} 
Problemi correlati