ASP.NET Ajax 聊天应用程序






4.15/5 (22投票s)
使用 Ajax 和 LINQ to XML 的聊天室页面。
引言
我开发了一个简单的聊天 Web 应用程序,它使用 XML 文件来存储有关在线聊天者的信息,使用了 ASP.NET、C#、LINQ to XML、Ajax -Anthem Framework-。
技术
该应用程序包含一个类,该类通过一些方法来处理 XML 文件,以创建 XML 或加载它,保存信息、修改它或删除它。 该类的 public
方法是
void Join(string userName, DateTime dateTime)
void Say(string userName, string msg, DateTime dateTime)
void Leave(string userName, DateTime dateTime)
List< string > GetOnlineNames()
int GetNumberOfOnlines()
List< string > GetMessagesHistory()
客户端调用 (使用 Ajax 异步地) 这些方法 Join()
、Say()
、Leave()
。
服务器端的一个定时器每隔一段时间(在我们的应用程序中为 2 秒)触发一次,读取 XML(通过调用这些方法 GetOnlineNames()
、GetNumberOfOnlines()
、GetMessagesHistory()
)并刷新我们的控件(异步地)。
代码
从我们的 XHandle.cs 类开始,它就像一个聊天室,让我们看看它的成员
using System.Linq;
using System.Xml.Linq;
using System.Collections.Generic;
public class XHandle
{
private string _path;
private string _xPageName;
private string _FullPath;
private XDocument _xDoc;
private bool _xDocCreated;
private XElement _usersRoot;
private int _onlineUsers;
....
}
构造函数的工作是
了解 App_Data 文件夹的路径 (_path
) 和 XML 文件的名称 (_xPageName
),以设置我们稍后将使用的 _FullPath
。
检查 XML 文件是否已经创建,如果未创建,则创建它。
public XHandle(string _path)
{
this._path = _path;
this._xDocCreated = false;
this._xPageName = "Default";
this._FullPath = this._path + @"\" + this._xPageName + ".xml";
this._onlineUsers = 0;
//Check the XML page if already exists
LoadXPage();
//or create it if it doesnt
if (!_xDocCreated)
{
CreateXPage();
}
}
public XHandle(string _path, string ChatRoomName)
{
this._path = _path;
this._xDocCreated = false;
this._xPageName = ChatRoomName;
this._FullPath = this._path + @"\" + this._xPageName + ".xml";
//Check the XML page if already exists
LoadXPage();
//or create it if it doesnt
if (!_xDocCreated)
{
CreateXPage();
}
}
私有方法
LoadXPage()
和 CreateXPage()
是 private
方法,用于加载或创建 XML 文件并将 _xDocCreated
布尔值设置为 true
或 false
。
private void CreateXPage()
{
_xDoc = new XDocument();
XDeclaration dec = new XDeclaration("1.0", "utf-8", "yes");
_xDoc.Declaration = dec;
_usersRoot = new XElement("users");
_xDoc.Add(_usersRoot);
_xDoc.Save(_FullPath);
_xDocCreated = true;
}
private void LoadXPage()
{
try
{
_usersRoot = XElement.Load(_FullPath);
_xDocCreated = true;
}
catch
{
_xDocCreated = false;
}
}
创建 XML 文件将得到以下结果
公共方法
当用户加入聊天时,我们将他们的信息保存为姓名、消息和日期时间,当用户说些什么时,我们也将其信息保存为姓名、消息和日期时间,当用户离开时,我们只删除与其姓名对应的任何信息。
这是一个名为 Ahmed 的用户加入聊天时的 XML 文件
这是一个名为 Ahmed 的用户说 hello 时的 XML 文件
最后,这是用户离开时的 XML 文件
三个方法 Join(..)
、void Say(..)
、void Leave(..)
public void Say(string userName, string msg, DateTime dateTime)
{
XElement user = new XElement("user");
XElement elementName = new XElement("name", userName);
XElement elementLastMsg = new XElement("message", msg);
XElement elementDate = new XElement("date", dateTime.ToString());
user.Add(elementName);
user.Add(elementLastMsg);
user.Add(elementDate);
_usersRoot.Add(user);
_usersRoot.Save(_FullPath);
}
public void Join(string userName, DateTime dateTime)
{
string systemMsg = userName + " joined chat room.";
this.Say(userName, systemMsg, dateTime);
}
public void Leave(string userName, DateTime dateTime)
{
var user = from o in _usersRoot.Elements("user")
where (string)o.Element("name").Value == userName
select o;
user.Remove();
_usersRoot.Save(_FullPath);
}
其他的 public
方法是获取姓名、所有在线聊天者数量以及消息历史记录的方法。
public List< string> GetOnlineNames()
{
List< string> names = new List< string>();
var users = (from o in _usersRoot.Elements("user")
select o).Distinct();
foreach (var user in users)
{
if (!names.Contains(user.Element("name").Value))
{
names.Add(user.Element("name").Value);
}
}
_onlineUsers = names.Count;
return names;
}
public int GetNumberOfOnlines()
{
var users = (from o in _usersRoot.Elements("user")
select o).Distinct();
List< string> names = new List< string>();
foreach (var user in users)
{
//Filter the names to avoid duplicates
if (!names.Contains(user.Element("name").Value))
{
names.Add(user.Element("name").Value);
}
}
if (names.Count > 0)
{
_onlineUsers = names.Count;
return names.Count;
}
_onlineUsers = 0;
return 0;
}
public List< string> GetMessagesHistory()
{
List< string> messages = new List< string>();
var users = (from o in _usersRoot.Elements("user")
where o.Element("message").Value != string.Empty
orderby DateTime.Parse(o.Element("date").Value) ascending
select o).Distinct();
foreach (var user in users)
{
string fullString = user.Element("name").Value +
" : " + user.Element("message").Value;
if (!messages.Contains(fullString))
{
messages.Add(fullString);
}
}
return messages;
}
好了,现在我们完成了我们的类,我们有能力获取在线姓名、他们的数量和消息历史记录(这意味着如果有人在 x 时间开始聊天,而另一个人在 x 时间 30 分钟后加入聊天,则后者将获得自聊天开始以来的消息历史记录...)。
移动到 ChatRoom.aspx

