ASP.NET WebForm(MVC)下实现消息推送(提供简单Demo下载)

由于项目需要,笔者最近需要实现Web客户端之间的消息的即时推送功能。

功能描述如下:

假设A,B,C用户登陆,内存记录下已登录的用户的信息,这时A在所在的客户端(SendInfo.aspx)页面向B发消息,则在B所在客户端页面(SendInfo.aspx)将弹出消息框。

关键点有两个:

1.保证客户端和服务端的连接

2.保证服务端能够向客户端广播消息

笔者是第一次做这样的实现,所以Google了一些资料,了解到可使用Comet,ajax轮询,WebSocket等技术实现,

由于时间关系,发现有些技术不是很容易理解,这里做了一个简单Demo.希望能够达到抛砖引玉的作用,与大家分享,共同提高。

笔者做了两个框架下的实现,ASP.NET Web Form和ASP.NET MVC 下的尝试。

ASP.NET Web Form版:

AsyncHandler.cs

View Code
using System;
using System.Collections.Generic;
using System.Web;
using System.Threading;

namespace CometSample
{
    public class WebIMAsyncHandler : IHttpAsyncHandler
    {
        #region IHttpAsyncHandler 成员

        public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)
        {
            string _UID = context.Request.Params["uid"];

            WebIMClientAsyncResult _AsyncResult = new WebIMClientAsyncResult(context, cb, extraData);

            string _Content = context.Request.Params["content"];
            string _Action = context.Request.Params["action"];

            if (_Action == "login")
            {
                _UID = context.Request.Params["uid"];
                _AsyncResult.LoginID = _UID;
                WebIMMessageHandler.Instance().Login(_UID, _AsyncResult);
            }
            else if (_Action == "logout")
            {
                _AsyncResult.LoginID = _UID;
                WebIMMessageHandler.Instance().Logout(_UID, _AsyncResult);
            }
            else if (_Action == "connect")
            {
                _AsyncResult.LoginID = _UID;
                WebIMMessageHandler.Instance().Connect(_AsyncResult);
            }
            else if (_Action == "getuserlist")
            {
                _AsyncResult.LoginID = _UID;
                WebIMMessageHandler.Instance().GetUserList(_AsyncResult);
            }
            //增加消息发送
            else if (_Action == "sendmsg")
            {

                _AsyncResult.LoginID = _UID;

                //WebIMMessageHandler.Instance().GetUserList(_AsyncResult);

                //调用
                WebIMMessageHandler.Instance().AddMessage(_Content, _AsyncResult);

            }

            //调用
            //WebIMMessageHandler.Instance().AddMessage(_Content, _AsyncResult);
            return _AsyncResult;
        }

        public void EndProcessRequest(IAsyncResult result)
        {

        }

        #endregion

        #region IHttpHandler 成员

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

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

        #endregion
    }

    public class WebIMClientAsyncResult : IAsyncResult
    {

        bool m_IsCompleted = false;
        private HttpContext m_Context;
        private AsyncCallback m_Callback;
        private object m_ExtraData;
        private string m_Content;
        private string m_LoginID = string.Empty;


        public WebIMClientAsyncResult(HttpContext p_Context, AsyncCallback p_Callback, object p_ExtraData)
        {
            this.m_Context = p_Context;
            this.m_Callback = p_Callback;
            this.m_ExtraData = p_ExtraData;
        }
        /// <summary>
        /// 用户编号
        /// </summary>
        public string LoginID
        {
            get
            {
                return m_LoginID;
            }
            set
            {
                m_LoginID = value;
            }
        }
        /// <summary>
        /// 发送消息的内容,暂时未使用到
        /// </summary>
        public string Content
        {
            get
            {
                return m_Content;
            }
            set
            {
                m_Content = value;
            }
        }
        #region IAsyncResult 成员

        public object AsyncState
        {
            get { return null; }
        }

        public WaitHandle AsyncWaitHandle
        {
            get { return null; }
        }

        public bool CompletedSynchronously
        {
            get { return false; }
        }

        public bool IsCompleted
        {
            get { return m_IsCompleted; }
        }

        #endregion

        /// <summary>
        /// 向客户端响应消息
        /// </summary>
        /// <param name="data"></param>
        public void Send(object data)
        {
            try
            {
                m_Context.Response.Write(this.Content);
                if (m_Callback != null)
                {
                    m_Callback(this);
                }
                
            }
            catch { }
            finally 
            {
                m_IsCompleted = true; 
            }
        }
    }
}

