js+ashx实现Comet模型---服务器推

来源:互联网 发布:电子商务网店美工 编辑:程序博客网 时间:2024/05/16 13:03

 实现Comet模型:基于 HTTP 长连接的“服务器推”技术

 以笔者自己做过的一个实时监控项目举例:涉及Extjs+ashx异步轮询终端发送过来的消息

 

AJAX 的出现使得 JavaScript 可以调用 XMLHttpRequest 对象发出 HTTP 请求,JavaScript 响应处理函数根据服务器返回的信息对 HTML 页面的显示进行更新。使用 AJAX 实现“服务器推”与传统的 AJAX 应用不同之处在于:

  1. 服务器端会阻塞请求直到有数据传递或超时才返回。
  2. 客户端 JavaScript 响应处理函数会在处理完服务器返回的信息后,再次发出请求,重新建立连接。
  3. 当客户端处理接收的数据、重新建立连接时,服务器端可能有新的数据到达;这些信息会被服务器端保存直到客户端重新建立连接,客户端会一次把当前服务器端所有的信息取回。


图 2. 基于长轮询的服务器推模型
图 2. 基于长轮询的服务器推模型

一些应用及示例如 “Meebo”, “Pushlet Chat” 都采用了这种长轮询的方式。相对于“轮询”(poll),这种长轮询方式也可以称为“拉”(pull)。因为这种方案基于 AJAX,具有以下一些优点:请求异步发出;无须安装插件;IE、Mozilla FireFox 都支持 AJAX。

在这种长轮询方式下,客户端是在 XMLHttpRequest 的 readystate 为 4(即数据传输结束)时调用回调函数,进行信息处理。当 readystate 为 4 时,数据传输结束,连接已经关闭。Mozilla Firefox 提供了对 Streaming AJAX 的支持, 即 readystate 为 3 时(数据仍在传输中),客户端可以读取数据,从而无须关闭连接,就能读取处理服务器端返回的信息。IE 在 readystate 为 3 时,不能读取服务器返回的数据,目前 IE 不支持基于 Streaming AJAX。

 

 

function beginRequest()
    {
        Ext.Ajax.request(
  {
      url: GtsMap.Config.rootData + 'Svc/TraLisrSvc.ashx',
      scriptTag: true,
      method: 'post',
      params: { AckId: _ackId },
      timeout: 30000,
      disableCaching: true,
      scope: this,
      success: function(resp, opts)
      {
          //增加对服务器返回状态的判断:登录信息
          if (resp.status == 202)
          {
              Ext.Msg.show(
               {
                   closable: false,
                   icon: Ext.Msg.WARNING,
                   buttons: Ext.Msg.OK,
                   title: GtsMap.Lang.msg_title_warning,
                   msg: GtsMap.Lang.login_expired,
                   fn: function() { this.app.exit(); },
                   scope: this
               });
              _work = false;
              return;
          }

          if (resp.responseText.length > 0)
          {
              var evt = Ext.decode(resp.responseText);

              if (evt)
              {
//                  _ackId = evt.TraEventId;
                  this.recv(evt);
              }

              //              if (_work && (resp.tId == (_tid + 1))) {
              if (_work)
              {
                  //重新请求,实现 Http Comet
                  //                  _tid = _tid + 1;
                  beginRequest.defer(_CYCLE_INT, this);
              }
          }
      },
      failure: function(resp, opts)
      {
          if (_work)
          {
              //              _tid = resp.tId;
              beginRequest.defer(_CYCLE_ERR, this);
          }

          //增加对错误的处理
          //如果只是 超时,则重新开始监听
          //          if (resp.isTimeout) {
          //              if (_work) {
          //                  _sendTime = new Date().toLocaleTimeString();
          //                  beginRequest.defer(_CYCLE_ERR, this);
          //              }
          //          }
          //          else {
          //              Ext.Msg.show(
          //     {
          //         closable: false,
          //         icon: Ext.Msg.WARNING,
          //         buttons: Ext.Msg.OK,
          //         title: GtsMap.Lang.msg_title_error,
          //         msg: GtsMap.Lang.app_server_error,
          //         fn: function() { this.app.exit();},
          //         scope: this
          //     });
          //          }
      }
  });
    };

 

 

 

 

使用异步 HTTP 处理程序,您可以在启动一个外部进程(如对远程服务器的方法调用)的同时继续执行该处理程序。该处理程序可以继续运行,而不必等待外部进程完成。

在异步 HTTP 处理程序的处理过程中,ASP.NET 将通常用于外部进程的线程放回线程池中,直到处理程序接收到来自外部进程的回调。由于只能同时执行有限数量的线程,因此这样可以避免阻止线程并改善性能。如果许多用户都在请求依赖于外部进程的同步 HTTP 处理程序,那么操作系统可能很快就会用完所有线程,因为大量线程被阻止,正在等待外部进程。

Asp.net环境下实现Comet就要用到   IHttpAsyncHandler接口

实现其中的BeginProcessRequest和EndProcessRequest方法

BeginProcessRequest方法中的HttpContext包含了Response和Request对象,AsyncCallback用于异步调用结束,运行结束后需要返回一个IAsyncResult对象。

 

 

using System;
using System.Web;
using System.Threading;

class HelloWorldAsyncHandler : IHttpAsyncHandler
{
    public bool IsReusable { get { return false; } }

    public HelloWorldAsyncHandler()
    {
    }
    public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, Object extraData)
    {
        context.Response.Write("<p>Begin IsThreadPoolThread is " + Thread.CurrentThread.IsThreadPoolThread + "</p>\r\n");
        AsynchOperation asynch = new AsynchOperation(cb, context, extraData);
        asynch.StartAsyncWork();
        return asynch;
    }

    public void EndProcessRequest(IAsyncResult result)
    {
    }

    public void ProcessRequest(HttpContext context)
    {
        throw new InvalidOperationException();
    }
}

class AsynchOperation : IAsyncResult
{
    private bool _completed;
    private Object _state;
    private AsyncCallback _callback;
    private HttpContext _context;

    bool IAsyncResult.IsCompleted { get { return _completed; } }
    WaitHandle IAsyncResult.AsyncWaitHandle { get { return null; } }
    Object IAsyncResult.AsyncState { get { return _state; } }
    bool IAsyncResult.CompletedSynchronously { get { return false; } }

    public AsynchOperation(AsyncCallback callback, HttpContext context, Object state)
    {
        _callback = callback;
        _context = context;
        _state = state;
        _completed = false;
    }

    public void StartAsyncWork()
    {
        ThreadPool.QueueUserWorkItem(new WaitCallback(StartAsyncTask), null);
    }

    private void StartAsyncTask(Object workItemState)
    {

        _context.Response.Write("<p>Completion IsThreadPoolThread is " + Thread.CurrentThread.IsThreadPoolThread + "</p>\r\n");

        _context.Response.Write("Hello World from Async Handler!");
        _completed = true;
        _callback(this);
    }
}

 

 参考资料:http://msdn.microsoft.com/zh-cn/library/ms227433(v=VS.90).aspx

                     http://www.ibm.com/developerworks/cn/web/wa-lo-comet/

 

 

原创粉丝点击