Asp.Net实现Http长连接推送

话说最新帮一个朋友搞智能家居方面的东西,做一个云平台。主要作用手机在局域网外环境时对手机客户端和智能网关中命令的互相转发。

目前已经有了一个稳定的Socket版本,但是考虑到以后的扩展和性能指标要改成Http长连接形式,这确实是一个很逗逼的方案。

下面普及一下Http长连接的概念,所谓的Http长连接其实不是指像Socket那样的建立一个连接client端和server端来回传递数据。Http长连接指的是客户端发送给服务器端的Http请求不会马上得到服务器的应答,而是当满足一定条件时服务器才“主动”将数据返回给客户端,这时一次Http请求才算结束。实际应用中为客户端在结束了一个长连接后往往要再次建立一个长连接,也就是客户端到服务器端总是维持一个打开的下行Http通道。

搞过Socket的同学都知道,Socket通讯中除了有自己的协议以外还要有心跳的命令,以此来保证客户端和服务器端连接的状态。这些本文都不去深究,主要还是说长连接的这个小框架。

代码是我们最好的伙伴,下面我们结合代码说说这个简单的东西。

Asp.Net4.0中加入了很多异步特性,其中IHttpAsyncHandler配合IAsyncResult可以很好的解决本文的需求。首先我们定义一个类实现IAsyncResult这个接口

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

using log4net;

namespace SM.BIZKeepAliveHttp
{
    /// <summary>
    /// 一个异步会话,会话会被临时缓存
    /// </summary>
    public class HKAsyncRequest : IAsyncResult
    {
        private static readonly ILog logger = LogManager.GetLogger(typeof(HKAsyncRequest));

        public HKAsyncRequest(HttpContext context, AsyncCallback cb, object extraData)
        {
            this.Context = context;
            this.CallBack = cb;
            this.ExtraData = extraData;
        }

        public HttpContext Context
        {
            get;
            set;
        }

        public object ExtraData
        {
            get;
            set;
        }

        public AsyncCallback CallBack
        {
            get;
            set;
        }

        public bool IsCompleted
        {
            get;
            set;
        }

        public object AsyncState
        {
            get;
            set;
        }

        public System.Threading.WaitHandle AsyncWaitHandle
        {
            get;
            set;
        }

        public bool CompletedSynchronously
        {
            get { return false; }
        }

        public void Send(string response) {
            if (String.IsNullOrEmpty(response))
                return;
            try
            {
                this.Context.Response.ContentType = "text/plain";
                this.Context.Response.Write(response);
                if (this.CallBack != null)
                {
                    this.CallBack(this);
                }
            }
            catch (Exception ex)
            {
                logger.Error("输出到客户端发生错误:" + ex.Message);
            }
            finally
            {
                IsCompleted = true;
            }
        }

        public void Send(byte[] b,int offset,int length){
            string str = Func.ByteArrayToHexString(b);
            this.Send(str);
        }

    }
}

这个类没有什么难的,主要是保存外部传进来的HttpContext、AsyncCallBack和ExtraData,HttpContext用来向Response中写回应,AsyncCallBack用来结束当前Http长连接请求,ExtraData自己该干什么干什么我没有用它。这里需要注意的是这个类中的CompletedSynchronously属性要返回false,不然客户端收不到数据。而且各个属性也别随便返回null,不然在写入Response时会报空指针的错误。

下面我们看看另一个接口的实现。在项目中新建一个一般处理程序(.ashx)文件。实现IAsyncHandler接口:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

using log4net;
using Newtonsoft.Json.Linq;

namespace SM.BIZKeepAliveHttp
{
    public class Data : IHttpAsyncHandler
    {

        public static readonly string DATAFIELD = "data";
        private static readonly ILog logger = LogManager.GetLogger(typeof(Data));

