2016-03-01 6 views
8

Vedo un comportamento strano con il codice here.Il client risponde sempre al precedente evento inviato dal server, non all'evento corrente

lato client (JavaScript):

<input type="text" id="userid" placeholder="UserID" /><br />` 
<input type="button" id="ping" value="Ping" /> 

<script> 
    var es = new EventSource('/home/message'); 
    es.onmessage = function (e) { 
     console.log(e.data); 
    }; 
    es.onerror = function() { 
     console.log(arguments); 
    }; 
    $(function() { 
     $('#ping').on('click', function() { 
      $.post('/home/ping', { 
       UserID: parseInt($('#userid').val()) || 0 
      }); 
     }); 
    }); 
</script> 

lato server (C#):

using System; 
using System.Collections.Concurrent; 
using System.Threading; 
using System.Web.Mvc; 
using Newtonsoft.Json; 

namespace EventSourceTest2.Controllers { 
    public class PingData { 
     public int UserID { get; set; } 
     public DateTime Date { get; set; } = DateTime.Now; 
    } 

    public class HomeController : Controller { 
     public ActionResult Index() { 
      return View(); 
     } 

     static ConcurrentQueue<PingData> pings = new ConcurrentQueue<PingData>(); 

     public void Ping(int userID) { 
      pings.Enqueue(new PingData { UserID = userID }); 
     } 

     public void Message() { 
      Response.ContentType = "text/event-stream"; 
      do { 
       PingData nextPing; 
       if (pings.TryDequeue(out nextPing)) { 
        var msg = "data:" + JsonConvert.SerializeObject(nextPing, Formatting.None) + "\n\n"; 
        Response.Write(msg); 
       } 
       Response.Flush(); 
       Thread.Sleep(1000); 
      } while (true); 
     } 
    } 
} 

Una volta che ho premuto il ping per aggiungere un nuovo elemento alla coda pings, il ciclo all'interno del metodo Message seleziona il nuovo oggetto ed emette un evento, tramite Response.Write (confermato utilizzando Debug.Print sul server). Tuttavia, il browser non attiva onmessage fino a quando non preme il ping una seconda volta e il browser emette un altro evento; a questo punto i dati dal primo evento raggiungono onmessage.

Come posso risolvere questo?


Per chiarire, questo è il comportamento che ci si aspetterebbe:

Client      Server 
------------------------------------------------------------------- 
Press Ping button 
XHR to /home/ping 
          Eneque new item to pings 
          Message loop issues server-sent event 
EventSource calls onmessage 

Questo è ciò che sta realmente accadendo:

Client      Server 
------------------------------------------------------------------- 
Press Ping button 
XHR to /home/ping 
          Eneque new item to pings 
          Message loop issues server-sent event 
(Nothing happens) 
Press Ping button again 
New XHR to /home/ping 
EventSource calls onmessage with previous event data 

(Durante l'esecuzione in Chrome richiesta message è elencato in la scheda Rete come sempre in sospeso. Non sono sicuro se questo è il comportamento normale degli eventi inviati dal server o forse è correlato al problema.)

Modifica

La rappresentazione della stringa della variabile msg dopo Response.Write assomiglia a questo:

"data:{\"UserID\":105,\"Date\":\"2016-03-11T04:20:24.1854996+02:00\"}\n\n" 

compreso molto chiaramente le nuove righe.

+0

controllo questo link ti aiuterà a controllare il tuo ultimo aggiornamento js e .css o non http://stackoverflow.com/questions/2185872/force-browsers-to-get-latest-js-and-css-files-in-asp-net -applicazione. Altrimenti aggiungi Expiration e Expires Header con questo link ti aiuterà https://support.microsoft.com/en-us/kb/234067 – Saineshwar

+0

@Saineshwar Hai persino letto la domanda? (1) Non ho bisogno di aggiornare la pagina; Ho già l'ultimo Javascript. (2) Microsoft KB non ha nulla a che fare con gli eventi inviati dal server (né potrebbe, dato che si concentra su Internet Explorer che non supporta eventi inviati dal server. –

risposta

0

io non sono sicuro se questo funzionerà, perché non posso provarlo subito, ma per quanto riguarda per aggiungere un fine ?:

Response.Flush(); 
Response.End(); 
+0

'Response.End()' impedisce tutti gli eventi. , la connessione è chiusa e il client riconosce che non ci saranno più eventi –

0

EDIT: La prego quindi di provare a creare l'oggetto e poi passando ad Enqueue? Come in:

PingData myData = new PingData { UserID = userID }; 
pings.Enqueue(myData); 

Ci potrebbe essere qualcosa di strano in cui Dequeue pensa è fatto il lavoro ma l'oggetto non è correttamente PingData ancora costruito.

Inoltre, è possibile provare console.log("I made it to the function") anziché console.log(e.data).

---- informazioni precedenti richieste di seguito ----

Si prega di accertarsi che il server di Debug.Print conferma questa riga di codice:

Response.Write("data:" + JsonConvert.SerializeObject(nextPing, Formatting.None) + "\n\n"); 

venga effettivamente eseguita? Si prega di ricontrollare questo. Se riesci a catturare la risposta inviata dal server, allora possiamo vedere di cosa si tratta?

Inoltre, possiamo vedere quali browser sono stati testati? Non tutti i browser supportano eventi del server.

+0

Ho provato su Chrome, che supporta eventi inviati dal server. AFAIK gli unici browser (supponendo che l'ultima versione) che non supportano SSE sono Internet Explorer/Edge. –

+0

@ZevSpitz Grazie per le informazioni. Devi sempre assicurarti che non sia una delle cose semplici. Aggiornamento della risposta con un paio di cose da provare. Sarebbe comunque bello vedere quale sia la risposta del server sembra come se potessi postarlo. – user1567453

+0

Vedere la mia modifica su come catturare la risposta –

1

Questa non è una risposta per dire, ma spero che ne porti uno. Sono stato in grado di farlo funzionare con il seguente codice.

public void Ping(int id) 
{ 
    pings.Enqueue(new PingData { ID = id }); 
    Response.ContentType = "text/plain"; 
    Response.Write("id received"); 
} 
public void Message() 
{ 
    int count = 0; 
    Response.ContentType = "text/event-stream"; 
    do { 
     PingData nextPing; 
     if (pings.TryDequeue(out nextPing)) { 
      Response.ClearContent(); 
      Response.Write("data:" + nextPing.ID.ToString() + " - " + nextPing.Date.ToLongTimeString() + "\n\n"); 
      Response.Write("event:time" + "\n" + "data:" + DateTime.Now.ToLongTimeString() + "\n\n"); 
      count = 0; 
      Response.Flush(); 
     } 
     if (!Response.IsClientConnected){break;} 
     Thread.Sleep(1000); 
     count++; 
    } while (count < 30); //end after 30 seconds of no pings 
} 

La riga di codice che fa la differenza è il secondo Response.Write. Il messaggio non viene visualizzato nel browser fino al prossimo ping simile al problema, ma il ping viene sempre visualizzato. Senza questa linea il ping apparirà solo dopo il ping successivo, oppure una volta che il mio contatore da 30 secondi si esaurirà.

Il messaggio mancante che appare dopo il timer di 30 secondi mi porta a concludere che questo è un problema .Net, o c'è qualcosa che ci manca. Non sembra essere un problema di origine degli eventi perché il messaggio appare su un evento del server e non ho avuto problemi con SSE con PHP.

Per riferimento, ecco il codice JavaScript e HTML con cui ho provato.

<input type="text" id="pingid" placeholder="ID" /><br /> 
<input type="button" id="ping" value="Ping" /> 

<div id="timeresponse"></div> 
<div id="pingresponse"></div> 

<script> 
    var es = new EventSource('/Home/Message'); 
    es.onmessage = function (e) { 
     console.log(e.data); 
     document.getElementById('pingresponse').innerHTML += e.data + " - onmessage<br/>"; 
    }; 
    es.addEventListener("ping", function (e) { 
     console.log(e.data); 
     document.getElementById('pingresponse').innerHTML += e.data + " - onping<br/>"; 
    }, false); 
    es.addEventListener("time", function (e) { 
     document.getElementById('timeresponse').innerHTML = e.data; 
    }, false); 
    es.onerror = function() { 
     console.log(arguments); 
     console.log("event source closed"); 
     es.close(); 
    }; 
    window.onload = function(){ 
     document.getElementById('ping').onclick = function() { 
      var xmlhttp = new XMLHttpRequest(); 
      xmlhttp.onload = function() { 
       console.log(this.responseText); 
      }; 
      var url = '/Home/Ping?id=' + document.getElementById('pingid').value; 
      xmlhttp.open("GET", url); 
      xmlhttp.send(); 
     }; 
    }; 
</script> 
0

Il comportamento predefinito di .net è di serializzare l'accesso allo stato di sessione. Blocca l'esecuzione parallela. Le richieste vengono elaborate in sequenza e l'accesso allo stato della sessione è esclusivo per la sessione. È possibile sovrascrivere lo stato predefinito per classe.

[SessionState(SessionStateBehavior.Disabled)] 
public class MyPulsingController 
{ 
} 

C'è un esempio di questo nella questione here.

+0

Fare ciò non ha alcun effetto sul problema. –

1

Poiché un evento è solo dati di testo, la mancanza della doppia interruzione di riga prima che il primo evento venga scritto per la risposta potrebbe influire sul client. L'esempio da mdn docs suggerisce

header("Content-Type: text/event-stream\n\n"); 

che potrebbe essere applicato applicano alla risposta NET manipolazione (notare gli effetti collaterali di Response.ClearContent()).

Se ci si sente troppo hacky, si potrebbe iniziare il vostro flusso con un commento keep-alive (se si vuole evitare timeout potrebbe essere necessario inviare periodicamente commenti):

: just a keep-alive comment followed by two line-breaks, Response.Write me first

+0

Una volta eseguita questa operazione, la richiesta 'message' non è più in sospeso nella scheda di rete di Chrome, ma il problema persiste. –

+0

@ZevSpitz: interessante, esaminando nuovamente il codice (sia front-end che back-end) controllerei la configurazione del server per problemi noti. [Il pool di applicazioni + compressione dinamica] (https://github.com/SignalR/SignalR/issues/273#issuecomment-9660117) sembra un inizio promettente –

Problemi correlati