最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Client always responding to previous server-sent event, not current event - Stack Overflow

programmeradmin25浏览0评论

I am seeing odd 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);
        }
    }
}

Once I've pressed ping to add a new item to the pings queue, the loop inside the Message method picks the new item up and issues an event, via Response.Write (confirmed using Debug.Print on the server). However, the browser doesn't trigger onmessage until I press ping a second time, and the browser issues another event; at which 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 is actually happening:

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

(While running in Chrome the message request is listed in the Network tab as always pending. I'm not sure if this is the normal behavior of server-sent events, or perhaps it's related to the issue.)

Edit

The string representation of the msg variable after Response.Write looks like this:

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

very clearly including the newlines.

I am seeing odd 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);
        }
    }
}

Once I've pressed ping to add a new item to the pings queue, the loop inside the Message method picks the new item up and issues an event, via Response.Write (confirmed using Debug.Print on the server). However, the browser doesn't trigger onmessage until I press ping a second time, and the browser issues another event; at which 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 is actually happening:

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

(While running in Chrome the message request is listed in the Network tab as always pending. I'm not sure if this is the normal behavior of server-sent events, or perhaps it's related to the issue.)

Edit

The string representation of the msg variable after Response.Write looks like this:

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

very clearly including the newlines.

Share Improve this question edited Jul 9, 2022 at 20:36 Zev Spitz asked Mar 1, 2016 at 9:46 Zev SpitzZev Spitz 15.4k8 gold badges74 silver badges148 bronze badges 2
  • check this link will help you to check your latest update js and .css or not stackoverflow./questions/2185872/…. Else add Expiration and the Expires Header with this link will help you support.microsoft./en-us/kb/234067 – Saineshwar Bageri - MVP Commented Mar 8, 2016 at 7:27
  • @Saineshwar Did you even read the question? (1) I have no need to refresh the page; I already have the latest Javascript. (2) The Microsoft KB has nothing to do with server-sent events (nor could it, as it is focused on Internet Explorer which doesn't support server-sent events. – Zev Spitz Commented Mar 8, 2016 at 13:03
Add a ment  | 

5 Answers 5

Reset to default 1

This isn't an answer per say but hopefully it will lead one. I was able to get it working 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 the second Response.Write. The message doesn't appear in the browser until the next ping similar to your issue, but the ping always appears. Without that line the ping will appear only after the next ping, or once my 30 second counter runs out.

The missing message appearing after the 30 second timer leads me to conclude that this is either a .Net issue, or there's something we're missing. It doesn't seem to be an event source issue because the message appears on a server event, and I've had no trouble doing SSE with PHP.

For reference, here's the JavaScript and HTML I used to test with.

<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>

Since an eventstream is just text data, missing the double line break before the first event is written to response could affect the client. The example from mdn docs suggests

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

Which could be applied apply to .NET response handling (note the side effects of Response.ClearContent()).

If it feels too hacky, you could start your stream with a keep-alive ment (if you want to avoid timing out you may have to send ments periodically):

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

I'm not sure if this will work because I can't try it now, but what about to add an End?:

Response.Flush();
Response.End();

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

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

There is an illustration of this in the question here.

EDIT: Would you please try creating the object first and then passing it to Enqueue? As in:

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

There might be something strange going on where Dequeue thinks it's done the job but the the PingData object isn't properly constructed yet.

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

---- PREVIOUS INFORMATION REQUESTED BELOW ----

Please make sure that the server Debug.Print confirms this line of code:

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

Is actually executed? Please double check this. If you can capture the server sent response then can we see what it is?

Also could we see what browsers you've tested on? Not all browsers support server events.

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论