        public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)
        {
            context.Response.Cache.SetCacheability(HttpCacheability.NoCache);
            string value = context.Request.Params.Get(DATAFIELD);

            //这里传过来的是SessionId,不是数据,数据不做重复Parse
            //用sessionId去缓存中找对应的会话,并填充异步AsyncResult
            HKAsyncRequest result = new HKAsyncRequest(context, cb, extraData);
            string error = null;
            if (String.IsNullOrEmpty(value))
            {
                error = "500 SessionId is null";
                context.Response.StatusCode = 500;
                logger.Error(error);
                result.Send(error);
                return result;
            }

            List<AliveClient> acs = AsyncManager.Sessions.FindAll(x => x.SessionId.Equals(value));
            if (acs == null || acs.Count == 0)
            {
                error = "404 SessionId:" + value + " has no connection.";
                context.Response.StatusCode = 404;
                logger.Debug(error);
                result.Send(error);
                return result;
            }

            AliveClient ac = acs.First();
            ac.Result = result;
            //执行命令
            CommondFactory.ExecuteCommond(ac);

            return result;
        }

        public void EndProcessRequest(IAsyncResult result)
        {
        }

        public void ProcessRequest(HttpContext context)
        {
        }

        public bool IsReusable
        {
            get
            {
                return false;
            }
        }
    }
}

这个类中主要实现的方法只有一个,那就是BeginProcessRequest,其他方法不用写任何代码。这个方法主要作用是建立一个IAsyncResult实例后保存起来,便于以后服务器端有了数据或是满足了特定情况把数据返回给客户端。所以我在代码里面建立了一个静态List的缓存保存这些IAsyncResult实现。当然这就是Asp.Net实现Http长连接的核心所在了。
  其它的就不多说了,大家可以看源代码,看代码时大家会发现我实际建立了两个.ashx文件,这和我这个项目的逻辑有关,因为协议规定客户端发送一条数据后服务器端马上要做出回应,所以我用一个传统的ashx作回应,回应前这个传统的ashx(connection.ashx)先分析数据把分析后的数据模型保存起来,同时给客户端一个SessionId。客户端收到回应后用这个SessionId发起长连接请求,服务器端就不再重复分析数据了,而是将之前的数据从缓存中取出使用,为了调试方便我把这个SessionId写死了。同时我用Quartz.Net建立了两个任务,一个CleanJob.cs实际是作为清理任务,定时清理掉缓存中的无效或已完成请求5分钟跑一次。还有一个任务是HeartJob.cs主要是用来模拟服务器端推送的逻辑,30秒跑一次。

用到Quartz.Net是因为我个人认为在Asp.net中直接启动BackgroundWorker的方式不是很好,还是调度引擎的线程模型更可靠。具体启动调度引擎的代码在Global.asax里面。

附件中是我剥离出来的代码,删除了业务部分只做测试用。测试界面为index.aspx,在文本框中写点东西点提交,先收到服务器的回应后每个30秒收到服务器的回应弹出alert窗口。这里要提的就是客户端js代码在收到一个长连接反馈后马上又建立一个长连接,这是关键所在。

源代码

时间: 2024-10-17 12:57:52

Asp.Net实现Http长连接推送的相关文章

Erlang C1500K长连接推送服务-内存

上篇 Erlang C1500K长连接推送服务-性能 提到:150w连接,使用了23GB内存,每个连接占用15KB,约一半是内核使用. 大概分析一下: 1. Erlang 节点 12GB,内部因为有内存池碎片问题,实际使用9GB,也就是实际每个进程+Port占用越6K,因为采用hibernate策略,已经没水分了. 2. linux内核 11GB,通过运行前后, cat /proc/meminfo 中 MemTotal - AnonPages 值的增加量基本就是内核占用. 实际Slab: 538

Erlang C1500K长连接推送服务-性能

Whatsapp已经使用Erlang在生产环境跑到96GB内存单机 3M长连接,参加:WhatsApp的Erlang世界.毕竟业务级别能达到Whatsapp那样极少,现在只有千万级,单机太多挂一台影响太大,再者就是没有多线接入,每个机房都得扔那么几台机器吧,所以1M就能满足要求. Erlang 作为长连接网关有着天生的优势: - 擅长与IO密集型业务,也要求将网关设计尽量简单,认证完后,简单解析报头,就直接将请求转发给后端服务处理 - 网络层有beam c 实现得非常高效 ,erlang 代码只

Asp.NET MVC3 使用 SignalR 实现推送

