在我目前正在做的项目中,我们需要开发一个网络聊天应用程序,不是很复杂的聊天,只是一种连接两个人谈论一个非常具体的话题的方式,我们不需要任何身份验证对于两个用户之一,我们不必支持表情符号、头像或类似的东西。
有项目成员建议我们可以通过BOSH来使用XMPP,我说这就像用船网捕鱼一样,并提出了一种更简单的方法,比如简单的Ajax/MySQL网络聊天,但我们担心性能由于不断轮询同时打开的许多聊天,因此在服务器中命中。
有没有人做过这样的事情?你会推荐什么?
你会推荐什么?
XMPP 通过 BOSH
当别人有的时候,没有必要发明你自己的消息格式和传输协议。如果你尝试,它会慢慢变得和 BOSH 一样复杂,但没有第三方库支持或标准化的好处。
如果你不喜欢 HTTP 轮询的想法,你可以在聊天页面上有一个 Flash 电影,它与服务器上的某个守护进程保持持续连接,然后 Flash 电影会调用客户端上的 JavaScript 函数来更新新消息出现时的聊天。(除非你想为你的聊天使用 Flash 界面..)
您可能还想研究彗星。
我以为每个人都用 cometd 来做这种事情。
BOSH 是通过 HTTP 传输 XMPP 的标准。它涉及将数据推送到客户端的 Comet。
几个月前我也做过同样的事情,只是玩弄这些概念很开心。我实际上使用了永久帧技术而不是轮询。
下面的代码是我的“comet”js 文件,其中包含获得“聚会聊天”设置所需的一般概念。
function Comet(key) {
var random = key;
var title = 'Comet';
var connection = false;
var iframediv = false;
var browserIsIE = /*@cc_on!@*/false;
var blurStatus = false;
var tmpframe = document.createElement('iframe');
var nl = '\r\n';
this.initialize = function() {
if (browserIsIE) {
connection = new ActiveXObject("htmlfile");
connection.open();
connection.write("<html>");
connection.write("<script>document.domain = '"+document.domain+"'");
connection.write("</html>");
connection.close();
iframediv = connection.createElement("div");
connection.appendChild(iframediv);
connection.parentWindow.comet = comet;
iframediv.innerHTML = "<iframe id='comet_iframe' src='./comet.aspx?key="+random+"'></iframe>";
} else {
connection = document.createElement('iframe');
connection.setAttribute('id', 'comet_iframe');
iframediv = document.createElement('iframe');
iframediv.setAttribute('src', './comet.aspx?key='+random);
connection.appendChild(iframediv);
document.body.appendChild(connection);
}
}
// this function is called from the server to keep the connection alive
this.keepAlive = function () {
if (!browserIsIE) {
mozillaHack();
}
}
// this function is called from the server to update the client
this.updateClient = function (value) {
var outputDiv = document.getElementById('output');
outputDiv.value = value + nl + outputDiv.value;
if (blurStatus == true) {
document.title = value;
}
if (!browserIsIE) {
mozillaHack();
}
}
this.onUnload = function() {
if (connection) {
// this will release the iframe to prevent problems with IE when reloading the page
connection = false;
}
}
this.toggleBlurStatus = function(bool) {
blurStatus = bool;
}
this.resetTitle = function() {
document.title = title;
}
function mozillaHack() {
// this hack will fix the hour glass and loading status for Mozilla browsers
document.body.appendChild(tmpframe);
document.body.removeChild(tmpframe);
}
}
诀窍是要意识到你的应用程序需要在服务器上调用 CGI 的唯一时间是有人说了什么。对于常规轮询,轮询一个静态页面,只要有新的聊天,CGI 脚本就会更新该页面。使用 HEAD 请求,将时间戳与上次看到的时间戳进行比较,并且仅在它们发生更改时执行完整的 GET。我有一个简单的以这种方式实现的简单聊天应用程序,对于我们拥有的几十个同时用户来说,负载和带宽使用可以忽略不计。
我以为每个人都用 cometd 来做这种事情。
我同意约翰的观点。但是还有一个问题没有回答。
我已经这样做了,但我们没有使用数据库,而是使用了平面文件,它最终确实削弱了服务器,但直到我们有大约 450 个活跃用户,如果我们使用数据库完成它可能会过得很好更好的。
这是在 Godaddy 的基本托管帐户上完成的。
编辑:顺便说一句,当我接到电话时,Godaddy 听起来不那么有趣。
我认为轮询是最简单的方法,并且会首先推荐。如果负载成为问题开始,请研究更复杂的技术。关于利弊的一个很好的讨论在这里 -
http://www.infoq.com/news/2007/07/pushvspull
http://ajaxian.com/archives/a-report-on-push-versus-pull
我刚找到这个帖子,它很旧,但是投票概念给很多人带来了麻烦。所以我会在这里放一个实现示例。但是在给你之前,我应该给你一个前段时间让我生气的建议:
轮询时,您应该注意会话行为(竞争条件)。为了简单起见:如果您打开一个会话,会话文件将被锁定,直到会话关闭,以避免 2 个线程将不同的数据写入其中。因此,如果您需要一个会话来检查用户是否已登录,请始终在轮询之前关闭会话。
我的演示为您提供了一个 PHP 中的轮询实现示例。我不会使用数据库,而是使用文件。单击轮询按钮时,您将进入循环,直到文件被修改,您将保持轮询。当您填写表格并单击发布时,您输入的内容将保存到文件中。文件的修改时间将改变,因此轮询将停止。
提示:使用像Firebug这样的工具来查看发生了什么。
现在让我们用比我的英语更好的语言说话:
<?php
// For this demo
if (file_exists('poll.txt') == false) {
file_put_contents('poll.txt', '');
}
if (isset($_GET['poll'])) {
// Don't forget to change the default time limit
set_time_limit(120);
date_default_timezone_set('Europe/Paris');
$time = time();
// We loop until you click on the "release" button...
$poll = true;
$number_of_tries = 1;
while ($poll)
{
// Here we simulate a request (last mtime of file could be a creation/update_date field on a base)
clearstatcache();
$mtime = filemtime('poll.txt');
if ($mtime > $time) {
$result = htmlentities(file_get_contents('poll.txt'));
$poll = false;
}
// Of course, else your polling will kill your resources!
$number_of_tries++;
sleep(1);
}
// Outputs result
echo "Number of tries : {$number_of_tries}<br/>{$result}";
die();
}
// Here we catch the release form
if (isset($_GET['release']))
{
$data = '';
if (isset($_GET['data'])) {
$data = $_GET['data'];
}
file_put_contents('poll.txt', $data);
die();
}
?>
<!-- click this button to begin long-polling -->
<input id="poll" type="button" value="Click me to start polling" />
<br/><br/>
Give me some text here :
<br/>
<input id="data" type="text" />
<br/>
<!-- click this button to release long-polling -->
<input id="release" type="button" value="Click me to release polling" disabled="disabled" />
<br/><br/>
Result after releasing polling :
<div id="result"></div>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>
<script type="text/javascript">
// Script to launch polling
$('#poll').click(function() {
$('#poll').attr('disabled', 'disabled');
$('#release').removeAttr('disabled');
$.ajax({
url: 'poll.php',
data: {
poll: 'yes' // sets our $_GET['poll']
},
success: function(data) {
$('#result').html(data);
$('#poll').removeAttr('disabled');
$('#release').attr('disabled', 'disabled');
}
});
});
// Script to release polling
$('#release').click(function() {
$.ajax({
url: 'poll.php',
data: {
release: 'yes', // sets our $_GET['release']
data: $('#data').val() // sets our $_GET['data']
}
});
});
</script>