关于php的Xajax到底是什么,这里不再介绍,详情可以看我《【php】Xajax Helloworld》(点击打开链接)与《【php】注册系统和使用Xajax即时验证用户名是否被占用》(点击打开链接),这里具体说说,怎么用php来实现在线聊天室或者直播间,Xajax在php就像Dwr在javaee的地位,《【DWR】Helloworld》(点击打开链接),是一种无需用户刷新,与后台交互数据自动返回结果的ajax技术。
一、基本目标
1、首先输入用户名与密码之后,就可以登录在线聊天室,在左侧的在线用户列表自然可以显示当前在线的用户,而先前登录了的用户,在隔一段时间无需自动刷新,或者直接点刷新就能马上看到当前在线的的用户
2、在线聊天室自然能够实现在线聊天的功能,这个聊天无需刷新,对方隔一段时间能够收到信息
3、如果用户长时间没有发言,就会被系统自动踢出聊天室,这里为了说明结果只设置为15秒,并且gif中的等待时间已经被剪掉
二、基本思想
1、文件目录结构如下:
首先xajax_core,xajax_js与copyright.inc.php是xajax插件固有的文件,而login.html是登录页面,login.php完成登录判断并且作在线用户登记
退出登录则分如下两个步骤,先跳转到outline.php清除在线用户的登记,exit.php是清除session,
之所以要分开两个页面也做这样事,是让那些没有登录的输入网址直接访问用户直接跳到exit.php,避免与正常退出登录的用户一样需要走操作数据库的流程,
当然,也确实可以放在一起,只是这样,会导致多重逻辑判断
而整个在线聊天的核心在与chat.php,其实chat.php确实应该用框架页分块优化的,《【HTML】框架页、表单与JavaScript的综合应用》(点击打开链接),这样在线用户列表、聊天内容可以分块刷新,不影响到其他部分,不用一刷就刷整页
2、数据库中表的结构:
共三张表,user表记录了用户信息,没什么好说了,主键id与用户名、密码,消息表与在线用户信息表不设置主键,是因为这两个表要经常被擦写,如果你设置主键就会导致主键的顺序索引1、2、3、4……中间断开,不美观,这里也没有先后顺序,没必要用主键
三、制作过程
1、login.html
登录页面,没什么好说的,就一个表单:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>登录页面</title> </head> <body> <form action="login.php" method="post"> 用户名:<input type="text" name="username" /><br /> 密码:<input type="password" name="password" /><br /> <input type="submit" value="登录" /> </form> </body> </html>
登录处理的页面,其实整个流程与之前的《【php】登录系统与输出浏览者信息》(点击打开链接)登录流程类似,只是多了一步把用户信息登记在在线列表的程序,这里如果想做得更加完善就把用户信息登记在在线列表之前,先查询库的在线列表里面有没有这个用户,如果没有才输入,防止一些用户上次不正常退出,比如直接关闭浏览器,而不是点右上角的“退出登录”。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>登陆中……</title> </head> <body> <?php session_start(); $username=$_REQUEST["username"]; $password=$_REQUEST["password"]; $con=mysql_connect("localhost","root","root"); if(!$con){ die("数据库连接失败!"); } mysql_select_db("test",$con); $dbusername=null; $dbpassword=null; $result=mysql_query("select * from user where username='".$username."';"); while($row=mysql_fetch_array($result)){ $dbusername=$row["username"]; $dbpassword=$row["password"]; } if(is_null($dbusername)){ ?> <script> alert("查无此人!"); window.location.href="login.html"; </script> <?php } else{ if($dbpassword!=$password){ ?> <script> alert("密码错误!"); window.location.href="login.html"; </script> <?php } else{ $_SESSION["username"]=$username; $_SESSION["code"]=mt_rand(0,100000); //如果用户的登录信息正确,就把用户的登录信息登录进在线用户信息表useronlinelist mysql_query("insert into useronlinelist(username,logindatetime,lastdatetime) values ('".$username."',now(),now());"); ?> <script> window.location.href="chat.php"; </script> <?php } } mysql_close($con); ?> </body> </html>
登录之后会让用户进入聊天室chat.php,这网页是整个工程的核心,放在最后讲,做的时候也应该最后做,先把用户退出登录的离线处理页面与清理session页面做好。
用户退出登录的离线处理页面,操作数据库,把在线用户信息表除名
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>无标题文档</title> </head> <body> <?php session_start(); if(isset($_SESSION["code"])){ $username=$_SESSION["username"]; $con=mysql_connect("localhost","root","root"); if(!$con){ die("连接失败!"); } mysql_select_db("test",$con); //把当前退出用户从在线用户信息表useronlinelist除名 mysql_query("DELETE FROM useronlinelist WHERE username = '".$username."';"); mysql_close($con); ?> <script> window.location.href="exit.php"; </script> <?php } else{ ?> <script> alert("请正常登录!"); window.location.href="exit.php"; </script> <?php } ?> </body> </html>
4、exit.php
与《【php】登录系统与输出浏览者信息》(点击打开链接)完全相同,完全没有改动
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>登出页面</title> </head> <body> <?php session_start(); session_destroy(); ?> <script> window.location.href="login.html"; </script> </body> </html>
在线聊天核心页面
(1)HTML布局
这里其实更应该用框架页来做的《【HTML】框架页、表单与JavaScript的综合应用》(点击打开链接),因为可以分块刷新,不影响到其他部分,不用一刷就刷整页,这里的div布局具体怎么布置《【CSS】关于div的对齐与网页布局》(点击打开链接),左上是一个“欢迎登录”语句,右上是一个“退出登录”,左中是“在线用户列表”里面的ul设置了useronlist是为了其后xajax可以控制,右中的"聊天室"的结构与左中完全一模一样,最下方是那些表单,能够让用户填写自己要发表的内容,行列文本的tomanspan,也是为了被xajax控制,因为这个对……说,实质上也同样是一个在线用户列表。其余混杂在里面的php部分主要是判断用户是否登录这一页。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>欢迎登录聊天室</title> </head> <body> <?php session_start(); if(isset($_SESSION["code"])){ ?> <div style="float:left">欢迎登录!<?php echo "${_SESSION["username"]}"; ?></div><div style="float:right"><a href="outline.php">退出登录</a></div> <div style="clear:both"></div> <div style="float:left; margin-right:5%;"> 在线用户列表 <ul id="useronlist"> 加载中…… </ul> </div> <div style="float:left"> 聊天室 <ul id="messagecontent"> </ul> </div> <div style="clear:both"></div> <div><?php echo "${_SESSION["username"]}"; ?>对<span id="tomanspan"></span>说:<input type="text" id="message" style="width:80%"/><button onclick="sendmessage()">发言</button><button onclick="onlineuserlist()">刷新</button></div> <?php } else{ ?> <script> alert("请正常登录!"); window.location.href="exit.php"; </script> <?php } ?> </body> </html>(2)XAJAX部分
也就是整个页面实现的核心,
首先是清理垃圾函数$xajax->registerFunction("cleardirty");这个函数会被其后的javascript语句每隔60秒执行一次,这个函数主要是防止一些用户上次不正常退出,比如直接关闭浏览器,而不是点右上角的“退出登录”,而造成数据库里面产生脏数据。同时也没有必要在在线聊天记录表中保存这么多聊天记录,甚至可以不自动删除1天前的聊天记录,时间可以更短,毕竟查询最近10条聊天记录,可能与刚插入的聊天记录产生读写互斥,所以还是通过时间差来比对。
function cleardirty(){ $orps=new xajaxResponse(); $con=mysql_connect("localhost","root","root"); if(!$con){ die("连接失败!"); } mysql_select_db("test",$con); mysql_query("set names utf8"); mysql_query("delete from useronlinelist where timestampdiff(second,lastdatetime,now())>60;"); mysql_query("delete from message where timestampdiff(hour,speakdatetime,now())>1; "); mysql_close($con); return $orps; }删除那些最后一次发表时间离现在超过60秒的人,注意mysql中是怎么求时间差的,
然后删除那些离现在已经超过1天的消息,
之后是在线用户列表显示函数$xajax->registerFunction("userlist");与在线消息显示函数$xajax->registerFunction("message");,这个两个函数会被其后的javascript语句每隔10秒执行一次,也就是每隔10秒就刷新一下页面
function userlist(){ $orps=new xajaxResponse(); $con=mysql_connect("localhost","root","root"); if(!$con){ die("连接失败!"); } mysql_select_db("test",$con); mysql_query("set names utf8"); $result=mysql_query("select * from useronlinelist;"); $textto="<select id='toman' name='toman'><option value='大家'>大家</option>"; $textonline=""; while($row=mysql_fetch_array($result)){ $textto.="<option value='${row["username"]}'>${row["username"]}</option>"; $textonline.="<li>${row["username"]}</li>"; } $textto.="</select>"; $orps->assign("tomanspan","innerHTML",$textto); $orps->assign("useronlist","innerHTML",$textonline); mysql_close($con); return $orps; } function message(){ $orps=new xajaxResponse(); $con=mysql_connect("localhost","root","root"); if(!$con){ die("连接失败!"); } mysql_select_db("test",$con); mysql_query("set names utf8"); $result=mysql_query("select * from message order by speakdatetime desc limit 10;"); $messagecontent=""; while($row=mysql_fetch_array($result)){ $messagecontent.="<li>【${row["fromman"]}】对【${row["toman"]}】说:${row["message"]}(${row["speakdatetime"]})</li>"; } $orps->assign("messagecontent","innerHTML",$messagecontent); mysql_close($con); return $orps; }两函数的结构完全相同,把所有的查询出来的结果,通过字符串不停地连接连成有序列表的<li>节点部分,
然后通过xajax指派到相应的<ul>节点下,这样也就实现了刷新功能
同时在线用户列表显示函数$xajax->registerFunction("userlist");,对于“对……说”这部分的构造要注意,因为构造<select>的工作必须在xajax的完成,再把一个完整的下拉列表放置在行内文本中,不能在html部分,放置一个<select>再把<option>,这里理论是可以的,但是在实际上xajax不知道什么原因无法指派。
最后发表函数$xajax->registerFunction("insertmessage");这个函数仅仅是用户点击发表按钮,并取得里面的发表人、收信人、发表内容才会被执行
要插入一条用户发表的信息入数据库之前,你首先要判断,这个用户是否离上次发表超过15秒,是否已经被清理垃圾函数$xajax->registerFunction("cleardirty");清理,而且判断其离上次发表是否超过15秒,清理了则弹出,未被清理也未超过15秒才允许入库并更新库里面的最后一次发表时间,如果不判断是否已经被清理垃圾函数$xajax->registerFunction("cleardirty");清理,会由于这个函数在并行,谁先谁后的操作系统线程问题,导致整个系统出现了问题。
function insertmessage($from,$to,$message){ $orps=new xajaxResponse(); $con=mysql_connect("localhost","root","root"); if(!$con){ die("连接失败!"); } mysql_select_db("test",$con); mysql_query("set names utf8"); $dbusername=null; $result=mysql_query("select * from useronlinelist where username='".$from."';"); while($row=mysql_fetch_array($result)){ $dbusername=$row["username"]; } $result=mysql_query("select timestampdiff(second,(select lastdatetime from useronlinelist where username='".$from."'),now()) as difftime;"); $difftime=null; while($row=mysql_fetch_array($result)){ $difftime=$row["difftime"]; } if(!(is_null($dbusername))&&$difftime<60){ mysql_query("update useronlinelist set lastdatetime=now() where username='".$from."';"); mysql_query("insert into message(fromman,toman,message,speakdatetime) values ('".$from."','".$to."','".$message."',now());"); } else{ $orps->alert("长达60秒没有发言,请重新登录!"); $orps->script("window.location.href='outline.php';"); } mysql_close($con); return $orps; }
综上所述,整个xajax部分如下,其余没有说明的语句,在之前的《【php】Xajax Helloworld》(点击打开链接)与《【php】注册系统和使用Xajax即时验证用户名是否被占用》(点击打开链接)说过,这里不再赘述,你完全可以用一个include语句将其分开,我是见小工程就放在一个页面chat.php里面:
<?php include 'xajax_core/xajax.inc.php'; $xajax=new xajax(); $xajax->registerFunction("cleardirty"); $xajax->registerFunction("userlist"); $xajax->registerFunction("message"); $xajax->registerFunction("insertmessage"); function cleardirty(){ $orps=new xajaxResponse(); $con=mysql_connect("localhost","root","root"); if(!$con){ die("连接失败!"); } mysql_select_db("test",$con); mysql_query("set names utf8"); mysql_query("delete from useronlinelist where timestampdiff(second,lastdatetime,now())>60;"); mysql_query("delete from message where timestampdiff(hour,speakdatetime,now())>1; "); mysql_close($con); return $orps; } function userlist(){ $orps=new xajaxResponse(); $con=mysql_connect("localhost","root","root"); if(!$con){ die("连接失败!"); } mysql_select_db("test",$con); mysql_query("set names utf8"); $result=mysql_query("select * from useronlinelist;"); $textto="<select id='toman' name='toman'><option value='大家'>大家</option>"; $textonline=""; while($row=mysql_fetch_array($result)){ $textto.="<option value='${row["username"]}'>${row["username"]}</option>"; $textonline.="<li>${row["username"]}</li>"; } $textto.="</select>"; $orps->assign("tomanspan","innerHTML",$textto); $orps->assign("useronlist","innerHTML",$textonline); mysql_close($con); return $orps; } function message(){ $orps=new xajaxResponse(); $con=mysql_connect("localhost","root","root"); if(!$con){ die("连接失败!"); } mysql_select_db("test",$con); mysql_query("set names utf8"); $result=mysql_query("select * from message order by speakdatetime desc limit 10;"); $messagecontent=""; while($row=mysql_fetch_array($result)){ $messagecontent.="<li>【${row["fromman"]}】对【${row["toman"]}】说:${row["message"]}(${row["speakdatetime"]})</li>"; } $orps->assign("messagecontent","innerHTML",$messagecontent); mysql_close($con); return $orps; } function insertmessage($from,$to,$message){ $orps=new xajaxResponse(); $con=mysql_connect("localhost","root","root"); if(!$con){ die("连接失败!"); } mysql_select_db("test",$con); mysql_query("set names utf8"); $dbusername=null; $result=mysql_query("select * from useronlinelist where username='".$from."';"); while($row=mysql_fetch_array($result)){ $dbusername=$row["username"]; } $result=mysql_query("select timestampdiff(second,(select lastdatetime from useronlinelist where username='".$from."'),now()) as difftime;"); $difftime=null; while($row=mysql_fetch_array($result)){ $difftime=$row["difftime"]; } if(!(is_null($dbusername))&&$difftime<60){ mysql_query("update useronlinelist set lastdatetime=now() where username='".$from."';"); mysql_query("insert into message(fromman,toman,message,speakdatetime) values ('".$from."','".$to."','".$message."',now());"); } else{ $orps->alert("长达60秒没有发言,请重新登录!"); $orps->script("window.location.href='outline.php';"); } mysql_close($con); return $orps; } $xajax->processRequest(); $xajax->printJavascript(); ?>
有了上面的xajax数字叙述,这里什么时候执行什么xajax函数顺理成章,注意对于发表函数$xajax->registerFunction("insertmessage");要在这里javascript中取到上面html部分的发信人、收信人、发送内容,才执行,而当前时间则用mysql语句的now()
<script> onlineuserlist(); setInterval("onlineuserlist()",10000); setInterval("cleardirty()",60000); function cleardirty(){ xajax_cleardirty(); } function onlineuserlist(){ xajax_userlist(); xajax_message(); } function sendmessage(){ var from="<?php echo "${_SESSION["username"]}"; ?>"; var to=document.getElementById("toman").value; var message=document.getElementById("message").value; xajax_insertmessage(from,to,message); xajax_userlist(); xajax_message(); document.getElementById("message").value=""; } </script>
<?php include 'xajax_core/xajax.inc.php'; $xajax=new xajax(); $xajax->registerFunction("cleardirty"); $xajax->registerFunction("userlist"); $xajax->registerFunction("message"); $xajax->registerFunction("insertmessage"); function cleardirty(){ $orps=new xajaxResponse(); $con=mysql_connect("localhost","root","root"); if(!$con){ die("连接失败!"); } mysql_select_db("test",$con); mysql_query("set names utf8"); mysql_query("delete from useronlinelist where timestampdiff(second,lastdatetime,now())>60;"); mysql_query("delete from message where timestampdiff(hour,speakdatetime,now())>1; "); mysql_close($con); return $orps; } function userlist(){ $orps=new xajaxResponse(); $con=mysql_connect("localhost","root","root"); if(!$con){ die("连接失败!"); } mysql_select_db("test",$con); mysql_query("set names utf8"); $result=mysql_query("select * from useronlinelist;"); $textto="<select id='toman' name='toman'><option value='大家'>大家</option>"; $textonline=""; while($row=mysql_fetch_array($result)){ $textto.="<option value='${row["username"]}'>${row["username"]}</option>"; $textonline.="<li>${row["username"]}</li>"; } $textto.="</select>"; $orps->assign("tomanspan","innerHTML",$textto); $orps->assign("useronlist","innerHTML",$textonline); mysql_close($con); return $orps; } function message(){ $orps=new xajaxResponse(); $con=mysql_connect("localhost","root","root"); if(!$con){ die("连接失败!"); } mysql_select_db("test",$con); mysql_query("set names utf8"); $result=mysql_query("select * from message order by speakdatetime desc limit 10;"); $messagecontent=""; while($row=mysql_fetch_array($result)){ $messagecontent.="<li>【${row["fromman"]}】对【${row["toman"]}】说:${row["message"]}(${row["speakdatetime"]})</li>"; } $orps->assign("messagecontent","innerHTML",$messagecontent); mysql_close($con); return $orps; } function insertmessage($from,$to,$message){ $orps=new xajaxResponse(); $con=mysql_connect("localhost","root","root"); if(!$con){ die("连接失败!"); } mysql_select_db("test",$con); mysql_query("set names utf8"); $dbusername=null; $result=mysql_query("select * from useronlinelist where username='".$from."';"); while($row=mysql_fetch_array($result)){ $dbusername=$row["username"]; } $result=mysql_query("select timestampdiff(second,(select lastdatetime from useronlinelist where username='".$from."'),now()) as difftime;"); $difftime=null; while($row=mysql_fetch_array($result)){ $difftime=$row["difftime"]; } if(!(is_null($dbusername))&&$difftime<60){ mysql_query("update useronlinelist set lastdatetime=now() where username='".$from."';"); mysql_query("insert into message(fromman,toman,message,speakdatetime) values ('".$from."','".$to."','".$message."',now());"); } else{ $orps->alert("长达60秒没有发言,请重新登录!"); $orps->script("window.location.href='outline.php';"); } mysql_close($con); return $orps; } $xajax->processRequest(); $xajax->printJavascript(); ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>欢迎登录聊天室</title> </head> <body> <?php session_start(); if(isset($_SESSION["code"])){ ?> <div style="float:left">欢迎登录!<?php echo "${_SESSION["username"]}"; ?></div><div style="float:right"><a href="outline.php">退出登录</a></div> <div style="clear:both"></div> <div style="float:left; margin-right:5%;"> 在线用户列表 <ul id="useronlist"> 加载中…… </ul> </div> <div style="float:left"> 聊天室 <ul id="messagecontent"> </ul> </div> <div style="clear:both"></div> <div><?php echo "${_SESSION["username"]}"; ?>对<span id="tomanspan"></span>说:<input type="text" id="message" style="width:80%"/><button onclick="sendmessage()">发言</button><button onclick="onlineuserlist()">刷新</button></div> <?php } else{ ?> <script> alert("请正常登录!"); window.location.href="exit.php"; </script> <?php } ?> </body> </html> <script> onlineuserlist(); setInterval("onlineuserlist()",10000); setInterval("cleardirty()",60000); function cleardirty(){ xajax_cleardirty(); } function onlineuserlist(){ xajax_userlist(); xajax_message(); } function sendmessage(){ var from="<?php echo "${_SESSION["username"]}"; ?>"; var to=document.getElementById("toman").value; var message=document.getElementById("message").value; xajax_insertmessage(from,to,message); xajax_userlist(); xajax_message(); document.getElementById("message").value=""; } </script>