一,简介 Signal 是微软支持的一个运行在 Dot NET 平台上的 html websocket 框架.它出现的主要目的是实现服务器主动推送(Push)消息到客户端页面,这样客户端就不必重新发送请求或使用轮询技术来获取消息. 二,实现机制 SignalR 的实现机制与 .NET WCF 或 Remoting 是相似的,都是使用远程代理来实现.在具体使用上,有两种不同目的的接口:PersistentConnection 和 Hubs,其中 PersistentConnection 是实现了长

Asp.NET MVC3 使用 SignalR 实现推送(接上)

一,Persistent Connection 示例教程 1,实现服务器端代码 1),编写服务器 PersistentConnection 代码 项目中 SignalR 目录下创建 PersistentConnection.cs 文件 using System; using System.Collections.Generic; using System.Threading.Tasks; using SignalR; namespace SignalTutorial.SignalR { publ

Asp.NET MVC 使用 SignalR 实现推送功能二(Hubs 在线聊天室 获取保存用户信息)

简单介绍 关于SignalR的简单实用 请参考 Asp.NET MVC 使用 SignalR 实现推送功能一(Hubs 在线聊天室) 在上一篇中,我们只是介绍了简单的消息推送,今天我们来修改一下,实现保存消息,历史消息和用户在线 由于,我这是在一个项目([无私分享:从入门到精通ASP.NET MVC]从0开始,一起搭框架.做项目 目录索引)的基础上做的,所以使用到的一些借口和数据表,不详细解析,只是介绍一下思路和实现方式,供大家参考 用户登录注册信息 当用户登录之后,我们注册一下用户的信息,我们

转:基于ASP.NET的Comet长连接技术解析

原文来自于: Comet技术原理 来自维基百科:Comet是一种用于web的技术,能使服务器能实时地将更新的信息传送到客户端,而无须客户端发出请求,目前有两种实现方式,长轮询和iframe流. 简单的说是一种基于现有Http协议基础上的长轮询技术,之所有会产生这种技术的主要原因是Http协议是无状态的所以客户端和服务端之间没办法建立起一套长时间的连接.比如我们要做一个聊天室,在Web环境下我们通常不能从服务端推送消息到浏览器里,而只能通过每个客户端不断的轮询服务器,以获取最新的消息,这样一来效率

AngularJS+ASP.NET MVC+SignalR实现消息推送

原文:http://www.mincoder.com/article/4565.shtml 背景 OA管理系统中,员工提交申请单,消息实时通知到相关人员及时进行审批,审批之后将结果推送给用户. 技术选择 最开始发现的是firebase,于是很兴奋的开始倒腾起来.firebase用 起来倒是简单:引用一个js即可,按官网上的教程很快便应用到了项目中.第二天打开项目发现推送功能不好使了,这是为何?最后发现firebase官网打 不开了...难道firebase被google收了也会被天朝给墙掉?也许

Asp.NET MVC 使用 SignalR 实现推送功能一(Hubs)

简介 ASP .NET SignalR 是一个ASP .NET 下的类库,可以在ASP .NET 的Web项目中实现实时通信.什么是实时通信的Web呢?就是让客户端(Web页面)和服务器端可以互相通知消息及调用方法,当然这是实时操作的. WebSockets是HTML5提供的新的API,可以在Web网页与服务器端间建立Socket连接,当WebSockets可用时(即浏览器支持Html5)SignalR使用WebSockets,当不支持时SignalR将使用其它技术来保证达到相同效果. Sign

asp.net 中长尾链接实现推送 -- comet

一般需求推送服务时,都会去第三方拿推送组件,如”极光“,”百度“,”小米"什么的,自己用.net实现推送服务端需要面对很多问题,比如C10K,但是企业内部使用往往用不了10K的链接,有个1K,2K就足够,这个时候完全可以自己实现一个推送服务,这样手机应用就不用走外网了. 使用.net实现推送服务有几个选择 1.是使用WCF 基于TCP的回调-针对.net To .net 端,经过7*24小时测试,2K左右的链接能稳定hold住,参考:http://www.cnblogs.com/wdfrog/p