zoukankan      html  css  js  c++  java
  • ASP.NET 实现 Comet 反向 Ajax

    概念:

               Comet 是基于 HTTP长连接的“服务器推”技术,是一种新的 Web 应用架构。基于这种架构开发的应用中,服务器端会主动以异步的方式向客户端程序推送数据,

    而不需要客户端显式的发出请求。Comet 架构非常适合事件驱动的 Web 应用,以及对交互性和实时性要求很强的应用,如股票交易行情分析、聊天室和 Web 版在线游戏等。

    //////////////////////////////////////////////////////////////////////////////

    演示示例: 1.分别在两个不同的浏览器打开Sender.aspx页面,分别输入不同的用户名登录。 2.输入收件人的名字和消息内容,点击发送按钮。

    //////////////////////////////////////////////////////////////////////////////

    好了,废话不多说了,我们开始建项目,首先建立一个文件夹重命名为“CSASPNETReverseAJAX”,打开VS 2012 >文件>新建>网站>ASP.NET 空网站,Web位置选择刚才创建的文件夹CSASPNETReverseAJAX


    1,添加一个Message.cs类,代码如下:

    /****************************** 模块标题 ******************************\
    * 模块名称:    Message.cs
    * 项目:        CSASPNETReverseAJAX
    * 消息类包含所有必需的字段在一个消息包。
    *
    \*****************************************************************************/

    namespace CSASPNETReverseAJAX {  
      /// <summary>    
      /// 这是一个实体类,代表一个信息项。    
      /// </summary>    
          public class Message     {        
           /// <summary>        
           /// 这个名字谁将接收这个消息。        
           /// </summary>        
               public string RecipientName { get; set; }

                  /// <summary>       
                  /// 消息内容。        
                 /// </summary>      
               public string MessageContent { get; set; }   
         }
    }

    2. 添加Client.cs类,Client.cs类代表一个客户端,可以从服务器接收消息。同时服务器可以发送消息给客户端。这个类包含两个私有成员和两个公共方法。
    代码如下:

    /****************************** 模块标题 ******************************\
    * 模块名称:    Client.cs
    * 项目:        CSASPNETReverseAJAX
    * 客户端类用于同步消息发送和接收的消息。
    * DequeueMessage方法被调用时,该方法将等到一个新消息
    *
    \*****************************************************************************/

    using System.Collections.Generic;
    using System.Threading;

    namespace CSASPNETReverseAJAX {    
    /// <summary>    
    /// 这个类代表一个web客户端可以接收消息。    
    /// </summary>     
         public class Client     {       
                  private ManualResetEvent messageEvent = new ManualResetEvent(false);        
                  private Queue<Message> messageQueue = new Queue<Message>();
                  /// <summary>        
                  /// //EnqueueMessage方法是专为发送方发送一条信息客户端。        
                  /// </summary>        
                  /// <param name="message">新消息</param>       
                 public void EnqueueMessage(Message message)       
                  {     
                   lock (messageQueue)           
                     {               
                       messageQueue.Enqueue(message);
                       // 设置一个新的消息事件。            
                       messageEvent.Set();        
                     }      
                 }
                 /// <summary>        
                 /// DequeueMessage方法是专为收件人接收消息。        
                 /// 如果没有消息,它会等待,直到一个新的消息插入。         
                 /// </summary>        
                 /// <returns>未读消息</returns>        
                 public Message DequeueMessage()        
                  {    
                    // 等到一个新消息。         
                    messageEvent.WaitOne();
                      lock (messageQueue)           
                       {
                         if (messageQueue.Count == 1)          
                            {
                               messageEvent.Reset();            
                            }                
                         return messageQueue.Dequeue();       
                       }        
                   }    
              }
         }

    3.添加 ClientAdapter.cs类,ClientAdapter.cs类是用于管理多个客户。表示层可以很容易地调用其方法来发送和接收消息。
    代码如下:

    /****************************** 模块标题 ******************************\
    * 模块名称:    ClientAdapter.cs
    * 项目:        CSASPNETReverseAJAX
    * ClientAdapter类管理多个客户端实例。 表示层
    * 调用它的方法来轻松地发送和接收消息。
    *
    \*****************************************************************************/

    using System.Collections.Generic;

    namespace CSASPNETReverseAJAX {    
    /// <summary>    
    /// 这个类是用来发送消息到多个客户端事件。    
    /// </summary>    
     public class ClientAdapter    
     {        
       /// <summary>        
       /// 收件人列表。        
       /// </summary>        
       private Dictionary<string, Client> recipients = new Dictionary<string,Client>();
            /// <summary>
            /// 发送一个消息到一个指定的收件人。
            /// </summary>
            public void SendMessage(Message message)
            {
                //判断如果收件人列表包含收件人就调用client.EnqueueMessage方法发送給他
                if (recipients.ContainsKey(message.RecipientName))
                {
                    Client client = recipients[message.RecipientName];

                    client.EnqueueMessage(message);
                }
            }

            /// <summary>
            /// 被一个收件人等和接收一个消息。
            /// </summary>
            /// <returns>消息内容</returns>
            public string GetMessage(string userName)
            {
                string messageContent = string.Empty;
                //判断收件人列表是否包userName
                if (recipients.ContainsKey(userName))
                {
                    Client client = recipients[userName];

                    messageContent = client.DequeueMessage().MessageContent;
                }

                return messageContent;
            }

            /// <summary>
            /// 加入一个用户到收件人列表。
            /// </summary>
            public void Join(string userName)
            {
                recipients[userName] = new Client();
            }

            /// <summary>
            /// 单例模式.
            /// 这种模式将确保只有一个这个类的实例的系统。
            /// </summary>
            public static ClientAdapter Instance = new ClientAdapter();
            private ClientAdapter(){ }
        }
    }

    4. 添加一个Web服务Dispatcher.asmxDispatcher.asmx 是web服务设计的调用Ajax来接收消息。
    Dispatcher.asmx.cs代码如下:

    /****************************** 模块标题 ******************************\
    * 模块名称:    Dispatcher.asmx.cs
    * 项目:        CSASPNETReverseAJAX
    * 这个web服务的设计被称为Ajax客户端。
    *
    \*****************************************************************************/

    using System.Web.Services;

    namespace CSASPNETReverseAJAX
    {
        /// <summary>
        /// 这个web服务包含的方法帮助事件分发给客户端。
        /// </summary>
        [WebService(Namespace = "http://tempuri.org/")]
        [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
        [System.ComponentModel.ToolboxItem(false)]
        [System.Web.Script.Services.ScriptService]
        public class Dispatcher : System.Web.Services.WebService
        {
            /// <summary>
            /// 调度新消息事件。
            /// </summary>
            /// <param name="userName">The loged in user name</param>
            /// <returns>消息内容</returns>
            [WebMethod]
            public string WaitMessage(string userName)
            {
                return ClientAdapter.Instance.GetMessage(userName);
            }
        }
    }
    5. 添加Sender.aspx页面,
    Sender.aspx前台页面代码如下:

    <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Sender.aspx.cs" Inherits="CSASPNETReverseAJAX.Sender" %>

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

    <html xmlns="http://www.w3.org/1999/xhtml">
    <head runat="server">
        <title></title>
    </head> <body>
        <form id="form1" runat="server">
        <!-- 登录 -->     <asp:Label ID="Label1" runat="server" ForeColor="Red" Text="请先登录:"></asp:Label><br />
        用户名<asp:TextBox ID="tbUserName" runat="server"></asp:TextBox>
        <asp:Button ID="btnLogin" runat="server" Text="登录" onclick="btnLogin_Click" />

        <!-- 接收信息 -->     <asp:ScriptManager ID="ScriptManager1" runat="server" AsyncPostBackTimeout="2147483647">
            <Services>
                <asp:ServiceReference Path="~/Dispatcher.asmx" />
            </Services>
        </asp:ScriptManager>
        <script type="text/javascript">

            // 这个方法将会持续一个http请求,等待消息。
            function waitEvent() {

                CSASPNETReverseAJAX.Dispatcher.WaitMessage("<%= Session["userName"] %>",
                function (result) {

                    displayMessage(result);

                    // 保持循环.
                    setTimeout(waitEvent, 0);
                }, function () {

                    // 保持循环.
                    setTimeout(waitEvent, 0);
                });
            }

            // 附加一个消息内容到结果面板.
            function displayMessage(message) {
                var panel = document.getElementById("<%= lbMessages.ClientID %>");

                panel.innerHTML += currentTime() + ": " + message + "<br />";
            }

            // 返回当前时间字符串.
            function currentTime() {
                var currentDate = new Date()
                return currentDate.getHours() + ":" + currentDate.getMinutes() + ":" + currentDate.getSeconds();
            }
        </script>

        <h3>消息:</h3>     <asp:Label ID="lbMessages" runat="server" ForeColor="Red"></asp:Label>     <br /><br />

        接收人姓名:<br />     <asp:TextBox ID="tbRecipientName" runat="server" Width="100px"></asp:TextBox><br />

        消息:<br />     <asp:TextBox ID="tbMessageContent" runat="server" Width="300px"></asp:TextBox><br />

        <asp:Button ID="btnSend" runat="server" Text="发送" onclick="btnSend_Click" />
            <br />
            <br /> <asp:Label ID="lbNotification" runat="server" ForeColor="Red"></asp:Label>
        </form>
      </body>
    </html>

    Sender.aspx.cs后台代码

    /****************************** 模块标题 ******************************\
    * 模块名称:    Sender.aspx.cs
    * 项目:        CSASPNETReverseAJAX
    *
    * 用户将使用这个页面送一条消息给一个特定的收件人。
    *
    *
    \*****************************************************************************/

    using System;

    namespace CSASPNETReverseAJAX {
        public partial class Sender : System.Web.UI.Page
        {
            //发送消息事件
            protected void btnSend_Click(object sender, EventArgs e)
            {
                // 创建一个消息实体包含所有必要的数据
                Message message = new Message();
                //从前台页面文本框获取姓名
                string Name = tbRecipientName.Text.Trim();
                //判断姓名不为空或不为null
                if (!string.IsNullOrEmpty(Name))
                {
                    //将姓名赋值給message.RecipientName
                    message.RecipientName = Name;
                    //从前台页面获取文本框消息内容并赋值給message.MessageContent
                    message.MessageContent = tbMessageContent.Text.Trim();
                    //判断消息接收人姓名不为Null或空白字符 且 消息内容不为空或不为null
                    if (!string.IsNullOrWhiteSpace(message.RecipientName) && !string.IsNullOrEmpty(message.MessageContent))
                    {
                        // 调用客户端适配器立即发送消息給特定收件人。
                        ClientAdapter.Instance.SendMessage(message);

                        // 用lable显示一个消息发送的当前时间.
                        lbNotification.Text += DateTime.Now.ToLongTimeString() + ": 消息已发送!<br/>";
                    }
                }
            }
            //登录事件
            protected void btnLogin_Click(object sender, EventArgs e)
            {
                //获取页面用户名文本框
                string userName = tbUserName.Text.Trim();

                // 判断用户名不为空或null
                if (!string.IsNullOrEmpty(userName))
                {
                    //加入一个用户名到收件人列表
                    ClientAdapter.Instance.Join(userName);
                    //用Session保存用户名
                    Session["userName"] = userName;
                }
                else
                {
                    //否则调用js弹窗提示“用户名不正确!”
                    ClientScript.RegisterStartupScript(this.GetType(), "msg", "alert('用户名不正确!')", true);
                }
            }

            protected void Page_PreRender(object sender, EventArgs e)
            {
                // 激活JavaScript等待循环。
                if (Session["userName"] != null)
                {
                    string userName = (string)Session["userName"];

                    //后台调用JavaScript方法waitEvent()开始等待循环。
                    ClientScript.RegisterStartupScript(this.GetType(), "ActivateWaitingLoop", "waitEvent();", true);
                    //前台lable显示正在等待新的消息
                    lbNotification.Text = string.Format("你的用户名是: <b>{0}</b>. 现在正在等待新的消息.", userName);

                    // 禁用登录。
                    tbUserName.Visible = false;
                    btnLogin.Visible = false;
                }
            }
        }
    }

    重新生成一下,没有报错的话我们就可以开始测试了。在IE浏览器打开Sender.aspx页面,打开HttpWatch开始监测浏览器, 输入用户名“张三” 点击登录,
    可以从HttpWatch看到已经开始运行js方法waitEvent()等待消息、,不要关闭页面,
    再从火狐浏览器打开Sender.aspx页面,输入用户名“李四” 点击登录,输入接收人姓名“张三”,输入消息“你好,我是李四!”,点击发送,
    我们再回到IE浏览器的Sender.aspx页面就可以收到“17:13:21: 你好,我是李四!”   
    基本上我们的示例到这里已经成功了!如有写的不好的地方欢迎大家指出。

  • 相关阅读:
    7月15日考试 题解(链表+状压DP+思维题)
    暑假集训日记
    C# .NET 使用 NPOI 生成 .xlsx 格式 Excel
    JavaSE 基础 第42节 局部内部类
    JavaSE 基础 第41节 匿名内部类
    JavaSE 基础 第40节 内部类概述
    JavaSE 基础 第39节 接口的应用
    JavaSE 基础 第38节 接口的实现
    JavaSE 基础 第37节 接口概述
    JavaSE 基础 第36节 抽象类概述与使用
  • 原文地址:https://www.cnblogs.com/Yashull/p/3010523.html
Copyright © 2011-2022 走看看