在 Page_Load
事件中,我们将
- 注册页面到 Anthem Manager,以允许从客户端脚本调用它。
- 获取
XHandle
类的一个实例。 - 如果存在在线聊天者,则获取其数量。
- 设置一个固定时间(可能是 2 秒)作为计时器间隔。
- 订阅 Tick 事件。
- 将刷新控件方法附加到委托,以便我们可以异步调用它们。
public partial class ChatRoom : System.Web.UI.Page
{
XHandle xmll;
List< string> names;
List< string> msgs;
private delegate void AsyncCallingNames();
private delegate void AsyncCallingMessages();
AsyncCallingNames callerNames;
AsyncCallingMessages callerMessages;
protected void Page_Load(object sender, EventArgs e)
{
Anthem.Manager.Register(this);
xmll = new XHandle(Server.MapPath("App_Data"), "FirstChatRoom");
//Get chat room name from user or chat admin
int x = xmll.GetNumberOfOnlines();
if (Session["userName"] != null)
{
LabelError.Text = "Online, Users Online: " + x.ToString();
}
else
{
LabelError.Text = "Offline, Users Online: " + x.ToString();
}
Timer1.Interval = 2;
//I set it to 1 second, and it worked well
Timer1.Tick += new EventHandler(Timer1_Tick);
callerNames = new AsyncCallingNames(this.RefreshListNames);
callerMessages = new AsyncCallingMessages(this.RefreshMessages);
}
.....
}
现在我们有两项工作
- 加入、说和离开,这通过以下方式完成
- 加入按钮点击事件处理程序
- 发送按钮点击事件处理程序
- onunload 客户端脚本事件处理程序
protected void ButtonJoin_Click(object sender, EventArgs e) { if (Session["userName"] == null) { Session["userName"] = TextBoxName.Text.ToString(); xmll.Join(Session["userName"].ToString(), DateTime.Now); TextBoxName.Enabled = false; Timer1.StartTimer(); TextBoxType2.Focus(); } } protected void ButtonSend_Click(object sender, EventArgs e) { if (Session["userName"] != null) { string name = (string)Session["userName"]; string msg = TextBoxType2.Text.ToString(); xmll.Say(name, msg, DateTime.Now); TextBoxType2.Text = ""; TextBoxType2.Focus(); } else { LabelError.Text = "You have to join with a name first.."; } } [Anthem.Method] public void Leave() { Timer1.StopTimer(); if (Session["userName"] != null) { string name = (string)Session["userName"]; xmll.Leave(name, DateTime.Now); LabelError.Text = "Offline"; } }
- 在计时器 tick 事件处理程序中刷新我们的控件
- 聊天者姓名的
ListBox
- 消息的
ListBox
void Timer1_Tick(object sender, EventArgs e) { if (Session["userName"] != null) { IAsyncResult resultN = callerNames.BeginInvoke(null, null); if (!resultN.IsCompleted) { callerNames.EndInvoke(resultN); } IAsyncResult resultM = callerMessages.BeginInvoke(null, null); if (!resultM.IsCompleted) { callerMessages.EndInvoke(resultM); } TextBoxType2.Focus(); } else { Timer1.StopTimer(); TextBoxType2.Text = "You have to join with a name first.."; } } private void RefreshListNames() { ListBox1.Items.Clear(); names = xmll.GetOnlineNames(); foreach (var name in names) { ListBox1.Items.Add(name); } } private void RefreshMessages() { ListBox2.Items.Clear(); msgs = xmll.GetMessagesHistory(); foreach (var msg in msgs) { ListBox2.Items.Add(msg); } }
- 聊天者姓名的
Leave()
最后一件事是如何从客户端脚本调用 Leave()
方法
< body önunload="Leave(); return false;">
< script type="text/javascript">
function Leave()
{
Anthem_InvokePageMethod('Leave', null, null);
}
< /script>
报告
我将非常感谢您尝试此代码并反馈您可能遇到的任何问题,以使此应用程序更好。