65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 WCF 服务和 JSon 进行 ASP.NET 聊天

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.75/5 (30投票s)

2011 年 10 月 8 日

CPOL

5分钟阅读

viewsIcon

173904

downloadIcon

17420

使用 WCF 服务和 JSon 进行 ASP.NET 聊天

引言

本文是我上一篇文章“使用 AJAX(弹出窗口)在 ASP.NET 中创建聊天应用程序”的升级版本,该文章存在一些缺点,例如将聊天消息存储在数据库中,并且添加到网站中有点复杂。我实现了 WCF 服务代替数据库,以便更容易地将其添加到网站中。JSon 用于服务器和客户端之间的通信。

Using the Code

基本思想与之前的一样,但对其进行了修改以改进对多浏览器的支持。当我在 IE、Firefox、Chrome 这三个主要浏览器上运行该应用程序时,它运行正常。

如果您已经阅读了上一篇文章,可以跳过工作流程,直接查看配置部分

应用程序的工作方式如下

  • 在线用户列表将显示出来。

    在线用户列表由 WCF 服务OnlineUsers.svc维护,在用户登录应用程序时记录一条条目。用户名将显示为超链接,每个用户都有一个唯一的聊天 ID(GUID)。

  • 当前登录用户将点击用户名打开一个新的聊天窗口,然后输入消息。点击用户名时,会调用 OpenChatBox() 函数,并将聊天 ID 作为参数传递。当用户输入任何消息并点击“发送”按钮时,消息将发送到服务器并使用提供的聊天 ID 存储到数据库。
    var top = 100;
    var left = 100;
    function OpenChatBox(uid, chatid) 
    {    
        var hidUsr = $('input[id*="hidCurrentUser"]')[0];
        if (hidUsr.value == uid)
            return;
        var win;
        if (navigator.userAgent.toLowerCase().indexOf("chrome") != -1)
            win = window.open("Chat.aspx?uid=" + uid + "&cid=" + 
            chatid, "ChatWindow" + chatid.replace("-", "").replace
            ("-", "").replace("-", "").replace("-", ""), 
            "status=0,toolbar=0, menubar=0, width=480, height=620");
        else
            win = window.open("Chat.aspx?uid=" + uid + "&cid=" + 
            chatid, "ChatWindow" + chatid.replace("-", "").
            replace("-", "").replace("-", "").
            replace("-", ""), "status=0,toolbar=0, menubar=0, width=480, height=550");
            
        top = top + 50;
        if (top > screen.availHeight - 550)
            top = 100;
        left = left + 50;
        if (left > screen.availWidth - 450)
            left = 100;
    
        win.moveTo(left, top);
        chats[chats.length] = win;
        return false;
    }

    消息存储在 ASP.NET 中实现如下。我在这里使用名为 'ChatMsg' 的类(DataContract)来向聊天服务传递消息。

    private void SendMessage(string msg)
    {
        ChatServiceClient objChatService = new ChatServiceClient();
        ChatMsg cm = new ChatMsg();        
        cm.Msg = msg;
        cm.Sender = HttpContext.Current.User.Identity.Name;
        cm.Receiver = ViewState["uid"].ToString();
        cm.ChatId = ViewState["vwChatId"].ToString();
        cm.SubmittedDate = DateTime.Now;
        objChatService.SendMessage(cm);        
    }

    Chat_Flow.jpg

  • 在接收者的屏幕上,会弹出一个新窗口。

    在每个用户的首页上,每隔 5 秒运行一个脚本以 ping 服务器以检查是否有新消息。

    function PingServer() {
        PingServerForMsg()
        setTimeout(PingServer, 5000);
    }
    
    
    function PingServerForMsg() {
           // Use ajax to call the GetChatMsg.aspx to get any new messages.
    
            xmlHttp = GetXmlHttpObject(stateChangeHandler);
    }

    这个 JavaScript 消息将调用 GetChat.aspx 页面获取新消息。我在这里调用 WCF 服务“”,获取消息,并将消息以 JSon 格式发送到客户端。我使用JSon.Net 的 'JsonConvert' 类将消息列表序列化。您不需要为这个应用程序学习 JSon,因为下载中包含了 JSon ddl,无需额外下载。

    来自 GetChat.aspx.cs 的代码

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    using System.Data;
    using WCFChatProxy;
    using Newtonsoft.Json;
    
    public partial class GetChat : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            if (Request.QueryString["p"] != null)
            {
                if (Request.QueryString["p"] == "read")
                {
                    ChatServiceClient objChat = new ChatServiceClient();
                    objChat.MessageStatusToRead(Request.QueryString["msgId"]);
                }
            }
            else if (Request.QueryString["cid"] != null)     //get messages for a 
                            //single chat window
            {
                Response.Clear();
                Response.ClearContent();
                Response.AddHeader("Cache-Control", "no-cache, must-revalidate");
    
                ChatServiceClient objChatService = new ChatServiceClient();
                List<chatmsg> lstMsgs = objChatService.GetMessages
                    (Request.QueryString["cid"].ToString(),
                    HttpContext.Current.User.Identity.Name,
            ChatMsgStatus.Recent).ToList();
    
                string strMsg = JsonConvert.SerializeObject(lstMsgs);
    
                Response.Write(strMsg);
                Response.End();
            }
            else // get messages for the current logged in user
            {
                Response.Clear();
                Response.ClearContent();
                Response.AddHeader("Cache-Control", "no-cache, must-revalidate");
    
                ChatServiceClient objChatService = new ChatServiceClient();
                List<chatmsg> lstMsgs = objChatService.GetMessagesByUserId
                (HttpContext.Current.User.Identity.Name).ToList();
    
                string strMsg = JsonConvert.SerializeObject(lstMsgs);
    
                Response.Write(strMsg);
                Response.End();
            }
        }
    }

    在浏览器端,收到新消息时,如果聊天 ID 是新的,浏览器将打开一个弹出窗口,或者如果已经为与新消息相关的聊天 ID 打开了弹出窗口,则会显示该消息。在这里,与旧文章的一个额外变化是,收到消息后,浏览器会向服务器发送一个确认,服务器会将消息标记为已读。

    function stateChangeHandler() {
        //readyState of 4 or 'complete' represents that data has been returned 
        if (xmlHttp.readyState == 4 || xmlHttp.readyState == 'complete') {
            //Gather the results from the callback 
            var str = xmlHttp.responseText;
            if (str != "") {  
                var chatmsgs = eval(str);
                for (ind = 0; ind < chatmsgs.length; ind++) {
                    var senderId = chatmsgs[ind].Sender;
                    var chatId = chatmsgs[ind].ChatId;
                    var message = chatmsgs[ind].Msg;
                    var msgId = chatmsgs[ind].MsgId;
    
                    message = restoreHtmlTags(message);
                    document.getElementById('msgPanel').innerHTML += "
    " + senderId + ": " + message;
                    MessageReceived(msgId, msgCounter);
    
                    document.getElementById('msgPanel').scrollTop = 
                document.getElementById('msgPanel').scrollHeight;
                }
            }
        }
    } 
  • 用户可以通过当前窗口进行通信。

    接收者可以在同一个窗口中输入消息进行回复。弹出窗口的屏幕是 Chat.aspx,它引用了 Chat.js 文件,该文件包含每秒 ping 服务器的 JavaScript 代码。但这次,它使用分配给该窗口的聊天 ID。因此,每个聊天都有一个唯一的聊天 ID,聊天消息在后端由聊天 ID 维护,弹出窗口通过聊天 ID 进行通信。使用 UpdatePanel 来避免用户点击“发送消息”时聊天窗口闪烁。当接收者收到消息时,消息将在服务器上被删除。

    用户还可以输入 HTML 标签来发送不同字体和大小的消息。在这里,开发人员可以自定义应用程序,为最终用户提供通过选择字体和大小下拉列表来设置自己样式的选项。在下面的截图,我正在输入字体颜色和大小。

    Chat_fonts.jpg - Click to enlarge image

  • 一个用户可以同时与多个用户进行通信。

    用户可以与任意数量的用户聊天,因为每个聊天都使用弹出窗口,并且每个聊天都有一个唯一的聊天 ID。

    multi_browser.jpg - Click to enlarge image

