如何使用PHP编写聊天室脚本?

4

有几个访客连接到了http://site.com/chat.php

他们每个人都可以编写并发送文本消息到chat.php上,它将立即显示在每个人的浏览器(http://site.com/chat.php)

我是否需要使用数据库?我的意思是,对于这样一个基于会话的聊天室,AJAX或PHP缓冲功能是否足够?

不同用户的会话如何共享彼此的数据?

任何想法或见解都将不胜感激,谢谢!

编辑:感谢提供的链接。但我想知道的是如何将数据推送到客户端浏览器。是不是只能不断刷新客户端浏览器(无论是否使用AJAX)?此外,这里的挑战在于不同用户之间如何分享聊天文本?你如何存储它们?如何在两个客户端之间同步文本?最好不要使用数据库。

编辑2:实际上,由Peter D提到的YShout做得非常好。它似乎没有持续刷新浏览器。但我不明白它如何将新消息推送到现有用户的窗口。


问题是当用户在您的网页上使用聊天框时,他们实际上并没有连接到任何服务器。因此,您需要对推送/拉取技术进行一些研究并做出选择。如果您让用户的页面刷新,您需要将文本存储在服务器上的某个地方。如果您想将文本推送出去,您需要“知道”有多少用户在聊天屏幕上,并且有办法识别它们并将数据推送到其屏幕上。 - Peter D
如果您提供这个问题的原因,那么给出一个令人满意的答案可能会更容易:您只需要一个聊天系统(最好是现有工具的链接),还是想要构建一个聊天系统以学习一些东西(最好是如何构建它的解释),您不想使用数据库的原因是什么(这是托管要求还是您觉得数据库不好)? - Simon Groenewolt
打开 Yshout 页面的源代码,你会看到 yshout.js 的链接。阅读其中的内容就能理解它是如何更新页面的。 - Joe
AJAX的整个意义在于在向服务器发出请求时无需刷新/重新加载页面。使用“反向AJAX”,可以通过使用“长轮询”来模拟HTTP推送。 - Calvin
5个回答

18

创建聊天应用程序有(大致)三种选择:

套接字

使用Flash/Java和套接字作为前端,使用支持套接字的编程语言作为后端。对于后端,我建议使用Java或Python,因为它们都支持多线程和NIO。虽然PHP也可以实现,但是它不能真正高效地进行多线程处理,通常不太适用于此。如果需要高性能,则可以选择此选项,但这可能不是您要寻找的。

使用Ajax和轮询

在这种情况下,所有客户端都会不断地(例如每隔2秒)轮询是否有新的消息。这种方式感觉很奇怪,因为您只能每隔一段时间获得响应。此外,它对您的服务器和带宽造成了相当大的压力。您可以通过浏览器不断刷新来发现应用程序使用此技术。这是一个次优解决方案。

使用Ajax和推送

这种方法使用多部分响应,并在后端具有长时间运行(PHP)脚本。虽然这不是最佳解决方案,但大多数情况下比轮询更好,并且在几个知名聊天程序中使用。此技术有时被称为 COMET

我的建议:如果您需要用于生产的聊天应用程序,请安装现有的应用程序。编写聊天应用程序并不是很容易。

如果您只是想学习,可以从简单的Ajax/轮询应用程序开始,然后尝试使用Ajax和推送编写一个自己的聊天应用程序。

是的,大多数情况下您需要一个数据库,虽然我成功地实现了一个非常简单的Ajax/轮询解决方案来处理文本文件(但我肯定不会在生产中使用它!)。

就我所知,不能仅使用前端JavaScript创建聊天应用程序而无需服务器端后端!

更新

如果您想知道如何进行数据推送,请查看此处的源代码:http://wehrlos.strain.at/httpreq/client.html。异步多部分是您想要的 :)

function asSendSyncMulti() {
    var httpReq = new XMLHttpRequest();

    showMessage( 'Sending Sync Multipart ' + (++this.reqCount)  );

    // Sync - wait until data arrives
    httpReq.multipart   = true;     
    httpReq.open( 'GET', 'server.php?multipart=true&c=' + (this.reqCount), false );
    httpReq.onload = showReq;
    httpReq.send( null );
}

function showReq( event ) {
    if ( event.target.readyState == 4 ) {
        showMessage( 'Data arrives: ' + event.target.responseText );
    }
    else {
        alert( 'an error occured: ' + event.target.readyState );
    }

}

showReq被调用每次数据到达时,而不仅仅是像常规ajax请求那样调用一次(我这里并未使用jquery或prototype,所以代码有点臃肿 - 这真的很旧 :)).

下面是服务器端的部分:

<?php

    $c = $_GET[ 'c' ];

    header('Content-type: multipart/x-mixed-replace;boundary="rn9012"');

    sleep( 1 );

    print "--rn9012\n";
    print "Content-type: application/xml\n\n";
    print "\n";
    print "Multipart: First Part of Request " . $c . "\n";
    print "--rn9012\n";
    flush();

    sleep( 3 );

    print "Content-type: application/xml\n\n";
    print "\n";
    print "Multipart: Second Part of Request " . $c . "\n";
    print "--rn9012--\n";

?>

更新2

