一、简介
jQuery是一个了不起的javascript库,它可以是我们用很少的几句代码就可以创建出漂亮的页面效果。jQuery对AJAX进行了非常好的封装,让AJAX的前台调用更加简洁跨平台。本文不对jQuery基础做更多的介绍,有兴趣者可以访问jQuery社区,得到最新的jQuery版本和相关教程。
JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式,易于人阅读和编写,同时也易于机器解析和生成。JSON采用完全独立于语言的文本格式,但是也使用了类似于C语言家族的习惯,这些特性使JSON成为AJAX调用中理想的数据交换语言。PHP5.2中内置了处理JSON的两个函数:json_encode和json_decode,可以方便地把PHP的数组和JSON字符串进行相互转换。
本文将使用jQuery做前台的AJAX调用,后台PHP处理数据库操作,使用JSON做数据传输,完成一个简易的网页文本聊天室。该聊天室的功能包括发送文本消息、显示聊天内容、显示在线用户列表、对某个用户私聊等。
二、建表
所有的聊天记录和在线用户都存在数据库中,这样数据库里需要建立这样两张表,建表SQL如下:
CREATE TABLE `messages` (
`id` int(10) NOT NULL auto_increment,
`sourceip` varchar(15) NOT NULL, -- 发言者的IP地址
`destip` varchar(15) NULL, -- 接收者的IP地址,如果非私聊就为NULL
`mtime` datetime NOT NULL, -- 发言时间
`content` varchar(500) NOT NULL, -- 发言内容
PRIMARY KEY (`id`)
);
CREATE TABLE `useronline` (
`userip` varchar(15) default NULL, -- 在线用户的IP地址
`lvtime` timestamp NULL default NULL -- 该用户的上一次访问时间
);
三、页面布局
对美工要求不那么苛刻,相关代码:
<div style="width:500px; height:500px;margin:10px auto; background-color:yellow;">
<h1 align="center">聊天室</h1>
<textarea id="content" cols="45" rows="25"></textarea>
<select id='users' size="25" style="width:100px;">
<option>ALL</option>
</select>
对<input type="text" id="dest" size="15" value="ALL" readonly>说:
<input type="text" id="msg" size="40">
<input type="button" value="发送">
</div>
四、发言
当用户在msg框内输入了信息后,点击“发送”按钮,或者在文本框内按回车按钮后,通过AJAX将文本内容发送到后台。在这里应该注意,发送信息和接收信息是两个独立的过程,两者之间没有关系。
发送信息的操作相当比较简单,这里使用jQuery的ajax方法,该方法使用JSON的形式封装了提交类型、目标url、提交的数据和AJAX成功后的操作四个部分的内容。相关代码:
//因为两个地方都要调用,将发送信息的AJAX过程封装为一个函数
function sendMsg(){
$.ajax({
type: "GET",
url: "msgInsert.php",
//将目标和聊天内容发送给msgInsert.php
data: "dest="+ $("#dest").val() +"&msg="+$("#msg").val(),
//发送成功后,将内容框清空
success: function(v){
$("#msg").val("");
}
});
}
//点击"发送"按钮的时候发送
$("input:button").click(sendMsg);
//在文本框上按回车也能发送
$("#msg").keypress(function(){
if(event.keyCode == 13){ //判断按键是回车
sendMsg();
}
});
后台的msgInsert.php接收到数据后,将信息插入到数据库中。该部操作并不用给前台返回数据,事实上上述的前台代码也没有接收任何后台对他的反馈。简单的操作如下:
<?php
//cong worked at Tue Oct 21 06:38:45 GMT 2008
//连接数据库
mysql_connect('localhost','root','root');
mysql_select_db('chat');
mysql_query("set names utf8");
mysql_query("insert into messages values (null, '{$_SERVER['REMOTE_ADDR']}', '{$_GET['dest']}', now(), '{$_GET['msg']}')");
echo mysql_insert_id();
?>
五、接受聊天信息
页面无刷新的接受其他用户的聊天信息,这个功能实现起来就复杂的多了。需要解决这么几个问题:
1、 如何主动接收聊天信息。对于一个客户端来讲,永远也无法知道别人什么时候说了话,所以想实现每条信息都实时地传送到客户端是不可能的。一种替代方案是在客户端每隔几秒钟就发出一次AJAX请求,查询一下后台是否有新的消息。这点可以通过js的setInterval()函数实现。
2、 如何标记哪些信息对于一个客户端是未取过的信息。由于客户端的数量无非确定,每个客户端到底已经获取了哪些信息也不能确定,不能在数据库中通过加标记字段的方式来分辨哪些已发送,哪些未发送。解决的办法是在客户端js中设置一个变量,用于保存该客户端已接收的最新的消息的id,每次发送请求时,将该id传送给后台,只查询id号更大的消息就行了。
3、 如何传送消息。当然是把消息封装为JSON字符串了。只不过可能新消息会有好几条,所以整个消息是一个数组,每条消息是数组中的一个子JSON对象。最终拼装成的字符串应该是如下这样一种格式:
{msgs:[
{id:1,content:'asdf',mtime:'2008-9-2'},
{id:2,content:'aaaa',mtime:'2008-9-2'},
{id:3,content:'bbbb',mtime:'2008-9-2'}
]}
OK,这几个问题都确定了,就可以进行相关的代码开发了。首先前台的请求:
//获取信息
lastid=0; //保存上次取的最后消息id
function getMsg(){
$.getJSON('msgGet.php', {'id':lastid}, function(json){
var arr = json.msgs; //得到对象中的信息的数组
for( var i=0;i lastid = arr[i].id; //将最新的id保存
var str = arr[i].sourceip + "(" +arr[i].mtime + ")对" + arr[i].destip +
"说:\r\n " +arr[i].content + "\r\n";
$("#content").text($("#content").text() + str);//信息添加到文本域后面
//将信息滚动显示到最新
$("#content")[0].scrollTop = $("#content")[0].scrollHeight;
}
});
}
//每两秒从服务器取一次数据
setInterval("getMsg()", 2000);
这里使用了jQuery的getJSON方法,该方法将前后台传递的参数都封装为JSON对象。由于从后台返回回来的JSON里存储了一个数组,所以用for进行了数组的遍历,将每条消息依次添加到文本域。
值得一提的是为了保持文本域的滚动条一直滚动在最底下,即能显示出最新的消息,设置了文本域的scrollTop属性的值为scrollHeight属性的值,$(“#content”)得到的是文本域的jQuery对象,不能直接访问这两个属性,需要用$("#content")[0]来得到文本域的DOM对象。
后台的msgGet.php文件主要接收到用户传过来的最新id,查询表,使用json_encode将每一条数据封装为一个JSON,再用字符串操作将整个将结果集封装为JSON对象数组返回。代码如下:
//查询数据,将满足条件的信息拼装为json字符串
$result = "{msgs:[";
$rs = mysql_query("select * from messages where id>{$_GET['id']}
and destip in ('ALL', '{$_SERVER['REMOTE_ADDR']}')");
while($row = mysql_fetch_assoc($rs)){
$result .= json_encode($row).',';
}
if(mysql_num_rows($rs)) //删除掉最后一个,
$result = substr($result,0,-1);
$result .= "]}";
echo $result;
六、维护在线用户列表
由于不涉及用户的注册登陆等内容,在线用户直接就通过用户的IP地址来表示。用户只要一进入这个聊天室页面,就要把用户的IP地址记入useronline表;一旦用户关闭聊天室,就要把用户的IP地址从表中删掉。同时,要及时的将表中的IP地址发送给每一个客户端,显示在在线用户列表中。
上述的问题可以简化为:在页面上每隔几秒发出请求查询一次在线用户表,在查询的同时更新本客户端IP的最后访问时间。所以前台代码为:
//获取在线用户列表
function getUsers(){
$.getJSON('userGet.php', function(json){
$("#users")[0].length = 1; //将列表框现有的内容清空
var arr = json.users; //得到对象中的信息的数组
for( var i=0;i $("#users").append(" "+arr[i].userip+"");
}
});
}
setInterval("getUsers()", 3000);
后台首先维护在线用户表,然后再查询表中的ip封装为json对象返回给前台。代码:
//维护在线用户列表
//查询ip是否已经存在
$rs = mysql_query("select * from useronline
where userip='{$_SERVER['REMOTE_ADDR']}'");
if(mysql_num_rows($rs)){ //如果存在就更新最后访问时间
mysql_query("update useronline set lvtime=now()
where userip='{$_SERVER['REMOTE_ADDR']}'");
}else{ //如果不存在就新增ip
mysql_query("insert into useronline values
('{$_SERVER['REMOTE_ADDR']}',now())");
}
//将最后更新时间大于10秒的用户清除出在线用户表
mysql_query("delete from useronline where now()-lvtime>10");//将最新数据返回给前台
$result = "{users:[";
$rs = mysql_query("select userip from useronline");
while($row = mysql_fetch_assoc($rs)){
$result .= json_encode($row).',';
}
if(mysql_num_rows($rs)) //删除掉最后一个,
$result = substr($result,0,-1);
$result .= "]}";
echo $result;
七、私聊
有了前面的基础,私聊的功能就很容易实现了。只要当用户点击某个在线用户的时候,将该IP地址显示到目标IP文本框中就可以了。这样发送信息的时候就会把该目标IP一起发送出去,接受信息的时候也只有目标IP的客户端才能查询到这样的信息。
上述功能的js代码如下:
$("#users").click(function(){
$("#dest").val($(this).val());
});
后续的功能前面都已经实现了。