客户端始终响应上一个服务器发送的事件,而不是当前事件
Client always responding to previous server-sent event, not current event
我在这里看到代码的奇怪行为。
客户端(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
方法中的循环就会选取新项目并通过Response.Write
发出事件(使用服务器上的 Debug.Print 确认)。但是,浏览器不会触发onmessage
,直到我第二次按 ping,并且浏览器发出另一个事件;此时,来自第一个事件的数据到达 onmessage
。
我该如何解决这个问题?
<小时 />澄清一下,这是我期望的行为:
Client Server
-------------------------------------------------------------------
Press Ping button
XHR to /home/ping
Eneque new item to pings
Message loop issues server-sent event
EventSource calls onmessage
这是实际发生的事情:
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
(在 Chrome 中运行时,message
请求在"网络"标签中列为"始终待处理"。我不确定这是否是服务器发送事件的正常行为,或者可能与问题有关。
编辑
Response.Write
之后的msg
变量的字符串表示形式如下所示:
"data:{'"UserID'":105,'"Date'":'"2016-03-11T04:20:24.1854996+02:00'"}'n'n"
非常清楚地包括换行符。
这不是一个答案,但希望它能引导一个答案。我能够让它使用以下代码工作。
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>
由于事件流只是文本数据,因此在将第一个事件写入响应之前缺少双换行符可能会影响客户端。来自 mdn 文档的示例建议
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
我不确定这是否有效,因为我现在无法尝试,但是添加一个 End 呢?
Response.Flush();
Response.End();
.net 的默认行为是序列化对会话状态的访问。它阻止并行执行。请求按顺序处理,对会话状态的访问是会话的独占。您可以覆盖每个类的默认状态。
[SessionState(SessionStateBehavior.Disabled)]
public class MyPulsingController
{
}
这里的问题中对此进行了说明。
编辑:请您先尝试创建对象,然后将其传递给 Enqueue?如:
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");
真的被执行了吗?请仔细检查一下。如果您可以捕获服务器发送的响应,那么我们可以看到它是什么吗?
我们还能看看你测试过的浏览器吗?并非所有浏览器都支持服务器事件。
- 在Javascript中的ajax响应之后未调用Dropdown事件
- Ember视图未响应用户启动的事件
- 如何使用更新图像源以响应新闻事件
- 为什么我没有得到对Comet服务器流事件的实时响应
- 基于类的单击事件在10行之后不响应-数据表
- EventSource 的响应具有非“文本/事件流”的 MIME 类型(“文本/纯文本”)
- 为什么不是't html<对象>元素响应鼠标事件
- jquery多个事件响应
- 使用命名函数设置多个事件响应
- 加载超链接页面后如何响应事件
- 响应菜单问题-如何在移动环境中处理HREF和onclick事件
- 尝试使用鼠标点击事件响应编写jQuery HTML5聊天框程序
- 使引导标记响应,jquery 事件丢失
- 节点 js 中的请求和响应事件如何彼此不同
- Jquery bind 事件没有响应
- 谷歌地图USGSO在同一级别标记上覆盖点击事件多响应
- Youtube iframe没有响应通过上图传递的指针事件的点击:none
- jQuery:点击禁用点击事件,直到ajax调用响应
- Gmaps API 从事件响应中丢失了 latlng
- 使用Jquery为循环中的元素添加事件响应器