利用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,再break出循环,
                 */
              temp++;       }
        }
    }

然后由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
            {          while (temp < i)                {                  if (!asyncResult.contex.Response.IsClientConnected)
                      throw new Exception();

                    Thread.Sleep(1000);     //这个类继承自IHttpAsyncHandler,是由线程池中取出一个线程来执行本类,所以这里让线程Sleep(1000)不会影响到UI线程
                    /*
                     *这里再查询数据库,得到数据后保存至变量resultStr,再break出循环,
                     */            temp++;
                }
            }
            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也不怕啦!

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

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

时间: 2024-10-21 11:23:12

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

利用jQuery Ajax技术实现每隔5秒向某页面传值

有时候我们需要每隔一段时间向某页面传值,比如说聊天室,每隔几秒就像数据库处理页面传值并取回,然后显示在聊天窗口.又或者是每隔一段时间就查询用户最后发言时间到现在是否间隔2分钟,如果是则将用户退出.这个时候我们就要用到HTML DOM setInterval() 方法. setInterval() 方法可按照指定的周期(以毫秒计)来调用函数或计算表达式. setInterval() 方法会不停地调用函数,直到 clearInterval() 被调用或窗口被关闭.由 setInterval() 返回

利用jquery.ajax在jsp页面动态生成table,可以增加修改,并支持一行和多行删除

声明:此为本人原创,只想实现功能,界面样式方面没多考虑,很粗糙能看懂就行--2018-5-14 动态生成table,我利用jsp内嵌java代码从后台获取对象集合,输出的时候有2中方法 1.直接利用java代码for(b1 b:bs)输出 2.利用JSTL标签库的c:foreach输出 不同之处在于,利用c:foreach输出要把获取的对象集合加入到request,然后用${}来读取,而for(b1 b:bs)可以直接输出. 第一种方法--for(b1 b:bs)输出<table id="

利用jquery.ajax()实现跨域

通过jQuery的ajax进行跨域,这其实是采用的jsonp的方式来实现的. jsonp是英文json with padding的缩写.它允许在服务器端生成script tags至返回至客户端,也就是动态生成javascript标签,通过javascript callback的形式实现数据读取. 前端代码如下: $.ajax({ type:"get", async:false, url:"http://192.168.0.168:8080/lightview/filecent

asp.net中利用Jquery+Ajax+Json实现无刷新分页(二)

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="PageTest.aspx.cs" Inherits="renmai_PageTest" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xh

asp.net如何在前台利用jquery Ajax调用后台方法

一 :最近因为帮同事开发项目使用到了asp.net,而我又想实现Ajax异步请求....从网上查询了一下资料之后,原来在asp.net中利用Ajax调用后台方法同样很简单,为了便于自己以后查看,特将此整理后记录如下 先贴上前台代码如下: 1 <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Index.aspx.cs" Inherits="aspnetAjax.In

js数组怎么利用jquery ajax怎么传到java后台,后台怎么接受啊。。

<p> 整整一个上午,我坐在秋末的冷峻的空气里,看着窗外灰色的天空.老旧的楼房,还有一株槐树绿意尚存.未曾落去的枝叶,任由大好的年华和时光在寂寞清冷的尘世中慢慢迷失和荒芜. 冬天很快就要到来了,站在眼前的这一株静默的槐树,仿佛对季节的变化:没有了感觉最后一线余光,脱落于夜.我知道这个无月的夜晚,天色更暗.我明白生命的大幕,启开之后,烦忧一幕接着一幕:而所有的快乐,只是幕启幕合.灯光由明转暗或由暗转明的过门,是短暂的响亮.即逝的华美. 花也向晚.一串念珠,静寂. 并不古老的建筑,衬着城之炫或者这

jquery ajax jsonp跨域调用实例代码

今天研究了AJAX使用JSONP进行跨域调用的方法,发现使用GET方式和POST方式都可以进行跨域调用,这里简单分享下,方便需要的朋友 客户端代码 复制代码 代码如下: <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="WebForm1.aspx.cs" Inherits="WebApp.WebForm1" %><!DOCTYPE html P

利用 jQuery UI 和 Ajax 创建可定制的 Web 界面

如今,网站比以往更具可定制性,允许用户更改其空间,根据自己的喜好对其进行个性化.个性化的主页或仪表板页面(例如 iGoogle.MyYahoo! 和 MyAOL)日渐普及,大多数 Web 应用程序内甚至也整合了类似的功能.jQuery 库简化了此类复杂 JavaScript 交互的编写,随着 jQuery UI 的引入,这项功能得到了进一步的简化,该库以易于访问的 jQuery 插件的形式提供了常用用户界面类型. 本文介绍了如何利用 Ajax 和 jQuery UI 创建具有各种定制功能的高度可

[开源] jQuery 插件,利用‘localStorage’ 对 jQuery AJAX进行缓存,优化页面ajax请求

jquery-ajax-cache 源码地址:https://github.com/WQTeam/jquery-ajax-cache jQuery插件——利用‘localStorage’ 和 ‘sessionStorage’ 对 jQuery AJAX 请求进行缓存. 首先说明下在什么场景下需要用到缓存ajax请求到localstorage中.都知道浏览器本身对http请求就是有缓存策略的,但是这种缓存方式两个缺陷:1.只能缓存get请求 2.同时缓存的设置都在后端响应的报文头部指定.(PS:现