聊天室在网上很常见,它给人们提供了在线聊天的机会。聊天室在功能上类似论坛,但是论坛上的信息可以保存较长的时间,而聊天室主要用于在线聊天,所以对存储信息方面的要求不高,另外还要经常删除一些信息,避免数据库存储的内容过多,影响显示的速度。聊天室主要提供三种功能:一是现实自己可以看到的信息,这包括针对所有人的聊天信息、自己发的信息以及发给自己的信息;二是要能够看到在线的人员名单,从而从中选择聊天的对象;三是输入聊天信息。
1 .方案分析
Ajax 的核心是 JavaScript XmlHttpRequest 对象。该对象在 Internet Explorer 5 中首次引入,它是一种支持异步请求的技术。简而言之, XmlHttpRequest 对象可以使用 JavaScript 向服务器提出请求并处理响应,而不阻塞用户。
在创建 Web 站点时,在客户端执行屏幕更新为用户提供了很大地灵活性。下面是 Ajax 可以完成的功能:
l 动态检测网页中的数据,无需页面重复加载。例如,动态检测网络考试系统倒记时时间,无需页面重复加载,并等待服务器重新发送整个页面。
l 提升站点的性能,这是通过减少从服务器下载的数据量而实现的。例如,在电子商城网的购物车页面,当更新购物车中的一项物品的数量时,通常会重新载入整个页面。如果使用 Ajax 计算新物品的总数量,服务器只会返回新物品总数量,而无须重新载入整个页面。
传统的聊天室基于客户端网页的自动刷新技术而实现,它的主要缺点是不断刷新页面造成屏幕的闪动,而经过了 Ajax 改造后的聊天室,每次只获取最新的发言信息,并将获取结果动态写入页面,不会有以上的缺点。
2 .实施过程
在制作基于 Ajax 的无刷新聊天室时,通过一个登录页面进入聊天室的主页面。在登录页面中输入用户名和密码进入聊天室的主页面 ChatRoom.aspx 中, 。
程序实现具体步骤:
( 1 )新建一个网站 ,将首页命名为 Login.aspx 。
( 2 ) 再新建一个在页面,命名为 ChatRoom.aspx 。其中添加 3 个 DropDownList 控件,1 个CheckBox 控件,1 个TextBox 控件和1 个Button 控件。 3 个 DropDownList 控件分别用来设置发送聊天信息的颜色、表情和聊天的对象。CheckBox 控件用来设置是否和选中的聊天对象进行私聊,TextBox 控件用来输入聊天信息,Button 控件用来提交聊天信息。
( 3 )本例中用到的存储过程如下。
存储过程 GetNewMsg 用于获取聊天信息。
CREATE PROCEDURE GetNewMsg @username varchar(50) AS --获取用户已经列出的消息id最大值 DECLARE @last int SET @last = ( SELECT lastchatinfo FROM UserInfo WHERE username = @username ) --获取最新的消息列表 SELECT id, user_from, user_to, content, expression, color, ispublic, sendtime FROM ChatInfo WHERE ( id > @last ) AND ( ( ispublic = 1 ) OR ( user_to = @username ) OR ( user_from = @username ) OR ( user_to = '大家' ) ) AND ( sendtime > GETDATE() - '00:05:00' ) ORDER BY sendtime DESC GO 存储过程GetOnlineUsers用于获取当前的在线人数。 CREATE PROCEDURE GetOnlineUsers AS --选取所有在线的用户 SELECT id, username FROM UserInfo WHERE isonline = 1 GO
存储过程 SendMsg 用于发送聊天信息。
CREATE PROCEDURE SendMsg @user_from varchar(50), @user_to varchar(50), @content varchar(255), @expression varchar(50), @color varchar(50), @ispublic bit AS --发送消息 INSERT INTO ChatInfo (user_from, user_to, content, expression, color, ispublic, sendtime) VALUES ( @user_from, @user_to, @content, @expression, @color, @ispublic, GETDATE() ) GO
存储过程 SetMsgPos 用于记录已经阅读过的消息 ID 。
CREATE PROCEDURE SetMsgPos @username varchar(50) AS --记录已经阅读过的消息id UPDATE UserInfo SET lastchatinfo = ( SELECT MAX(id) FROM ChatInfo ) WHERE username = @username GO 存储过程UserLogin用于判断登录的各个信息。 CREATE PROCEDURE UserLogin @username varchar(50), @password varchar(50) AS --用户名密码正确 IF EXISTS ( SELECT id FROM UserInfo WHERE username = @username AND password = @password ) BEGIN UPDATE UserInfo SET isonline = 1, lastchatinfo = ( SELECT ISNULL(MAX(id), 0) FROM ChatInfo ) WHERE username = @username AND password = @password --发布公告 INSERT INTO ChatInfo (user_from, user_to, content, expression, color, ispublic, sendtime) VALUES ( '', '', '【聊天室公告】:欢迎' + @username + '来到聊天室!', '', 'ff0000', 1, GETDATE() ) RETURN 0 END --用户名存在,密码不正确 IF EXISTS ( SELECT id FROM UserInfo WHERE username = @username ) RETURN 1 --用户名不存在,则根据输入创建新用户 INSERT INTO UserInfo (username, password, isonline) VALUES ( @username, @password, 1 ) UPDATE UserInfo SET lastchatinfo = ( SELECT ISNULL(MAX(id), 0) FROM ChatInfo ) WHERE username = @username AND password = @password --发布公告 INSERT INTO ChatInfo (user_from, user_to, content, expression, color, ispublic, sendtime) VALUES ( '', '', '【聊天室公告】:欢迎新人' + @username + '来到聊天室!', '', 'ff0000', 1, GETDATE() ) RETURN 2 GO
存储过程 UserLogout 用于退出聊天室。
CREATE PROCEDURE UserLogout @username varchar(50) AS UPDATE UserInfo SET isonline = 0, lastchatinfo = ( SELECT ISNULL(MAX(id), 0) FROM ChatInfo ) WHERE username = @username --发布公告 INSERT INTO ChatInfo (user_from, user_to, content, expression, color, ispublic, sendtime) VALUES ( '', '', '【聊天室公告】:' + @username + '已经离开聊天室!', '', 'ff0000', 1, GETDATE() ) GO
( 4 )程序主要代码如下。
下面是 ChatRoom.aspx 页面中通过 JavaScript 创建的几个函数,这些函数通过调用服务器端的方法来实现指定的功能,具体代码如下。
当用户单击【发送】按钮时会出发 send 方法,首先读取当前页面中用户输入的各项信息,如:用于输入聊天内容文本框中的信息、聊天的对象、字体的颜色、用户使用的表情和是否选择密谈。然后调用服务器端的 SendMsg 方法,并将读取的这些信息以参数的形式传递过去。同时,显示聊天的内容。这一步是通过调用服务器端的 GetNewMsg String 方法实现的,具体代码如下。
function send() { var txtContent = document.all("content").value; //文本框输入内容 if (txtContent == "") return; var user_to = document.all("userlist").value; //聊天对象 var textcolor = document.all("textcolor").value; //颜色 var expression = document.all("expression").value; //表情 var isPublic = !(document.all("isSecret").checked); //是否密谈 //调用服务器端方法发送消息 ChatRoom.SendMsg(txtContent, user_to, textcolor, expression, isPublic); //更新聊天内容显示 var div = document.all("chatcontent"); div.innerHTML = ChatRoom.GetNewMsgString().value + div.innerHTML; //清空输入框 document.all("content").value = ""; }
自定义的 refresh_chatcontent 方法用于定时的更新聊天内容。首先获取当前页面中的所有聊天内容,然后通过调用服务器端的 GetNewMsgString 方法得到最新消息的 HTML 字符串。之后判断获得的字符串是否为空,如果不为空就不需要更新,这样避免了不必要的更新,节省了资源的消耗。最后通过 window.setTimeout 对内容做定时的更新,本例中更新的时间间隔为 1000 毫秒,具体代码如下。
//定时更新聊天内容 function refresh_chatcontent() { //调用服务器方法获取最新消息的HTML字符串 var div = document.all("chatcontent"); var strNewMsg = ChatRoom.GetNewMsgString().value; //判断是否为空,避免不必要的更新 if (strNewMsg != "") div.innerHTML = strNewMsg + div.innerHTML; //定时更新 window.setTimeout(refresh_chatcontent, 1000); }
refresh_onlineusers 方法用于更新用户列表,其中包括左侧在线人数的列表以及聊天对象的下拉列表,具体代码如下。
//更新用户列表(左侧和下拉列表) function refresh_onlineusers() { //发送对象列表 var userlist = document.all("userlist"); //调用服务器端方法获取用户列表字符串(用逗号分隔) var strUserlist = ChatRoom.GetOnlineUserString().value; //获取客户端显示的用户列表字符串 var strUserlistClient = ""; for (var i = 1;i < userlist.options.length;i++) { if (i != userlist.options.length - 1) { strUserlistClient += userlist.options[i].value + ","; } else { strUserlistClient += userlist.options[i].value; } } if (strUserlistClient != strUserlist) //在线用户列表发生变化 { var userArr = strUserlist.split(','); //在线用户数 var usercount = document.all("usercount"); usercount.innerHTML = "在线名单:(" + userArr.length + "人)"; //左边用户列表 var tableHTML = ""; for (var i = 0;i < userArr.length;i++) { tableHTML += "" + userArr[i] + " "; } tableHTML += "
"; var div = document.all("onlineusers"); div.innerHTML = tableHTML; //初始化 while (userlist.options.length > 0) { userlist.removeChild(userlist.options[0]); //清空所有选项 } //增加“所有的人”选项 var oOption = document.createElement("OPTION"); oOption.text = "所有的人"; oOption.value = "大家"; userlist.add(oOption); //下拉列表中增加在线用户的选项 for (var i = 0;i < userArr.length;i++) { var oOption = document.createElement("OPTION"); oOption.text = userArr[i]; oOption.value = userArr[i]; userlist.add(oOption); } } //每隔秒更新 window.setTimeout(refresh_onlineusers, 1000); }
当 ChatRoom.aspx 页面被关闭之前将会调用 logout 方法,页面是在
中调用 logout 方法的,代码如下。
logout 方法的主要作用就是当有用户离开聊天室时,更新当前的再线用户,去除离线的用户这一功能是通过调用服务器端的 Logout 方法实现的,具体代码如下。
//退出聊天室 function logout() { ChatRoom.Logout(); }
setObj 方法用于设置聊天对象,通过选择对象的下拉列表设置聊天的对象,例如想和某个人进行私聊,就可以通过下拉列表进行选择,然后选中“密谈”就可以进行私聊了,具体代码如下。
//设置聊天对象 function setObj(str) { var userlist = document.all("userlist"); for (var i = 0;i < userlist.options.length;i++) { if (str == userlist.options[i].value) { userlist.selectedIndex = i; break; } } }
当单击“退出聊天”时会触发 Close 方法用于清除登录信息并关闭浏览器,具体代码如下。
//关闭浏览器窗口 function Close() { var ua = navigator.userAgent; var ie = navigator.appName == "Microsoft Internet Explorer" ? true:false; if (ie) { var IEversion=parseFloat(ua.substring(ua.indexOf("MSIE ")+5,ua.indexOf(";",ua.indexOf("MSIE ")))); if (IEversion< 5.5) { var str = ''; str += ' '; document.body.insertAdjacentHTML("beforeEnd", str); document.all.noTipClose.Click(); } else { window.opener = null; window.close(); } } else { window.close(); } }
下面介绍本方案用到的几个服务器端的方法,首先将 Ajax.dll 组件引用到项目当中,然后在 ChatRoom.aspx 页的 Page_Load 事件中添加如下代码注册 Ajax 类型。
protected void Page_Load(object sender, System.EventArgs e) { Ajax.Utility.RegisterTypeForAjax(typeof(ChatRoom)); }
当单击【发送】按钮发表聊天信息时,会调用客户端的 send 方法将文本框中输入的文字显示出来,实现了发送聊天信息的无刷新技术,在客户端的 send 方法中调用了服务器端的 SendMsg 方法,下面就介绍一下 SendMsg 方法。
首先连接数据库,然后通过调用存储过程“ SendMsg ”将 ChatRoom.aspx 页面中发表的聊天信息插入数据库相应的字段中,具体代码如下。
[Ajax.AjaxMethod()] public void SendMsg(string strMsg, string strUserTo, string strColor, string strExpression, bool bIsPublic) { SqlConnection conn = new SqlConnection( ConfigurationSettings.AppSettings["ConnectionString"]); SqlCommand cmd = conn.CreateCommand(); cmd.CommandType = CommandType.StoredProcedure; cmd.CommandText = "SendMsg"; cmd.Parameters.Add("@user_from", UserName); cmd.Parameters.Add("@user_to", strUserTo); cmd.Parameters.Add("@content", strMsg); cmd.Parameters.Add("@expression", strExpression); cmd.Parameters.Add("@color", strColor); cmd.Parameters.Add("@ispublic", bIsPublic); conn.Open(); cmd.ExecuteNonQuery(); conn.Close(); }
在客户端的 send 方法中也调用了服务器端的 GetNewMsgString 方法,其主要的功能是将存储到数据库中的聊天信息查询出来并且显示到页面上。
首先连接数据库,然后调用存储过程 GetNewMsg 。然后通过数据阅读器读取数据库中的聊天信息,再将数据赋值给字符串 strMsgHTML ,具体代码如下。
[Ajax.AjaxMethod()] public string GetNewMsgString() { string strMsgHTML = ""; //连接数据库 SqlConnection conn = new SqlConnection( ConfigurationSettings.AppSettings["ConnectionString"]); SqlCommand cmd = conn.CreateCommand(); //调用存储过程GetNewMsg cmd.CommandType = CommandType.StoredProcedure; cmd.CommandText = "GetNewMsg"; cmd.Parameters.Add("@username", UserName); conn.Open(); using (SqlDataReader dr = cmd.ExecuteReader()) { while (dr.Read()) { if (dr.GetString(1) != "") { strMsgHTML += string.Format( "{1} {2} {3} {4} >> {5} ", dr.GetString(5), dr.GetString(1), TestIsPublic(dr.GetBoolean(6)), TestYourself(dr.GetString(2)), dr.GetString(4), Replace_GTLT(dr.GetString(3))); } else { strMsgHTML += string.Format("{1} ",dr.GetString(5),dr.GetString(3)); } } } conn.Close(); SetMsgPos(); return strMsgHTML; }
在 ChatRoom.aspx 页面的 HTML 代码中通过 JavaScript 建立一个 refresh_onlineusers 方法用来获取当前的 在线 人数,并且定时的更新当前在线人数,在 refresh_onlineusers 方法中调用了服务器端的 GetOnlineUserString 方法,下面介绍 GetOnlineUserString 方法。
服务器端的 GetOnlineUserString 方法通过调用存储过程 GetOnlineUsers 将当前的在线人数从数据库中读取出来,具体代码如下。
[Ajax.AjaxMethod()] public string GetOnlineUserString() { string strUserlist = ""; SqlConnection conn = new SqlConnection( ConfigurationSettings.AppSettings["ConnectionString"]); SqlCommand cmd = conn.CreateCommand(); cmd.CommandType = CommandType.StoredProcedure; cmd.CommandText = "GetOnlineUsers"; conn.Open(); using (SqlDataReader dr = cmd.ExecuteReader()) { while (dr.Read()) { strUserlist += dr.GetString(1) + ","; } } conn.Close(); return strUserlist.TrimEnd(','); }
在当前的聊天室页面关闭时会出发 HTML 代码中的 logout 方法,此方法通过调用服务器端的 Logout 方法更新在线人数,具体代码如下。
[Ajax.AjaxMethod()] public void Logout() { SqlConnection conn = new SqlConnection( ConfigurationSettings.AppSettings["ConnectionString"]); SqlCommand cmd = conn.CreateCommand(); cmd.CommandType = CommandType.StoredProcedure; cmd.CommandText = "UserLogout"; cmd.Parameters.Add("@username", UserName); conn.Open(); cmd.ExecuteNonQuery(); conn.Close(); }
注意: Ajax.AjaxMethod ()定制属性,属性服务会告知 ajax 封装类为此方法创建一个 javascript 代理,这样才能被客户端调用。
3 .补充说明
聊天室的关键技术在于如何使用媒介来保存所有用户的公有信息,聊天室的数据存放一般有三种形式:一是用全局变量 Application 和 Session 对象,这种形式速度快效果好,但系统资源消耗太大。二是应用读写数据库实现,这种方法简明但服务器频繁读写数据库。第三种可用读写 TXT 文件完成,适合简单的聊天室。
另外需要补充的一点就是利用 Ajax 开发无刷新的聊天时,还有另一种方法,就是利用 ajax 中 updatepanel 实现无刷新显示 。
当用户填写完用户昵称单击【进入聊天室】按钮,就可以进入聊天室,默认情况下用户昵称不允许为空。【进入聊天室】按钮的 Click 事件中的代码首先判断输入昵称的文本框是否为空,如果为空则不会将页面提交到服务器。如果用户输入了昵称,页面就会转向 ChatRoom.aspx 页面就可以进入聊天室进行聊天了,具体大代码如下。
protected void Button1_Click(object sender, EventArgs e) { if (TextBox1.Text == "") return; else Session["Name"] = TextBox1.Text; if (Application["TalkContent"] == null) Application["TalkContent"] = "欢迎" + Session["Name"] + "的到来! "; else Application["TalkContent"] += "欢迎" + Session["Name"] + "进来了! "; Page.Response.Redirect("~/ChatRoom.aspx"); }
当登录成功进入 ChatRoom.aspx 页面的效果 ,左侧是在线的用户昵称,右侧的上半部分是显示所有聊天信息的窗口,下半部分是发表聊天信息的地方。
本例主要是利用了 Ajax 的 UpdatePanel 控件和 Timer 控件制作完成的,通过 UpdatePanel 控件控制刷新的范围,通过 Timer 控件控制刷新的间隔,聊天内容的刷新是通过 ID 为 UpdatePanel1 的 UpdatePanel 控件中实现的,具体代码如下。
而提交聊天信息的无刷新技术是在 ID 为 UpdatePanel2 的 UpdatePanel 控件中实现的,具体代码如下。
当页面加载时,首先读取 Application["TalkContent"] 中的信息并将其赋值给 Label1 ,然后再将 Session["Name"] 赋值给 Label2 用户显示再线用户,具体代码如下。
protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { Label1.Text = Application["TalkContent"].ToString(); Page.Title = "Hello " + Session["Name"].ToString() + " WelCome to Talkroom!"; TextBox1.Focus(); Label2.Text=Label2.Text+Session["Name"].ToString()+" "; } }
在控件 Timer 的 Click 事件中,实现了刷新之后的聊天内容的读取,并且当聊天的内容达到一定数量时,清空所有的聊天纪录,具体代码如下。
protected void Timer1_Tick(object sender, EventArgs e) { Label1.Text = Application["TalkContent"].ToString(); if (Application["TalkContent"].ToString().Length > 2000) Application["TalkContent"] = ""; }
当单击【发言】按钮时,首先会判断文本框是否输入了发言的内容,如果输入了发言的内容则会将内容赋值给 Application["TalkContent"] ,这样就可以显示出来,具体代码如下。
protected void Button1_Click(object sender, EventArgs e) { if (TextBox1.Text == "") return; Application["TalkContent"] += Session["Name"] + " 说: " + TextBox1.Text + " "; Label1.Text = Application["TalkContent"].ToString(); TextBox1.Text = string.Empty; TextBox1.Focus(); }