我看到代码here的奇怪行为。
客户端(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>
服务器端(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);
}
}
}
在我按下ping以向pings
队列添加新项目后,Message
方法中的循环会选择新项目并通过{{1}发出事件(在服务器上使用Debug.Print确认)。但是,在第二次按ping之前,浏览器不会触发Response.Write
,浏览器会发出另一个事件;此时第一个事件的数据达到onmessage
。
我该如何解决这个问题?
澄清一下,这是我期望的行为:
onmessage
这就是实际发生的事情:
Client Server
-------------------------------------------------------------------
Press Ping button
XHR to /home/ping
Eneque new item to pings
Message loop issues server-sent event
EventSource calls onmessage
(在Chrome中运行时,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
请求在“网络”标签中列为常规待处理状态。我不确定这是否是服务器发送事件的正常行为,或者可能是&#39;与此问题有关。)
修改
message
之后的msg
变量的字符串表示如下所示:
Response.Write
非常明确地包括换行符。
答案 0 :(得分:1)
这不是每个人的答案,但希望它会引领一个。我能够使用以下代码。
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
}
产生差异的代码行是第二个Response.Write
。在下一次类似于您的问题的ping之前,该消息不会出现在浏览器中,但始终会显示ping。如果没有该行,ping将仅在下一次ping之后出现,或者在我的30秒计数器用完之后出现。
在30秒计时器之后出现的缺失消息让我得出结论,这可能是.Net问题,或者我们缺少的东西。它似乎不是一个事件源问题,因为该消息出现在服务器事件上,并且我在使用PHP进行SSE时没有遇到任何问题。
供参考,这里是我用来测试的JavaScript和HTML。
<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 :(得分:1)
由于事件流只是文本数据,因此在将第一个事件写入响应之前缺少双线中断可能会影响客户端。来自mdn docs的示例建议
header("Content-Type: text/event-stream\n\n");
可应用于.NET响应处理(请注意Response.ClearContent()
的副作用)。
如果觉得太乱,你可以用保持评论开始你的流(如果你想避免超时,你可能需要定期发送评论):
: just a keep-alive comment followed by two line-breaks, Response.Write me first
答案 2 :(得分:0)
我不确定这是否有效,因为我现在无法尝试,但是要添加一个End?:
re.search("^[A-Z]+([A-Z\w]+|\s)+$","Diploma in Fine Art from Royal School of Art & Design")
答案 3 :(得分:0)
PingData myData = new PingData { UserID = userID };
pings.Enqueue(myData);
可能会有一些奇怪的事情,Dequeue认为它完成了这项工作,但PingData对象尚未正确构建。
我们也可以尝试console.log("I made it to the function")
而不是console.log(e.data)
。
----以下要求的以前的信息----
请确保服务器Debug.Print确认这行代码:
Response.Write("data:" + JsonConvert.SerializeObject(nextPing, Formatting.None) + "\n\n");
实际执行了吗?请仔细检查一下。如果您可以捕获服务器发送的响应,那么我们可以看到它是什么吗?
我们还能看到你测试过的浏览器吗?并非所有浏览器都支持服务器事件。
答案 4 :(得分:0)
.net的默认行为是序列化对会话状态的访问。它会阻止并行执行。按顺序处理请求,并且会话状态的访问权限对会话是唯一的。您可以覆盖每个类的默认状态。
[SessionState(SessionStateBehavior.Disabled)]
public class MyPulsingController
{
}
问题here中有一个例子。