MessageHandler.cs

View Code
using System;
using System.Collections;
using System.Collections.Generic;
using System.Web;
using System.Text;

namespace CometSample
{
    public class WebIMMessageHandler
    {
        private static readonly WebIMMessageHandler m_Instance = new WebIMMessageHandler();
        
        //记录所有请求的客户端
        List<WebIMClientAsyncResult> m_Clients = new List<WebIMClientAsyncResult>();
        //Dictionary<string,WebIMClientAsyncResult> m_Clients=new Dictionary<string,WebIMClientAsyncResult>();
        string m_Users = string.Empty;
        public WebIMMessageHandler()
        {

        }

        public static WebIMMessageHandler Instance()
        {
            return m_Instance;
        }

        /// <summary>
        /// 登录
        /// </summary>
        /// <param name="p_LoginID"></param>
        /// <param name="p_ClientAsyncResult"></param>
        public void Login(string p_LoginID, WebIMClientAsyncResult p_ClientAsyncResult)
        {
            bool _Logined = false;
            foreach (WebIMClientAsyncResult _Item in m_Clients)
            {
                if (_Item.LoginID == p_LoginID)
                {
                    p_ClientAsyncResult.Content = "你已登录";
                    _Logined = true;
                    break;
                }
            }
            if (!_Logined)
            {
                //m_Clients.Add(p_ClientAsyncResult);
                p_ClientAsyncResult.Content = "OK";
            }
            p_ClientAsyncResult.Send(null);

        }

        private string GetUsers()
        {
            /*
            string _Users = string.Empty;
            foreach (WebIMClientAsyncResult _Item in m_Clients)
            {
                _Users += _Item.LoginID + ",";
            }
            return _Users;
             */

            var sbUsers = new StringBuilder();
            sbUsers.Append("Users:");

            foreach (WebIMClientAsyncResult _Item in m_Clients)
            {
                sbUsers.Append(_Item.LoginID);
                sbUsers.Append(",");
            }

            return sbUsers.ToString();
        }

        public void Logout(string p_LoginID, WebIMClientAsyncResult p_ClientAsyncResult)
        {
            foreach (WebIMClientAsyncResult _Item in m_Clients)
            {
                if (_Item.LoginID == p_LoginID)
                {
                    m_Clients.Remove(_Item);
                    break;
                }
            }
            p_ClientAsyncResult.Content = "退出成功";
            p_ClientAsyncResult.Send(null);
            //UpdateUserList();

            string _Users = GetUsers();
            foreach (WebIMClientAsyncResult _Item in m_Clients)
            {
                _Item.Content = _Users;
                _Item.Send(null);
            }
            m_Clients.Clear();
        }

        public void GetUserList(WebIMClientAsyncResult p_ClientAsyncResult)
        {
            Connect(p_ClientAsyncResult);
            string _Users = GetUsers();
            foreach (WebIMClientAsyncResult _Item in m_Clients)
            {
                _Item.Content = _Users;
                _Item.Send(null);
            }
            m_Clients.Clear();
        }

        public void Connect(WebIMClientAsyncResult p_Client)
        {
            bool _Exists = false;
            foreach (WebIMClientAsyncResult _Item in m_Clients)
            {
                if (_Item.LoginID == p_Client.LoginID)
                {
                    _Exists = true;
                    break;
                }
            }
            if (!_Exists)
            {
                m_Clients.Add(p_Client);
            }
        }

        /*
        public void UpdateUserList()
        {
            string _Users = GetUsers();
            foreach (WebIMClientAsyncResult result in m_Clients)
            {
                result.Content = m_Users;
                result.Send(null);
            }
            m_Clients.Clear();
        }*/

