The client always responds to a previous event sent by the server, rather than the current event

I see weird behavior with the code here .

Client Side (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> 

Server Side (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); } } } 

As soon as I pressed ping to add a new element to the pings , the loop inside the Message method picks the new element up and throws an event through Response.Write (confirmed by Debug.Print on the server). However, the browser does not start onmessage until I press the ping button a second time and the browser throws another event; at this point, the data from the first event reaches onmessage .

How can i fix this?


To clarify, this is the behavior I would expect:

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

This is what actually happens:

 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 

(When launched in Chrome, the Message request is listed on the Network tab as always pending. I'm not sure if this is the usual behavior of events sent by the server, or is possibly related to the problem.)

Edit

The string representation of the msg variable after Response.Write as follows:

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

very clear, including newlines.

+8
javascript asp.net-mvc server-sent-events
source share
5 answers

This is not an answer for one, but hopefully he will lead it. I managed to get it to work with the following code.

 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 } 

The line of code that makes the difference is second Response.Write . The message does not appear in the browser until the next ping looks like your problem, but the ping always appears. Without this line, the ping will appear only after the next ping or after the 30-second counter has expired.

The missing message that appeared after the 30-second timer leads me to the conclusion that this is either a .Net problem or something is missing. This doesn't seem to be a problem with the event source, because the message appears in the server event, and I had no problems with SSE with PHP.

For reference, here is the JavaScript and HTML that I used for testing.

 <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> 
+1
source share

Since the flow of events is just text data, the absence of a double line break before the first event is recorded in the response can affect the client. An example from mdn docs suggests

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

which may apply apply to the processing of .NET responses (note the side effects of Response.ClearContent() ).

If it is too hacked, you can start your thread with a keep-alive comment (if you want to avoid a timeout, you may have to send comments periodically):

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

+1
source share

I'm not sure if this will work, because I cannot try it now, but how to add End ?:

 Response.Flush(); Response.End(); 
0
source share

EDIT: Could you try to create an object first and then pass it to Enqueue? How in:

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

Perhaps something strange is happening where Dequeue believes he did the job, but the PingData object has not yet been properly built.

We can also try console.log("I made it to the function") instead of console.log(e.data) .

---- PREVIOUS INFORMATION STATED BELOW ----

Verify that the Debug.Print server confirms this line of code:

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

Actually being performed? Please double check this. If you can capture the response of the sent server, can we see what it is?

Also we can see which browsers you tested? Not all browsers support server events.

0
source share

The default .net behavior is to serialize access to session state. It blocks parallel execution. Requests are processed sequentially, and access to session state is exclusive to the session. You can override the default state for each class.

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

There is an illustration in this question.

0
source share

All Articles