题
在我目前正在进行的项目中,我们需要开发一个网络聊天应用程序,不是一个非常复杂的聊天,只是一种连接两个人谈论一个非常具体的主题的方式,我们不需要任何类型的身份验证对于两个用户之一,我们不必支持表情符号、头像或类似的东西。
有项目成员建议我们可以通过BOSH使用XMPP,我说这就像船用网捕鱼一样,并提出了一个更简单的方法,比如简单的Ajax/MySQL网络聊天,但我们担心性能由于同时打开的许多聊天不断轮询,因此在服务器中出现了问题。
以前有人做过这样的事情吗?你会推荐什么?
其他提示
你会推荐什么?
通过 BOSH 的 XMPP
当别人已经发明消息格式和传输协议时,您无需发明自己的消息格式和传输协议。如果您尝试,它会慢慢变得像 BOSH 一样复杂,但没有第三方库支持或标准化的好处。
如果您不喜欢 HTTP 轮询的想法,您可以在聊天页面上播放 Flash 电影,该页面与服务器上的某个守护程序保持持续连接,然后 Flash 电影将调用客户端上的 JavaScript 函数来更新当新消息出现时进行聊天。(除非你想要一个 Flash 界面来聊天..)
您可能还想研究一下彗星。
我以为每个人都用 cometd 来做这类事情。
BOSH 是通过 HTTP 传输 XMPP 的标准。它涉及 Comet 将数据推送到客户端。
诀窍是要认识到您的应用程序需要在服务器上调用 CGI 的唯一时间是当有人说话时。对于常规轮询,轮询静态页面,只要有新的聊天,您的 CGI 脚本就会更新该页面。使用 HEAD 请求,将时间戳与上次看到的时间戳进行比较,并且仅在这些更改时执行完整的 GET。我有一个以这种方式实现的简单的天真的聊天应用程序,对于我们拥有的几十个同时用户来说,负载和带宽使用量可以忽略不计。
几个月前我也做了同样的事情,并且玩弄这些概念很有趣。我实际上使用了 永远的框架 技术而不是轮询。
下面的代码是我的“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);
}
}
我以为每个人都用 cometd 来做这类事情。
我同意约翰的观点。但还有一个问题没有得到解答。
我已经做到了这一点,但我们没有使用数据库,而是使用了平面文件,它最终确实瘫痪了服务器,但直到我们有大约 450 个活跃用户,如果我们使用数据库来完成它,它可能会表现良好更好的。
这是在 Godaddy 的基本托管帐户上完成的。
编辑:顺便说一句,当我接到电话时,Godaddy 听起来不那么有趣了。
我认为轮询是最简单的方法,并且首先推荐这种方法。如果负载成为问题,请开始研究更复杂的技术。关于利弊的很好的讨论在这里 -http://www.infoq.com/news/2007/07/pushvspull
http://ajaxian.com/archives/a-report-on-push-versus-pull
我刚刚发现这篇文章,它很旧,但民意调查的概念给很多人带来了麻烦。所以我会在这里放一个实现示例。但在给你之前,我应该给你一个前段时间让我生气的建议:
当您进行轮询时,您应该注意会话行为(竞争条件)。为了简单起见:如果您打开一个会话,会话文件将被锁定,直到会话关闭为止,以避免 2 个线程向其中写入不同的数据。因此,如果您需要一个会话来检查用户是否已登录等,请务必在轮询之前关闭会话。
我的演示为您提供了 PHP 中轮询实现的示例。我不会使用数据库,而是使用文件。当您单击轮询按钮时,您将进入循环,直到文件被修改,您将保持轮询。当您填写表格并单击“发布”时,您输入的内容将保存到文件中。文件的修改时间将发生变化,因此轮询将停止。
提示:使用类似的工具 萤火虫 看看发生了什么。
现在让我们用比我的英语更好的语言说话:
<?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>