加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > asp.Net > 正文

asp.net-mvc – 在同一个流连接上返回多个结果以实现HTML5 Serve

发布时间:2020-12-16 06:22:01 所属栏目:asp.Net 来源:网络整理
导读:我试图在我的MVC 4 Web上设置一个轻量级的 HTML5 Server-Sent Event实现,而不使用其中一个库来实现套接字和类似物. 我正在尝试的轻量级方法是: 客户端: EventSource(或IE的jquery.eventsource) 服务器端: 使用AsynchController进行长轮询(很抱歉在这里删
我试图在我的MVC 4 Web上设置一个轻量级的 HTML5 Server-Sent Event实现,而不使用其中一个库来实现套接字和类似物.
我正在尝试的轻量级方法是:

客户端:
EventSource(或IE的jquery.eventsource)

服务器端:
使用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}nn",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}nn",value));
                        //response.Write(string.Format("id:{0}n",this.Version));
                    }
                    else
                    {
                        response.Write("");
                    }
                }
            }
        }
    }

问题出在服务器端,因为预期结果与实际发生的结果之间仍然存在很大差距.

预期结果:

> EventSource打开与服务器的流连接,
>服务器保持打开一段安全时间(比方说,2分钟),以防止线程从死客户泄漏,
>当服务器收到新的消息事件(并排入线程安全集合,如BlockingCollection)时,它们将在开放流中推送到客户端:

>消息1在T 0ms接收,在T x被推送到客户端
>消息2在T 200ms接收,在T x 200ms推送到客户端

实际行为:

> EventSource打开与服务器的流连接,
>服务器保持打开状态,直到消息事件到达(感谢长轮询)
>一旦收到消息,MVC就会推送消息并关闭连接.
> EventSource必须重新打开连接,这会在几秒钟后发生.

>消息1在T 0ms接收,在T x 3200ms推送到客户端

这是不行的,因为它失败了使用SSE的目的,因为客户端再次开始重新连接,因为正常轮询和消息传递被延迟.

现在,问题是:
在发送第一条消息并在同一连接上发送更多消息后,是否存在保持连接打开的本机方式?

解决方法

您希望使用 Response.Flush发送数据,而不是依赖于SimpleComplete来发送数据.通过执行AsyncManager.OutstandingOperations.Decrement(),您告诉AsyncController您已完成处理请求并且您已准备好发送响应并关闭连接.相反,您应该避免调用OutStandingOperations.Decrement()直到连接丢失等.每当您想要将消息推送到客户端时,您直接从某个后台线程调用 Response.Write和Response.Flush.此外,AsyncControllers具有默认超时,之后它们会自动关闭连接.为了解决这个问题,您需要使用 NoAsyncTimeoutAttribute进行相关操作.

作为旁注,AsyncController的接口实际上不允许实现SSE流的干净方式;鉴于我使用的是Asp.NET MVC 4,我个人会实现HttpTaskAsyncHandler.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读