首页 新闻 论坛 群组 Blog 文档 下载 读书 Tag 网摘 搜索 开源 FAQ 第二书店 博文视点 程序员
频道: 研发 数据库 中间件 信息化 视频 .NET Java 游戏 移动 服务: 人才 外包 培训
    图书品种:235680
       
热门搜索: ASP.NET Ajax Spring Hibernate Java

本章讲述有关网络通讯方面的技术,随着Internet技术的飞速发展,越来越多的人已经开始选择通过网络进行即时沟通。网络通讯的途径有很多,例如聊天室、在线发送接收手机短信、发送和接收电子邮件等。通过对本章的学习,可以使更多地人认识到网络通讯的重要性以及实现网络通讯的诸多方案。

7.6  AJAX聊天室无刷新技术方案

聊天室在网上很常见,它给人们提供了在线聊天的机会。聊天室在功能上类似论坛,但是论坛上的信息可以保存较长的时间,而聊天室主要用于在线聊天,所以对存储信息方面的要求不高,另外还要经常删除一些信息,避免数据库存储的内容过多,影响显示的速度。聊天室主要提供三种功能:一是现实自己可以看到的信息,这包括针对所有人的聊天信息、自己发的信息以及发给自己的信息;二是要能够看到在线的人员名单,从而从中选择聊天的对象;三是输入聊天信息。

1.方案分析

Ajax的核心是JavaScript XmlHttpRequest对象。该对象在Internet Explorer 5中首次引入,它是一种支持异步请求的技术。简而言之,XmlHttpRequest对象可以使用JavaScript向服务器提出请求并处理响应,而不阻塞用户。

在创建Web站点时,在客户端执行屏幕更新为用户提供了很大地灵活性。下面是Ajax可以完成的功能:

l          动态检测网页中的数据,无需页面重复加载。例如,动态检测网络考试系统倒记时时间,无需页面重复加载,并等待服务器重新发送整个页面。

l          提升站点的性能,这是通过减少从服务器下载的数据量而实现的。例如,在电子商城网的购物车页面,当更新购物车中的一项物品的数量时,通常会重新载入整个页面。如果使用Ajax计算新物品的总数量,服务器只会返回新物品总数量,而无须重新载入整个页面。

传统的聊天室基于客户端网页的自动刷新技术而实现,它的主要缺点是不断刷新页面造成屏幕的闪动,而经过了Ajax改造后的聊天室,每次只获取最新的发言信息,并将获取结果动态写入页面,不会有以上的缺点。

2.实施过程

*  实例位置:光盘\ mr\07\7.6\01

在制作基于Ajax的无刷新聊天室时,通过一个登录页面进入聊天室的主页面。在登录页面中输入用户名和密码进入聊天室的主页面ChatRoom.aspx中,聊天室的主页面如图7.51所示。

图7.51  啸天聊天室的主页面

程序实现具体步骤:

(1)新建一个网站命名为01,将首页命名为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方法,并将读取的这些信息以参数的形式传递过去。同时,显示聊天的内容。这一步是通过调用服务器端的GetNewMsgString方法实现的,具体代码如下。

  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 = "<table>";

    for (var i = 0;i < userArr.length;i++)

    {

     tableHTML += "<tr><td><label onmouseover=\"this.style.cursor='hand'\" onmouseout=\"this.style.cursor='default'\" onclick=\"setObj('" + userArr[i] + "')\">" + userArr[i] + "</label></td></tr>";

    }

    tableHTML += "</table>";

    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方法,页面是在<Body></Body>中调用logout方法的,代码如下。

<body bottomMargin="0" onbeforeunload="logout()" leftMargin="0" topMargin="0" rightMargin="0" style="text-align: center">

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  = '<object id=noTipClose classid="clsid:ADB880A6-D8FF-11CF-9377-00AA003B7A11">';

      str += '<param name="Command" value="Close"></object>';

      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(

                               "<span class='chatmsg' style='COLOR: #{0}'>{1}&nbsp;{2}&nbsp;{3}&nbsp;{4}&nbsp;>>&nbsp;{5}</span><br>",

                               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("<span class='chatmsg' style='COLOR: #{0}'>{1}</span><br>",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实现无刷新显示,效果如图7.52所示。

图7.52  聊天室的登录界面

当用户填写完用户昵称单击【进入聊天室】按钮,就可以进入聊天室,默认情况下用户昵称不允许为空。【进入聊天室】按钮的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"] + "的到来!<br>";

        else

            Application["TalkContent"] += "欢迎" + Session["Name"] + "进来了!<br>";

        Page.Response.Redirect("~/ChatRoom.aspx");

    }

当登录成功进入ChatRoom.aspx页面的效果如图7.53所示,左侧是在线的用户昵称,右侧的上半部分是显示所有聊天信息的窗口,下半部分是发表聊天信息的地方。

图7.53  聊天室界面

本例主要是利用了Ajax的UpdatePanel控件和Timer控件制作完成的,通过UpdatePanel控件控制刷新的范围,通过Timer控件控制刷新的间隔,聊天内容的刷新是通过ID为UpdatePanel1的UpdatePanel控件中实现的,具体代码如下。

    <asp:UpdatePanel ID="UpdatePanel1" runat="server">

                <ContentTemplate>

                    <asp:Panel ID="Panel1" runat="server" Height="150px" ScrollBars="Auto" Width="360px">

                    <asp:Label ID="Label1" runat="server" Font-Size="10pt"></asp:Label></asp:Panel>

                    <asp:Timer ID="Timer1" runat="server" Interval="2000" OnTick="Timer1_Tick">

                    </asp:Timer>

                </ContentTemplate>

                <Triggers>

                    <asp:AsyncPostBackTrigger ControlID="Timer1" EventName="Tick" />

                </Triggers>

            </asp:UpdatePanel>

而提交聊天信息的无刷新技术是在ID为UpdatePanel2的UpdatePanel控件中实现的,具体代码如下。

<asp:UpdatePanel ID="UpdatePanel2" runat="server">

            <ContentTemplate>

            <span style="font-size: 8pt">发言内容: </span>

            <asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>

            <asp:Button ID="Button1" runat="server" OnClick="Button1_Click" Text="发言" />

            </ContentTemplate>

            <Triggers>

                <asp:AsyncPostBackTrigger ControlID="Button1" EventName="Click" />

            </Triggers>

        </asp: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()+"<br>";

        }

    }

在控件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 + "<br>";

        Label1.Text = Application["TalkContent"].ToString();

        TextBox1.Text = string.Empty;

        TextBox1.Focus();

    }

查看所有评论(0)条】

最近评论



正在载入评论列表...
热点评论