利用jquery Ajax和.Net IHttpAsyncHandler实现网站的即时提示

项目做完有一段时间了,一直想写个博客总结一下,之前也没写过有质量的博客.一是怕写出来被各位大牛笑话,二也是因为怕自己只了解了一点皮毛就发出来误导了别人,所以一直没怎么写过博客,但是看很多大牛都鼓励程序员写博客,一来可以回顾一下自己做的项目中的重点,二也可以发现很多自己以前没发现的问题.所以自己也试试写一下吧,一直没有总结的习惯,也想改改.文笔不好,经验欠缺,各位轻喷.

-----------------------------------------------------分割线-----------------------------------------------

因为项目的需要,主管要求我做一个登录后即时提醒的功能,即数据有变化的时候立即通知用户.然后我就开始百度,Google各种关键字搜索.最后知道有几种方式可以实现这种需求.即轮询和长连接.另外还有微软提供的一个开源的框架signalr(目前楼主本人就知道这些).

因为HTTP的无状态性,无连接性.导致web程序和服务器之间的数据传输只能是:浏览器向服务器发送一个请求,服务器再响应请求,然后返回要请求的数据.即浏览器和服务器的关系是请求--响应的关系,这种关系的好处就不说了(我也知道的不多 - -!),但是服务器却不能主动向浏览器发送数据,因为它是无状态的.那如果有这种需求了怎么办呢?聪明的人有很多,聪明人想出来解决的办法也挺多.前人栽树后人乘凉,咱们就先开始试试哪种方案最适合项目需求的.

1.signalr

园子里的已经有过介绍signalr的文章:SignalR 项目介绍 是张善友老师写的

我是通过在 Asp.NET MVC 中使用 SignalR 实现推送功能这篇文章了解到具体的使用方法,没有深入点的研究,它适用于做web即时聊天方面的.

楼主的项目则是要实现类似监视数据库的功能,所以不考虑这个方法,有兴趣的朋友可以去了解一下.

2.轮询

所谓轮询就是客户端不停的向服务器发送异步的请求,当发现数据库有变化时再通知浏览器做处理.这种方法实现起来简单,但是想想也知道,由于是不停的向服务器发送请求,对服务器来说是压力山大,要是同时打开的网页太多了话,有可能造成服务器崩溃.

3.长连接

前两种方法都不是LZ想要的,看来LZ就只能祭出那一招了:长连接.

楼主是百度GOOGLE党,就摘一段网友的话来解释长连接:客户端向服务器发送一个请求,服务器接收请求并hlod住这个连接,直到有数据或请求超时才返回客户端,客户端紧接着再发送一次请求,如此循环直到页面关闭,这也解释了为什么它叫长连接.比如这张图:

这张图的前两个请求超时我都设置为1分钟,返回后再立即发送一个请求.

好了,既然只剩下最后一招了,那就的把最后一招耍好,

首先是客户端要发送一个异步的请求:

/*客户端发出的异步请求*/
            function asyncRequest() {
                $.ajax({
                    type: "POST",
                    url: "asyncResult.asyn",
                    data: "time=60",    //请求的超时时间
                    success: function (data) {
                        if (data != "") {
                            /*执行操作,比如弹出提示*/
                        }
                        asyncRequest(); //得到服务器响应后继续发一个请求
                    },
                    error: function () {
                        asyncRequest(); //服务器抛出错误后继续发送一个请求
                    }
                });
            }

服务器接收这个异步请求的方法也要实现异步操作,要不然会阻塞正常的请求,所以要实现IHttpAsyncHandler这个接口,实现服务器的异步计算.

public class asyncResponse : IHttpAsyncHandler
    {
        public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)
        {
            myAsyncResult result = new myAsyncResult(context, cb, extraData);
            asyncRequestMgr.add(result);
            asyncRequestMgr.send();
            return result;
        }

        public void EndProcessRequest(IAsyncResult result)
        {
            asyncRequestMgr.resultStr = "";     //异步结束时清空结果
        }

        public bool IsReusable
        {
            get { return false; }
        }

        public void ProcessRequest(HttpContext context)
        {
        }
    }

asyncResponse类用来接收所有的异步请求,并交给静态类asyncRequestMgr来根据请求计算结果:

public static class asyncRequestMgr
    {
        public static string resultStr = "";
        private static myAsyncResult asyncResult;
        /// <summary>
        /// 把一个异步的请求对象保存到静态对象中供操作
        /// </summary>
        /// <param name="result"></param>
        public static void add(myAsyncResult result)
        {
            asyncResult = result;
        }
        /// <summary>
        /// 
        /// </summary>
        public static void send()
        {
            string time = asyncResult.contex.Request.Form["time"];
            getResult(time);
            asyncResult.send(resultStr);    //发送数据到客户端
        }
        /// <summary>
        /// 得到结果或返回空值
        /// </summary>
        private static void getResult(string time)
        {
            int i = int.Parse(time), temp = 0;
            while (temp < i)
            {
                Thread.Sleep(1000);     //这个类继承自IHttpAsyncHandler,是由线程池中取出一个线程来执行本类,所以这里让线程Sleep(1000)不会影响到UI线程
                /*
                 *这里再查询数据库,得到数据后保存至变量resultStr,
                 */
            }
        }
    }