        /// <summary>
        /// 广播消息
        /// </summary>
        /// <param name="p_Message"></param>
        /// <param name="p_AsyncResult"></param>
        public void AddMessage(string p_Message, WebIMClientAsyncResult p_ClientAsyncResult)
        {
            //保持连接
            if (p_Message == "-1")
            {
                m_Clients.Add(p_ClientAsyncResult);
            }
            else
            {
                //将当前请求的内容输出到客户端
                p_ClientAsyncResult.Content = p_Message;
                p_ClientAsyncResult.Send(null);

                //否则将遍历所有已缓存的client,并将当前内容输出到客户端
                foreach (WebIMClientAsyncResult result in m_Clients)
                {
                    //发送给所有已经登录用户
                    var strMsg = string.Format("{0}{1}{2}{3}{4}",p_ClientAsyncResult.LoginID,"发送给",result.LoginID,"的消息:",p_Message);
                    //result.Content = p_Message;
                    result.Content = strMsg;  
                    result.Send(null);

                    //发送给指定用户
                    /*
                    if (string.Equals(result.LoginID, "ZhangShan") && !string.Equals(p_ClientAsyncResult.LoginID, "ZhangShan")) 
                    {
                        var strMsg = string.Format("{0}{1}{2}{3}{4}{5}","Msgs:", p_ClientAsyncResult.LoginID, "发送给", result.LoginID, "的消息:", p_Message);
                        //result.Content = p_Message;
                        result.Content = strMsg;
                        result.Send(null);
                    }
                     */
                }

                //清空所有缓存
                m_Clients.Clear();
            }
        }

    }
}

Login.js

View Code
/// <reference path="jquery-1.3.2.min.js" >
$(document).ready(function () {

    //登录,登录成功后,获取在线用户列表,
    function login() {

        //增加页面跳转
        var strUrl = '/SendInfo.aspx?strUid=' + $("#txtLoginID").val();
        window.open(strUrl);
    }

    $("#btnLogin").click(function () { if ($("#txtLoginID").val() == '') alert('空'); login(); });


})

WebIM.js

View Code
/// <reference path="jquery-1.3.2.min.js" >
//$(document).ready(function () {

    //状态,代表是否登录
    //var _logined = false;

    //登录,登录成功后,获取在线用户列表,
    function login() {
        //$.post("comet_broadcast.asyn", { action: 'login', uid: $("#txtLoginID").val() },
        $.post("comet_broadcast.asyn", { action: 'login', uid: strUid },
        function (data, status) {
            if (data == "OK") {
                _logined = true;
                getuserlist();

                //增加页面跳转
                /*var strUrl = '\SendInfo.aspx?strUid=' + $("#txtLoginID").val();
                window.open(strUrl);
                */
            }
            else {
                alert(data);
            }
        });
    }

    //获取在线用户列表,获取列表后,进入消息等待
    function getuserlist() {
        //$.post("comet_broadcast.asyn", { action: 'getuserlist', uid: $("#txtLoginID").val() },
        $.post("comet_broadcast.asyn", { action: 'getuserlist', uid: strUid },
        function (data, status) {
            //alert('getuserlist' + data);
            var result = $("#divResult");
            result.html(result.html() + "<br/>" + "用户列表:" + data);

            wait();
        });
    }

    //退出
    function logout() {
        //$.post("comet_broadcast.asyn", { action: 'logout', uid: $("#txtLoginID").val() },
        $.post("comet_broadcast.asyn", { action: 'logout', uid: strUid },
        function (data, status) {
            _logined = false;
            alert(data);
        }
         );
    }

    //消息等待,接收到消息后显示,发起下一次的消息等待
    function wait() {
        //$.post("comet_broadcast.asyn", { action: 'connect', uid: $("#txtLoginID").val() },
        $.post("comet_broadcast.asyn", { action: 'connect', uid: strUid },
           function (data, status) {

               /*
               var result = $("#divResult");
               result.html(result.html() + "<br/>" + "用户列表:" + data);
               */

               //2.窗口
               new Ext.ux.ToastWindow({
                   title: '提示窗口',
                   html: data,
                   iconCls: 'error'
               }).show(document);

               //服务器返回消息,再次建立连接
               if (_logined) {
                   wait();
               }

           }, "html");
    }

    /***********
    *********************消息发送部分***************************
    ************/

    function send() {

        //$.post("comet_broadcast.asyn", { action: 'sendmsg', uid: $("#txtLoginID").val(), content: $("#content").val() },
        $.post("comet_broadcast.asyn", { action: 'sendmsg', uid: strUid, content: $("#content").val() },
        function (data, status) {

            /*
            var result = $("#divResult");
            result.html(result.html() + "<br/>" + "已发消息:" + data);
            */

            //发送方页面提示
            //潜规则:暂时不处理
            /*
            //2.窗口
            new Ext.ux.ToastWindow({
                title: '提示窗口',
                html: data,
                iconCls: 'error'
            }).show(document);
            */

        }, "html"
        );

        //向comet_broadcast.asyn发送请求,消息体为文本框content中的内容,请求接收类为AsnyHandler
        //$.post("comet_broadcast.asyn", { content: $("#content").val() });

        //清空内容
        $("#content").val("");
    };

    /**
    * 获取字符串中某个特殊字符首次出现的位置之前的子串
    */
    function GetSubStrBySpecChar(strConnStr,strSplit){
    
    var arrStr = strConnStr.split(strSplit);
    var strSubStr = arrStr[0];
    
    return strSubStr;

    }

