ajax聊天室
当出现Web 2.0术语时,开发人员会谈论很多社区。 而且,无论您认为是炒作还是不炒作,吸引客户或读者就当前话题或所售产品进行即时对话的想法都非常引人注目。 但是你怎么到达那里? 您能否在产品列表的同一页上放置一个聊天框,以使您的客户不必安装特殊软件甚至Adobe Flash Player? 当然! 事实证明,您可以使用免费的现成工具来完成所有这些工作,例如PHP,MySQL,动态HTML(DHTML),Ajax和Prototype.js库。
事不宜迟,让我们直接进入实现。
在登录
聊天的第一步是拥有一个身份。 这需要一个基本的登录页面,如清单1所示。
清单1. index.html
<html>
<head><title>Chat Login</title></head>
<body>
<form action="chat.php" method="post">
Username: <input type="text" name="username">
<input type="submit" value="Login">
</form>
</body>
</html>
在图1中 ,您可以看到此页面的屏幕截图。
图1.聊天的登录窗口
注意:我只需要这个例子,因为我想确定谁在说什么。 对于您的应用程序,您可能已经具有登录名,因此您可以使用现有的用户名。
基本的聊天系统
聊天系统实际上只是一个字符串表,其中每个字符串都属于一个人。 清单2显示了该模式的最基本版本。
清单2. chat.sql
DROP TABLE IF EXISTS messages;
CREATE TABLE messages (
message_id INTEGER NOT NULL AUTO_INCREMENT,
username VARCHAR(255) NOT NULL,
message TEXT,
PRIMARY KEY ( message_id )
);
该脚本包含一个自动递增的消息ID,用户名和消息。 如果您认为这很重要,还可以为每个消息添加时间戳以跟踪其发送时间。
如果要管理有关不同主题的多个对话,则必须有另一个表来跟踪主题,并在messages表中包含相关的topic_id
。 我想使该示例保持简单,因此我使用了最简单的模式。
要设置数据库并加载架构,我使用了以下说明:
% mysqladmin create chat
% mysql chat < chat.sql
根据您的MySQL服务器的设置及其安全设置和密码,命令可能会有所不同。
清单3显示了聊天的基本用户界面(UI)。
清单3. chat.php
<?php
if ( array_key_exists( 'username', $_POST ) ) {
$_SESSION['user'] = $_POST['username'];
}
$user = $_SESSION['user'];
?>
<html>
<head><title><?php echo( $user ) ?> - Chatting</title>
<script src="prototype.js"></script>
</head>
<body>
<div id="chat" style="height:400px;overflow:auto;">
</div>
<script>
function addmessage()
{
new Ajax.Updater( 'chat', 'add.php',
{
method: 'post',
parameters: $('chatmessage').serialize(),
onSuccess: function() {
$('messagetext').value = '';
}
} );
}
</script>
<form id="chatmessage">
<textarea name="message" id="messagetext">
</textarea>
</form>
<button onclick="addmessage()">Add</button>
<script>
function getMessages()
{
new Ajax.Updater( 'chat', 'messages.php', {
onSuccess: function() { window.setTimeout( getMessages, 1000 ); }
} );
}
getMessages();
</script>
</body>
</html>
在脚本的顶部,您可以从登录页面的已发布参数中获取用户名,并将其存储在会话中。 然后该页面继续加载宝贵的Prototype.js JavaScript库,该库将为您处理所有Ajax工作。
之后,页面上会出现聊天消息。 该区域由位于文件底部的getMessages()
JavaScript函数填充。
消息区域下方是一个表单和一个textarea
,用户可以在其中键入其消息文本。 您还有一个标记为“ 添加”的按钮,可将消息添加到聊天中。
该页面如图2所示 。
图2.简单的聊天窗口
仔细检查getMessages()
函数后,发现该页面确实每1000毫秒(1秒)对服务器进行一次轮询,以检查是否有新消息,并将调用的输出放入页面顶部的消息区域。 我将在本文的后面部分详细讨论轮询 ,但是目前,我想通过显示messages.php页面来完成聊天的基本实现,该页面将返回当前的消息集。 此页面如清单4所示。
清单4. messages.php
<table>
<?php
// Install the DB module using 'pear install DB'
require_once("DB.php");
$db =& DB::Connect( 'mysql://root@localhost/chat', array() );
if (PEAR::isError($db)) { die($db->getMessage()); }
$res = $db->query('SELECT * FROM messages' );
while( $res->fetchInto( $row ) )
{
?>
<tr><td><?php echo($row[1]) ?></td>
<td><?php echo($row[2]) ?></td></tr>
<?php
}
?>
</table>
在脚本的顶部,我使用DB库连接数据库,该数据库可从PEAR获得(请参阅参考资料 )。 如果尚未安装该库,则可以使用以下命令进行安装:
% pear install DB
安装PEAR后,脚本可以查询当前消息,获取每一行并输出用户名和注释文本。
最后一个脚本是add.php脚本,该脚本从页面上addmessage()
函数中的Prototype.js Ajax代码调用。 该脚本从会话中获取消息文本和用户名,并将新行插入到消息表中。 清单5中显示了此代码。
清单5. add.php
<?php
// Install the DB module using 'pear install DB'
require_once("DB.php");
$db =& DB::Connect( 'mysql://root@localhost/chat', array() );
if (PEAR::isError($db)) { die($db->getMessage()); }
$sth = $db->prepare( 'INSERT INTO messages VALUES ( null, ?, ? )' );
$db->execute( $sth, array( $_SESSION['user'], $_POST['message'] ) );
?>
<table>
<?php
$res = $db->query('SELECT * FROM messages' );
while( $res->fetchInto( $row ) )
{
?>
<tr><td><?php echo($row[1]) ?></td>
<td><?php echo($row[2]) ?></td></tr>
<?php
}
?>
</table>
add.php脚本还返回当前消息列表,因为原始页面上的Ajax代码会从返回HTML代码中更新聊天消息。 此行为为用户提供了即时反馈,表明他们在对话中添加了评论。
这些是聊天系统的基础。 下一节将介绍如何提高轮询效率。
进行更好的聊天
使用原始的聊天系统,该页面每秒请求对话的所有聊天消息。 虽然对于简短的对话来说还算不错,但是对于更长的时间来说,这将是一个真正的性能问题。 值得庆幸的是,有一个相当简单的解决方案。 一个message_id
与每个消息相关联,并且该数目逐渐增加。 因此,如果您知道具有特定ID的消息,则只需询问该ID之后的任何消息。 这样可以大大减少消息流量。 对于大多数请求,您可能不会收到新消息,这是一个很小的数据包。
将此设置更改为更有效的设计需要对chat.php页面进行一些更改,如清单6所示。
清单6. chat.php(修订版)
<?php
if ( array_key_exists( 'username', $_POST ) ) {
$_SESSION['user'] = $_POST['username'];
}
$user = $_SESSION['user'];
?>
<html>
<head><title><?php echo( $user ) ?> - Chatting</title>
<script src="prototype.js"></script>
</head>
<body>
<div style="height:400px;overflow:auto;">
<table id="chat">
</table>
</div>
<script>
function addmessage()
{
new Ajax.Request( 'add.php', {
method: 'post',
parameters: $('chatmessage').serialize(),
onSuccess: function( transport ) {
$('messagetext').value = '';
}
} );
}
</script>
<form id="chatmessage">
<textarea name="message" id="messagetext">
</textarea>
</form>
<button onclick="addmessage()">Add</button>
<script>
var lastid = 0;
function getMessages()
{
new Ajax.Request( 'messages.php?id='+lastid, {
onSuccess: function( transport ) {
var messages = transport.responseXML.getElementsByTagName( 'message' );
for( var i = 0; i < messages.length; i++ )
{
var message = messages[i].firstChild.nodeValue;
var user = messages[i].getAttribute('user');
var id = parseInt( messages[i].getAttribute('id') );
if ( id > lastid )
{
var elTR = $('chat').insertRow( -1 );
var elTD1 = elTR.insertCell( -1 );
elTD1.appendChild( document.createTextNode( user ) );
var elTD2 = elTR.insertCell( -1 );
elTD2.appendChild( document.createTextNode( message ) );
lastid = id;
}
}
window.setTimeout( getMessages, 1000 );
}
} );
}
getMessages();
</script>
</body>
</html>
现在,您有了一个<table>
标记,而不是包含所有消息的“聊天” <div>
<table>
标记; 您可以为每个新消息动态地向该标签添加行。您可以看到getMessages()
函数中的更改,该函数自第一个版本以来就有所增加。
此新版本的getMessages()
期望messages.php页面的结果是包含新消息的XML块。 现在,messages.php页面还具有一个名为id
的参数,这是该页面看到的最后一条消息的message_id
。 第一次退出时,此ID为0,以便messages.php页面返回其拥有的所有内容。 之后,它将发送到目前为止所看到的最后一条消息的ID。
XML响应由onSuccess
处理程序分解,并使用标准DHTML文档对象模型(DOM)函数(例如insertRow()
, insertCell()
和appendChild()
将每个元素添加到表中。
清单7显示了返回XML而不是HTML的messages.php文件的升级版本。
清单7. messages.php
<?php
// Install the DB module using 'pear install DB'
require_once("DB.php");
header( 'Content-type: text/xml' );
$id = 0;
if ( array_key_exists( 'id', $_GET ) ) { $id = $_GET['id']; }
$db =& DB::Connect( 'mysql://root@localhost/chat', array() );
if (PEAR::isError($db)) { die($db->getMessage()); }
?>
<messages>
<?php
$res = $db->query( 'SELECT * FROM messages WHERE message_id > ?', $id );
while( $res->fetchInto( $row ) )
{
?>
<message id="<?php echo($row[0]) ?>" user="<?php echo($row[1]) ?>">
<?php echo($row[2]) ?>
</message>
<?php
}
?>
</messages>
图3显示了新的增强版本。
图3.优化的聊天窗口
它的外观和感觉都没有改变。 但这比原始的效率要高得多。
“实时”的神话
如果您是Ajax的新手,或者只是一个在该领域工作了足够长的知识的合理程序员,那么“轮询”的想法可能会使您不寒而栗。 不幸的是,轮询就是您的全部。 没有跨平台,跨浏览器的方法可以在客户端和服务器之间创建连续的管道,而无需在线路的两端都安装特殊的软件。 即使那样,您可能仍需要特殊的防火墙配置才能完成所有操作。 因此,如果您想要一个所有人都可以使用的简单解决方案,那么您将拥有Ajax和轮询。
但是,营销和坚持“实时”又如何呢? 轮询不能是实时的。 可以吗 我认为这取决于您对“ 实时 ”一词的定义。 当我过去编写用于电生理数据采集的代码时, 实时意味着微秒。 我确信,在某些情况下,地质学家会很乐意将几分钟,几天甚至几年视为实时。
如果我看维基百科,我发现人类的平均React时间介于200到270毫秒之间。 那只是做诸如击球之类的事情的时候。 即使您已完全参与对话,阅读消息和开始撰写回复的时间也必须更长。 因此,实际上,等待这些聊天消息时,在200毫秒范围内(可能更长一点)的任何时间都很好。 我稍等一下,对我来说还不错。
作为developerWorks上Ajax论坛的主持人(请参阅参考资料 ), 轮询和实时性问题至少每月出现一次。 希望我能揭露轮询问题和涉及Ajax的实时问题。 我的建议是在尝试创建一些非常复杂的实时解决方案之前先尝试轮询。 至少,在尝试定制解决方案之前,请了解可以使用现成的工具做什么。
从这里继续
希望我在这里给您的内容是您在应用程序中自己实现聊天系统的一个很好的起点。 以下是有关下一步的一些想法:
- 跟踪用户:在聊天旁边列出积极参与对话的人员列表。 这样做可以使人们知道参加聚会的人以及何时出入。
- 允许多个对话:允许同时进行不同主题的多个对话。
- 允许表情符号:将诸如
:-)
字符分组转换为适当的笑脸图像。 - 使用URL解析:在客户端JavaScript代码中使用正则表达式查找URL,并将其转换为超链接。
- 处理Enter键:通过挂接到
textarea
的onkeydown
事件来监视用户按下Enter或Return键,而不是使用Add按钮。 - 在用户键入内容时显示:在用户开始键入内容时向服务器发出警报,以便其他参与者可以看到答复处于待处理状态。 如果打字员的速度较慢,这将使对话减少到最低程度的感觉降低了。
- 限制已发布消息的大小:保持对话进行的另一种方法是保持消息较小。 通过限制
onkeydown
限制textarea
的最大字符数,以帮助加快对话速度。
这些只是您可以对该代码进行增强的一些想法。 如果您这样做了,并且希望与社区共享您的更改,请告诉我,我可以将其作为下载中源代码的一部分。
结论
我承认我真的不怎么闲聊。 我从来没有打开我的聊天客户端。 我很长时间只使用一次短信。 我的聊天句柄是idratheryouemail。 说真的 但是我发现上下文聊天(例如本文中显示的内容)确实很有吸引力。 为什么? 因为它专注于网站要解决的主题,所以使对“ TomKat”最新消息的讨论减至最少。
在您的Web应用程序中尝试该示例代码。 查看您是否可以与读者和客户进行实时对话,并在developerWorks Ajax论坛站点上让我知道如何进行。 希望您会感到惊喜。
翻译自: https://www.ibm.com/developerworks/web/library/x-ajaxxml8/index.html
ajax聊天室