然后由myAsyncResult类来发送结果:

 public class myAsyncResult : IAsyncResult
    {
        public HttpContext contex;
        public AsyncCallback cb;
        public object extraData;
        /// <summary>
        /// 初始化数据
        /// </summary>
        /// <param name="contex"></param>
        /// <param name="cb"></param>
        /// <param name="extraData"></param>
        public myAsyncResult(HttpContext contex, AsyncCallback cb, object extraData)
        {
            this.contex = contex;
            this.cb = cb;
            this.extraData = extraData;
        }
        /// <summary>
        /// 返回客户端请求的数据
        /// </summary>
        public void send(string resultStr)
        {
            this.contex.Response.Write(resultStr);
        }
    }

这样一个异步请求就算完成了,也实现了监视数据库的目的,但是如果客户不小心在后台查询数据库的时候按了刷新怎么办呢?这样建立起来的连接就会断开,而且由于我的前台是页面加载的时候开始异步请求,那一刷新一下又会再发送一次请求,而后台第一次的查询还在继续.这样后台就会有两次请求一起执行,一起查询数据库.再如果数据库的变化被第一次的请求查询到,但是第一次的请求因为客户刷新页面,连接已经断开,那用户也就不能得到数据变化的通知了.再再如果用户不小心无(手)意(贱)一直按着F5不放,那前台就会一直刷新一直请求,后台的N个请求同时查数据库.再再再如果有10个用户同时按F5不放,那就是10*N个请求同时查数据库,最后服务器只能不堪重负崩溃掉,如果这样怎么办呢?由于LZ平时MSDN看的少,确实苦恼了一阵子,最后突然发现HttpContext.Response有个属性:IsClientConnected,这个属性帮了大忙了,它返回一个BOOL值,表示当前请求是否在连接状态。有了这个属性就好办了,在getResult方法中加上判断,如果IsClientConnected==false的话,立即抛出一个异常,再把查询的结果保存到resultStr变量中,这样线程就不会继续执行下去.

修改后的getResult方法:

/// <summary>
        /// 得到结果或返回空值
        /// </summary>
        private static void getResult(string time)
        {
            int i = int.Parse(time), temp = 0;
            try
            {
                if (!asyncResult.contex.Response.IsClientConnected)
                    throw new Exception();
                while (temp < i)
                {
                    Thread.Sleep(1000);     //这个类继承自IHttpAsyncHandler,是由线程池中取出一个线程来执行本类,所以这里让线程Sleep(1000)不会影响到UI线程
                    /*
                     *这里再查询数据库,得到数据后保存至变量resultStr,
                     */
                }
            }
            catch (Exception)
            {
                /*这里把异常的线程中的结果保存至resultStr中*/
                throw;
            }
        }

然后在send方法执行前判断resultStr是不是空的,如果是空的就不用查询数据库,直接发送resultStr:

 /// <summary>
        /// 
        /// </summary>
        public static void send()
        {
            if (resultStr == "")
            {
                string time = asyncResult.contex.Request.Form["time"];
                getResult(time);
            }
            asyncResult.send(resultStr);    //发送数据到客户端
        }

这样无论按多久的F5,只要服务器判断哪个请求的连接状态为false就抛出异常,保持最多只让一个请求来查询数据库,现在就算再怎么无()意()按F5也不怕啦!

----------------------------------------分割线-------------------------------------

第一次发自认为是技术贴的帖子,如果大家觉得我哪里理解有误请及时指出来,避免误导他人.


<script type="text/javascript"> </script><script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"></script>
C#网页版+客户端版聊天软件源码分享(C#+长连接+Sqllite数据库实现) 今天我给大家分享一个聊天程序的源码。 网页版加客户端版并存,可以互通 我相信对大家学习和扩展这一块知识是很有用的。 我们先来看下软件结构 一个Web版一个网页版,而客户端是连接的网页的 http://localhost:53947/wwwroot/Lesktop 这个路径 http://localhost:53947/wwwroot/这一部分是网页的地址,大家可以根据自己配置情况进行修改 然后浏览一下Default.aspx页面如下 这是负面版的,客户端的也是一样的,我们先来注册 一个账户 在这里我们注册两个账户还有一个是text用来聊天对话使用 注册的方法是一样的我就不多说了。 下面登录第一个账户看看 这是登录后的效果。 单击聊天室 然后我们再登录另外一个账户text 好了大家应该能看到效果了吧。 然后咱们再发个“你好” 收到了吧,再回复一个 对就是这个效果, 再来看看桌面版的 刚才的消息都在 这是桌面版的效果。 大家感觉怎么样。 我感觉大家可以在这个基础之上进行扩展,最少可以看看他的实现思路 源码分享给大家了 sufeinet.com即时通信_云骞.zip (3.25 MB, 下载次数: 1078) ReceiveResponsesHandler 类,这个主要是用来接收和维护长连接的 实现长连接的两个重要来代码预览 [C#] 纯文本查看 复制代码 ? 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 using System; using System.Collections.Generic; using System.Collections; using System.Text; using System.Web; using System.Xml; using System.Threading; namespace Core { public class ReceiveResponsesHandler : IHttpAsyncHandler { public ReceiveResponsesHandler() { } HttpContext m_Context = null; IAsyncResult IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, Object extraData) { m_Context = context; string sessionId = context.Request.Params["SessionID"]; string clientVersion = context.Request.Params["ClientVersion"]; string serverVersion = context.Request.Params["ServerVersion"]; ResponsesListener asyncResult = new ResponsesListener(sessionId, cb, extraData); try { if (serverVersion != ServerImpl.Instance.Version) throw new IncompatibleException();
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值