Default.aspx

View Code
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="CometSample._Default" %>

<!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>

    <script src="Scripts/jquery-1.3.2.min.js" type="text/javascript"></script>
    <script src="Scripts/Login.js" type="text/javascript"></script>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:Label ID="Label1" runat="server" Text="帐号"></asp:Label>
        <input id="txtLoginID" type="text" />
        <input id="btnLogin" type="button" value="Login" />
        <br />
        <div id="divUserList">
        </div>
    </div>
    </form>
</body>
</html>

SendInfo.aspx

View Code
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="SendInfo.aspx.cs" Inherits="CometSample.SendInfo" %>

<!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>SendInfo</title>
    <script src="Scripts/jquery-1.3.2.min.js" type="text/javascript"></script>
    <script src="Scripts/WebIM.js" type="text/javascript"></script>

    <link href="Scripts/ext-3.4.0/resources/css/ext-all.css" rel="stylesheet" type="text/css" />
    <script src="Scripts/ext-3.4.0/adapter/ext/ext-base.js" type="text/javascript"></script>
    <script src="Scripts/ext-3.4.0/ext-all.js" type="text/javascript"></script>
    <script src="Scripts/ToastWindow.js" type="text/javascript"></script>


    <script language="javascript" type="text/javascript">

        var strUid = "<%=strUid %>";

        $(document).ready(function () {

            //状态,代表是否登录
            var _logined = false;

            //alert(strUid);
            //getuserlist_send();
            login();

            $("#btnSend").click(function () { send(); })

            $("#content").keypress(function (e) {

                var keyCode = null;

                if (e.which)
                    keyCode = e.which;
                else if (e.keyCode)
                    keyCode = e.keyCode;

                if (keyCode == 13) {

                    send();

                    return false;
                }

                return true;
            });
        })
               
    </script>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <div id="divUserList">
        </div>
        <br />
        广播内容:
        <input type="text" id="content" /><br />
        消息记录:
        <div id="divResult">
        </div>
        <input type="button" id="btnSend" value="广播" />
    </div>
    <div>
        <input id="btnLogout" type="button" value="注销" onclick="logout();"/></div>
    </form>
</body>
</html>

最后还需要关注的是配置文件中的路径

在web.config 文件的system.web之间加上

<httpHandlers>
            <add path="comet_broadcast.asyn" type="CometSample.WebIMAsyncHandler" verb="POST,GET"/>
        </httpHandlers>

 

好了运行程序,

效果如下:

登陆之后,跳转到sendinfo页面

 

笔者打开连个浏览器,模拟两个客户端登陆,并且模拟广播消息(能够广播,那么向指定客户端发消息也就很容易了)

我们可以看到在页面右下角,有消息弹出

在.NET WebForm下笔者实现了客户端之间即时消息的推送,

但是在.NET MVC 2 下遇到了一些问题,因为mvc框架下对 ,NET WebForm中某些东西不支持。

各位看官,若有在MVC2下的实现,多多交流和分享哈! 

源码下载

Demo1

Demo2

 

posted @ 2013-03-12 08:23  楠木大叔  阅读(7031)  评论(26编辑  收藏  举报