关于数据库:如果您的后端采用像mod_php/cgi这样不共享的架构,那么您肯定需要像数据库或文本文件这样的某种外部存储。但是:您可以通过编写自己的HTTP服务器来依赖内存(在PHP中可能是可行的,但我不建议进行严肃的工作)。这并不是很复杂,但可能超出了您问题的范围^^

更新3

我犯了一个错误!我把所有东西搞混了,因为我实际上已经很久没有做类似的事情了。以下是更正:

  1. 多部分响应仅适用于Mozilla浏览器,因此受到限制。COMET并不意味着多部分响应。

  2. COMET的意思是:传统的单部分响应,但会被保留(使用无限循环和睡眠),直到有数据可用。因此,浏览器对每个操作都有1个请求/响应(在最坏的情况下),即使没有任何值得响应的内容,也是每x秒一个请求而不是一个请求。


+1 好信息。我受到了帮助! - Shiplu Mokaddim
1
好的,各位,这个答案已经超过3年了,已经不相关了。看看node.js和socket.io。 - stefs
又是一年,又有一个新的库:作为 socket.io 的替代方案,我会考虑使用 sock.js。此外,如果你处理的连接大多是实时读取而不是实时写入,你可以使用 EventSource(也称为服务器发送事件)。它有一些 polyfills 和包装器,但在服务器端,它的行为很像好老的长轮询。你只需要像往常一样使用普通的 AJAX 进行写操作即可。 - Alan Plum

4
您提到希望此功能可以在没有数据库的情况下运行,并且不需要客户端轮询服务器以获取更新。
理论上,您可以通过在服务器上将聊天的“日志”存储在文本文件中,并更改页面使用户在chat.php页面上执行GET请求来实现此目的,但PHP页面实际上从未完成向用户发送响应。 (例如,响应从未完成)
当没有消息时,您需要发送一些“无操作”数据以保持连接,但在理论上,这将起作用。
问题是,要完成上述工作仍然需要很多工作。 您需要向服务器提交新评论进行AJAX发布...用户的浏览器将一直旋转(除非您将聊天记录嵌套在iframe中-例如,更多工作)...而这种设置将非常难以管理。
我建议从其他地方获取免费的聊天脚本(例如http://tinychat.com/),或者如果您想自己制作(为了乐趣/体验),那么请开始使用DB并构建一个页面,该页面将从服务器推送和拉取消息。

最后,如果您担心使用AJAX请求会对服务器造成压力...不用担心。只需构建聊天功能,如果发现存在性能问题,请回到StackOverflow上提问如何优化,以便在没有活动时不会有数百个请求涌入聊天室。


2

虽然HTTP不是为轻松推送而设计的,但您可以通过使PHP脚本永远不终止并仔细观察JavaScript结果来模拟推送连接。

本质上,您正在模拟流读取器。


这个解决方案的可扩展性如何? - pdu
很多聊天应用都这样做(这也是我的示例代码所做的)。虽然不是最佳解决方案,但肯定比拉取更好。 - stefs

1
你可以使用类似PubNub的服务完全使用HTML和JavaScript来完成此操作。您不需要数据库,因为您可以使用history api之类的东西来填充最后x个聊天消息。
以下是使用PubNub构建聊天应用程序的快速教程。

10行代码中的实时聊天应用程序

enter image description here

Enter Chat and press enter
<div><input id=input placeholder=you-chat-here /></div>

Chat Output
<div id=box></div>

<script src=http://cdn.pubnub.com/pubnub.min.js></script>
<script>(function(){
var box = PUBNUB.$('box'), input = PUBNUB.$('input'), channel = 'chat';
PUBNUB.subscribe({
    channel  : channel,
    callback : function(text) { box.innerHTML = (''+text).replace( /[<>]/g, '' ) + '<br>' + box.innerHTML }
});
PUBNUB.bind( 'keyup', input, function(e) {
    (e.keyCode || e.charCode) === 13 && PUBNUB.publish({
        channel : channel, message : input.value, x : (input.value='')
    })
} )
})()</script>

谢谢,这就是我要找的代码。 - CodeSlayer

1

如果您希望新用户在进入聊天室之前加载聊天历史记录,则需要使用DB或其他存储。 除非您想创建一个用于学习的聊天,否则有太多免费的聊天工具可供使用,不值得费心。

http://tinychat.com 是另一个简单的聊天网站。

AJAX很好用。我已经为我的网站创建了一个简单的页面。但是我发现聊天并没有像你想象的那样经常被使用。

共享数据会变得更加复杂,最好通过托管IRC服务器并允许用户使用具有数据交换功能的IRC客户端来实现。虽然没有什么阻止您让一个用户上传到网站,然后其他人下载。使用Web界面进行点对点通信将会很困难,因为用户彼此之间没有任何连接。


没错,这就是我需要解决的问题,没有数据库:如何同步聊天文本并将其推送到所有客户端浏览器? - datasn.io
在浏览器中推送内容的唯一方式是作为浏览器中运行的小程序,该程序注册了一个“推送”位置。大多数AJAX聊天会调用一个低开销页面来请求更新。我在我的聊天室中也是这样做的,提供客户端接收的最后一条消息ID,然后页面返回自那之后的消息。这需要在服务器上进行某种类型的存储,即使只是持续到所有客户端都完成更新为止。我使用了一个数据库来过期消息,因此我只查询大于ID的未过期消息。 - Joe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接