我正在尝试在我的 MVC 4 Web 上设置一个轻量级的 HTML5 服务器发送事件实现,而不使用可用于实现套接字和类似内容的库之一。我正在尝试的轻量级方法是:
客户端:(
EventSource
或jquery.eventsource
用于 IE)
服务器端:长轮询AsynchController
(很抱歉将原始测试代码放在这里,但只是为了提供一个想法)
public class HTML5testAsyncController : AsyncController
{
private static int curIdx = 0;
private static BlockingCollection<string> _data = new BlockingCollection<string>();
static HTML5testAsyncController()
{
addItems(10);
}
//adds some test messages
static void addItems(int howMany)
{
_data.Add("started");
for (int i = 0; i < howMany; i++)
{
_data.Add("HTML5 item" + (curIdx++).ToString());
} _data.Add("ended");
}
// here comes the async action, 'Simple'
public void SimpleAsync()
{
AsyncManager.OutstandingOperations.Increment();
Task.Factory.StartNew(() =>
{
var result = string.Empty; var sb = new StringBuilder();
string serializedObject = null;
//wait up to 40 secs that a message arrives
if (_data.TryTake(out result, TimeSpan.FromMilliseconds(40000)))
{
JavaScriptSerializer ser = new JavaScriptSerializer();
serializedObject = ser.Serialize(new { item = result, message = "MSG content" });
sb.AppendFormat("data: {0}\n\n", serializedObject);
}
AsyncManager.Parameters["serializedObject"] = serializedObject;
AsyncManager.OutstandingOperations.Decrement();
});
}
// callback which returns the results on the stream
public ActionResult SimpleCompleted(string serializedObject)
{ ServerSentEventResult sar = new ServerSentEventResult();
sar.Content = () => { return serializedObject; };
return sar;
}
//pushes the data on the stream in a format conforming HTML5 SSE
public class ServerSentEventResult : ActionResult
{
public ServerSentEventResult() { }
public delegate string GetContent();
public GetContent Content { get; set; }
public int Version { get; set; }
public override void ExecuteResult(ControllerContext context)
{
if (context == null)
{
throw new ArgumentNullException("context");
} if (this.Content != null)
{
HttpResponseBase response = context.HttpContext.Response;
// this is the content type required by chrome 6 for server sent events
response.ContentType = "text/event-stream";
response.BufferOutput = false; // this is important because chrome fails with a "failed to load resource" error if the server attempts to put the char set after the content type
response.Charset = null;
string[] newStrings = context.HttpContext.Request.Headers.GetValues("Last-Event-ID");
if (newStrings == null || newStrings[0] != this.Version.ToString())
{
string value = this.Content();
response.Write(string.Format("data:{0}\n\n", value));
//response.Write(string.Format("id:{0}\n", this.Version));
}
else
{
response.Write("");
}
}
}
}
}
问题出在服务器端,因为预期结果与实际情况之间仍有很大差距。
预期结果:
EventSource
打开到服务器的流连接,- the server keeps it open for a safe time (say, 2 minutes) so that I am protected from thread leaking from dead clients,
as new message events are received by the server (and enqueued to a thread safe collection such as
BlockingCollection
) they are pushed in the open stream to the client:- message 1 received at T+0ms, pushed to the client at T+x
- message 2 received at T+200ms, pushed to the client at T+x+200ms
Actual behaviour:
EventSource
opens a stream connection to the server,- the server keeps it open until a message event arrives (thanks to long polling)
- once a message is received, MVC pushes the message and closes the connection.
EventSource
has to reopen the connection and this happens after a couple of seconds.- message 1 received at T+0ms, pushed to the client at T+x
- 消息 2 在T+200ms收到,在T+x+3200ms推送到客户端
这是不行的,因为它违背了使用 SSE 的目的,因为客户端像正常轮询一样重新开始重新连接,并且消息传递被延迟。
现在,问题是:在发送第一条消息并在同一连接上发送更多消息后,是否有一种本地方法可以保持连接打开?