在您的网站中配置步骤

  • 创建一个新网站或打开您想要添加聊天页面的 ASP.NET 项目。
  • 下载附带的 zip 文件,解压并将“ChatService”文件夹复制到本地驱动器。在 IIS 中配置一个虚拟目录/应用程序,名称任意(例如 'WCFChatService'),并使其在 .NET 4.0 下工作。检查 https:///<your_service_app_name>/WCFChat.svchttps:///<your_service_app_name>/OnlineUsers.svc 是否可以在浏览器中打开。您也可以使用 WCFTestClient 进行测试(http://msdn.microsoft.com/en-us/library/bb552364.aspx)。
  • 在您的网站中为两个服务添加服务引用。
  • 在您的网站中添加对 Newtonsoft.Json.dll 的引用。如果您不想从 JSon.Net 网站下载,请从 'ChatClient\Bin' 文件夹中获取 DLL。
  • 在您的网站的登录页面,成功登录后,添加以下代码以将用户添加到在线用户列表。
    OnlineUsersProxy.OnlineUsersServiceClient objOnlineUsers = 
            new OnlineUsersProxy.OnlineUsersServiceClient();
    Member mem = new Member();
    mem.UserId = txtUserID.Text;
    mem.DisplayName = GetDisplayNameById(mem.UserId); //write your own method
    objOnlineUsers.AddOnlineUser(mem);            
    
    //redirect home page
  • Chat.aspxGetChat.aspx 复制到您的网站文件夹,并将 Chat.jsChatWindow.js 文件复制到 Scripts 子文件夹。将下面的中继器控件的标签复制到您想要显示在线用户的主页上。
  • Online users:<br />
        <asp:Repeater ID="rptrOnlineUsers" runat="server">
             <ItemTemplate>                                        
              <span style="cursor:hand;text-decoration:underline;" 
        onclick="OpenChatBox('<%#((OnlineUsersProxy.Member)
        Container.DataItem).UserId%>','<%#((OnlineUsersProxy.Member)
        Container.DataItem).ChatId%>')"><%#((OnlineUsersProxy.Member)
        Container.DataItem).DisplayName%></span><br />
             </ItemTemplate>
        </asp:Repeater><span class="Apple-tab-span" style="white-space: pre; ">    
        </span>   
    <input type="hidden" id="hidCurrentUser" runat="server" /> 
  • Page_Load 中添加以下代码以将在线用户绑定到中继器。我在这里将当前登录用户的 ID 保存在隐藏字段中,该字段稍后将在聊天中使用。
    hidCurrentUser.Value = HttpContext.Current.User.Identity.Name;
    
    OnlineUsersProxy.OnlineUsersServiceClient wsOnlineUsers = 
            new OnlineUsersProxy.OnlineUsersServiceClient();
    rptrOnlineUsers.DataSource = wsOnlineUsers.GetOnlineUsers
                (HttpContext.Current.User.Identity.Name);
    rptrOnlineUsers.DataBind(); 
  • 运行您的项目进行测试..... :-)

关注点

我希望使用 WCF 和 JSon.Net 代替数据库会是一个更好的解决方案,在改进它的过程中,我也有机会修复了其他浏览器(Firefox 和 Chrome)的问题。

增强功能

  • WCF 服务将消息存储在内存中,我实现了一些方法来释放内存,通过删除旧消息。用户可以在注销时调用这些方法来删除已注销用户的消息。
  • 开发人员可以尝试实现聊天历史记录的保存。
  • 在线用户列表可以实现为一个用户控件,使其可重用,并显示用户的图像,因为它使用了中继器控件。
  • 我为聊天窗口使用了简单的设计,可以进行修改以包含消息时间和图像。
© . All rights reserved.