使用 AJAX 在 ASP.NET 中实现的聊天应用程序(弹出窗口)






4.83/5 (26投票s)
使用 AJAX 和 SQL Server 在 ASP.NET 中实现的聊天应用程序。
引言
在这篇文章中,我想分享我在开发聊天应用程序方面的工作,该应用程序可以与您的常规 ASP.NET 应用程序一起使用。我正在使用 AJAX 和 SQL Server 作为后端。由于我使用了 UpdatePanel,因此只能在 ASP.NET 2.0 或更高版本中使用。
使用代码
该应用程序的工作方式如下
- 显示在线用户列表.
- 当前登录用户将单击用户名以打开一个新的聊天窗口,然后键入消息.
可以从后端数据库加载在线用户列表,并在用户登录到应用程序时记录一个条目。 用户名将显示为超链接,每个用户都有唯一的聊天 ID。
单击用户名时,将使用聊天 ID 作为参数调用 OpenChatBox() 函数。 当用户键入任何消息并单击“发送”按钮时,该消息将被发送到服务器并使用提供的聊天 ID 存储到数据库中。
function OpenChatBox(uid, chatid) {
    var win = window.open("ChatBox.aspx?uid=" + uid + "&cid=" + 
              chatid, "ChatWindow" + chatid + 
              document.getElementById('hidCurrentUser').value, 
              "status=0,toolbar=0, menubar=0, width=450, 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 中实现。
Private Sub SendMessage(ByVal msg As String)
    Dim con As SqlConnection = New SqlConnection( _
            "Data Source=.\SQLEXPRESS;AttachDbFilename=" & _ 
            "D:\Dev\Projects\WebChat\WebChat\App_Data\WebChat" & _ 
            ".mdf;Integrated Security=True;User Instance=True")
    con.Open()
    Dim comm As SqlCommand = New SqlCommand("stp_SendMessage", con)
    comm.CommandType = CommandType.StoredProcedure
    Dim paramSender As SqlParameter = New SqlParameter( _
        "@Sender", SqlDbType.VarChar, 10)
    paramSender.Value = HttpContext.Current.User.Identity.Name
    comm.Parameters.Add(paramSender)
    Dim paramRec As SqlParameter = New SqlParameter( _
        "@Reciever", SqlDbType.VarChar, 10)
    paramRec.Value = ViewState("uid")
    comm.Parameters.Add(paramRec)
    Dim paramMsg As SqlParameter = New SqlParameter( _
       "@Msg", SqlDbType.VarChar, 1000)
    paramMsg.Value = msg
    comm.Parameters.Add(paramMsg)
    'store the chat id supplied from browser
    Dim paramChatId As SqlParameter = New SqlParameter( _
     "@ChatId", SqlDbType.VarChar, 100)
    paramChatId.Value = ViewState("vwChatId").ToString()
    comm.Parameters.Add(paramChatId)
    comm.ExecuteNonQuery()
    con.Close()
End Sub

在每个用户的主页上,每 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 消息将调用 *GetChatMsg.aspx* 页面以获取新消息,下面显示的是 *GetChatMsg.aspx* 的代码
Imports System.Data
Imports System.Data.SqlClient
Partial Public Class GetChatMsg
    Inherits System.Web.UI.Page
    Protected Sub Page_Load(ByVal sender As Object, _
              ByVal e As System.EventArgs) Handles Me.Load
        Response.Clear()
        Response.ClearContent()
        'set response to not to cache, to avoid the ajax from displaying old message.
        Response.AddHeader("Cache-Control", "no-cache, must-revalidate")
        Dim ds As DataSet = GetMessage(HttpContext.Current.User.Identity.Name)
        Dim strMsg As String = ""
        If ds.Tables.Count > 0 Then
            If ds.Tables(0).Rows.Count > 0 Then
                For Each dr As DataRow In ds.Tables(0).Rows
                    If strMsg.Length > 0 Then
                        strMsg += "#"
                    End If
                    strMsg += dr("Sender").ToString() + "&"
                    strMsg += dr("ChatId").ToString() + "&"
                    strMsg += dr("Msg").ToString().Replace("&", _
                                 "_@AMP__").Replace("#", "_@HASH__")
                Next
            End If
        End If
        Response.Write(strMsg)
        Response.End()
    End Sub
    Private Function GetMessage(ByVal strUid As String) As DataSet
        Dim dsMsgs As DataSet = New DataSet()
        Dim con As SqlConnection = New SqlConnection( _
                "Data Source=.\SQLEXPRESS;AttachDbFilename=D:\Dev\" & _ 
                "Projects\WebChat\WebChat\App_Data\WebChat.mdf;" & _ 
                "Integrated Security=True;User Instance=True")
        con.Open()
        Dim comm As SqlCommand = New SqlCommand("stp_GetMessage", con)
        comm.CommandType = CommandType.StoredProcedure
        Dim paramSender As SqlParameter = New SqlParameter( _
            "@Uid", SqlDbType.VarChar, 10)
        paramSender.Value = HttpContext.Current.User.Identity.Name
        comm.Parameters.Add(paramSender)
        If (Request.QueryString("cid") <> Nothing) Then
            Dim paramChatId As SqlParameter = New SqlParameter( _
                             "@ChatId", SqlDbType.VarChar, 100)
            paramChatId.Value = Request.QueryString("cid")
            comm.Parameters.Add(paramChatId)
        End If
        Dim da As SqlDataAdapter = New SqlDataAdapter(comm)
        da.Fill(dsMsgs)
        con.Close()
        Return dsMsgs
    End Function
End Class
在浏览器端,在获取新消息时,如果聊天 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 != "" ) {
            //document.getElementById('txtMsg').value = str;
            //eval(str);
            var msgs = str.split('#');
            for (ind = 0; ind < msgs.length; ind++) {
                msgs[ind] = msgs[ind].replace("_@HASH__", "#");
                var msg = msgs[ind].split("&");
                msg[0] = msg[0].replace("_@AMP__", "&");
                msg[1] = msg[1].replace("_@AMP__", "&");
                msg[2] = msg[2].replace("_@AMP__", "&");            
                var blnExisting = false;
                for (iind = 0; iind < chats.length; iind++) {
                    try
                    {
                        if (chats[iind] != null && chats[iind].name == 
                            "ChatWindow" + msg[1] + 
                            document.getElementById('hidCurrentUser').value)
                        blnExisting = true;
                    }
                    catch(e){}
                }  
                if( blnExisting == false)
                    OpenChatBox(msg[0], msg[1]);
            }
        }
    }
}
收件人可以在同一窗口中使用消息回复。 弹出窗口具有屏幕 *ChatBox.aspx*,该屏幕具有 JavaScript 代码的 *ChatBox.js* 引用,以再次每秒 ping 服务器。 但是这次,它使用分配给该窗口的聊天 ID。 因此,每个聊天都有一个唯一的聊天 ID,并且聊天消息由聊天 ID 在后端维护,并且弹出窗口通过聊天 ID 进行通信。 此处使用了 UpdatePanel,以避免用户单击“发送消息”时聊天窗口闪烁。 并且当收件人收到消息时,该消息将在服务器上删除。
用户可以与任意数量的用户聊天,因为弹出窗口用于每个聊天,每个聊天都有唯一的聊天 ID。 聊天 ID 生成为“时间戳 + 发送者 ID + 接收者 ID”。
单击“发送”按钮后,键入的消息将由代码隐藏保存到 SQL Server 中的表中(此处,可以使用任何其他数据库)。 在接收者端,在 AJAX 调用中,消息读取器将读取与接收者相关的消息并发送给接收者。 请查看附加的 ASP.NET 代码的源代码,该代码适用于 Visual Studio 2008,但是相同的代码可以用于 VS 2005 (ASP.NET 2.0),而无需进行任何更改。

当第一个用户发送消息时,该消息将保存到后端的所选用户。 当在接收者上运行的应用程序(在示例中的 *default.aspx* 中实现)时,AJAX 用于每 5 秒 ping 一次服务器以查找消息。

增强功能
- 开发人员可以尝试实现保存聊天历史记录。
- 可以在用户控件中实现在线用户列表,使其可重用。
- 可以更改聊天窗口以包括消息时间和图像。


