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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM