不使用数据库或文件的PHP基于Web的聊天

时间:2012-07-17 05:54:47

标签: php design-patterns web-applications

我正在尝试使用PHP实现实时聊天应用程序。是否可以在不使用数据库或文件等持久数据存储的情况下执行此操作。基本上我需要的是一个用PHP编写的调解员

  1. 接受来自客户端浏览器的消息
  2. 将消息广播给其他客户
  3. 忘记留言

14 个答案:

答案 0 :(得分:14)

您应该查看html5的 Web套接字。它使用双向连接,因此您不需要任何数据库或文件。任何来到服务器的聊天消息都将直接发送给其他用户浏览器而无需任何Ajax调用。但您还需要设置Web套接字服务器。

Web套接字也用于许多实时应用程序。我很快就会编写完整的教程。我会通知你。

答案 1 :(得分:7)

刚刚尝试过我以前从未做过的回答这个问题的事情。似乎工作,但我只测试了一次。我没有使用Socket而是想到了使用共享的Session变量。基本上,无论用户如何,我都强制Session_id为相同的值,因此它们都共享相同的数据。从快速测试看起来似乎有效。这是我做的:

session_id('12345');
session_start();
$session_id = session_id();
$_SESSION['test'] = $_SESSION['test'] + 1;
echo "session: {$session_id} test: {$_SESSION['test']} <br />";

所以我的思维过程就是你只需将聊天信息存储在一个Session变量中,并强制所有人,无论他们是谁使用共享会话。然后,您可以简单地使用ajax继续重新加载当前的Session变量,并在添加消息时使用ajax编辑会话变量。此外,您可能希望将Session设置为永不过期或具有非常长的maxlifetime。

正如我所说,我只是玩了几分钟,看看它是否会起作用。

答案 2 :(得分:6)

您需要使用Sockets。本文将准确介绍您要执行的操作:http://devzone.zend.com/209/writing-socket-servers-in-php/

答案 3 :(得分:3)

当我试图解决同样的问题时,我选择了Nginx的Push Module。我选择采用这种方式,因为我必须支持旧浏览器(通常不支持WebSockets),并且没有信心在TCP代理后面设置适当的解决方案,如Socket.io

工作流程如下:

  1. 客户通过长轮询连接到我的/subscriber位置,该位置对所有人开放。
  2. /publisher位置仅接受来自我自己服务器的连接
  3. 当客户订阅和谈话时,它基本上只是要求PHP脚本处理发送的任何数据。
  4. 此脚本可以执行验证,授权等操作,然后以JSON格式将消息转发(通过curl)到/publisher
  5. Nginx的推送模块处理将消息发送回订户,客户端建立新的长轮询连接。
  6. 如果我必须重新执行此操作,那么我肯定会使用Socket.io路由,因为它有适当的后备方式来进行Comet风格的长轮询,并且对于客户端和服务器脚本都有很好的文档。

    希望这有帮助。

答案 4 :(得分:2)

您需要使用某种存储作为缓冲区。不使用文件或数据库(也使用文件)是合理的。您可以尝试使用php的shared memory函数,但我不知道任何有效的解决方案,因此您必须从头开始。

答案 5 :(得分:2)

  

是否可以在不使用持久数据存储的情况下执行此操作   数据库或文件?

这是可能的,但您不应该使用。基于数据库或文件不会减慢聊天速度。它将为您的聊天应用程序提供额外的安全性。您可以使用 ajax 套接字进行基于网络的聊天,而无需持久数据。

您应该看到以下帖子:

  1. Is database based chat room bad idea?
  2. Will polling from a SQL DB instead of a file for chat application increase performance?
  3. Using memcached as a database buffer for chat messages
  4. persistent data in php question
  5. https://stackoverflow.com/questions/6569754/how-can-i-develop-social-network-chat-without-using-a-database-for-storing-the-c
  6. File vs database for storage efficiency in chat app

答案 6 :(得分:2)

PHP不适合您的要求(在正常的设置中,如apache-php,fastcgi等),因为PHP脚本会针对每个请求从上到下执行,并且无法在请求之间维护任何状态使用外部服务或数据库/文件(例如http://php.net/manual/de/book.apc.php除外,但它不用于实现聊天,也不会扩展到多个服务器。)

你一定要看Node.js,特别是Node.js模块Socket.IO(一个Websocket库)。它非常容易使用和岩石。 Socket.IO还可以通过可选的redis后端扩展到多个聊天服务器,这意味着它更容易扩展。

尝试将$_SESSION与静态会话ID一起用作通信通道并不是解决方案,因为PHP会将会话数据保存到文件中。

答案 7 :(得分:1)

最好使用node.js服务器。 WebSockets现在不是跨浏览器(除了适用于node.js的socket.io之外)

答案 8 :(得分:1)

如果您对PHP有业务需求,那么在混合中添加另一种语言只会意味着您有两个问题。

完全有可能运行一个永久性的,经常运行的daemonised PHP IRCd服务器:我知道,因为我已经完成了它,制作了一个可以运行多年的在线游戏。

我使用的IRC服务器部分是WaveIRCd的修改版本: http://sourceforge.net/projects/waveircd/

我使用我在这里提供的代码守护它: http://www.thudgame.com/node/254

那段代码可能有些过分:我把它写得尽可能坚固,所以它尝试使用PHP的pcntl_fork()进行守护,然后回退到在后台递归调用自身,然后回到perl,等等on:它还处理PHP安全模式的安全限制,以防有人打开它,并通过cron调用安全限制。

你可能会把它简化为几行:带有注释“守护程序规则......”的位 - 遵循这些规则,你就可以很好地保护你的过程。

为了处理任何意外的守护进程死亡等,然后我每分钟通过cron运行该守护进程,在那里它检查守护进程是否已经运行,如果是这样,要么悄然死亡,要么守护进程没有响应,杀了它并取而代之。

由于IRC的整体分布式特性,它非常坚固耐用,并且给了我一个多人浏览器游戏,几年前没有停机几年,直到几个月前吃点腐烂。我应该尝试重写Flash中的前端,并在某天有空时再次重新启动...

(然后我为PHP机器人运行另一个daemonizer来管理游戏本身,然后让我的游戏作为java applet连接到它,并与机器人交谈以玩游戏,但这在这里无关紧要。)

由于不再维护WaveIRCd,因此可能值得寻找是否有其他人分叉项目并支持它。

[编辑:那就是说,如果你想要你的前端是HTML5 / Javascript,或者如果你想通过HTTP连接的同一个端口连接,那么你的选择比使用Flash或Java时更有限。在这种情况下,请接受其他人的建议,并使用“WebSockets”(在大多数当前浏览器中支持不佳)或“Socket.io”项目(使用WebSockets,但可以回退到Flash或其他各种方法,具体取决于浏览器可用)。

以上是主机允许在另一个端口上运行服务的情况。特别是,许多人在他们的ToS中有明确的规则来反对运行IRCd。]

答案 9 :(得分:0)

简而言之,你不能。 当前的HTTP / HTML实现不支持pushstate,因此聊天应用的算法应该遵循:

  1. A:已发送消息
  2. B,C,D:在发送新消息时执行此消息。
  3. 因此接收者总是必须发出新请求并检查是否已发送新消息。 (AJAX Call或类似的东西) 所以发送的事件和接收事件之间总是存在延迟。

    • 表示数据必须保存在全局内容中,如db或file system。

    看看: http://today.java.net/article/2010/03/31/html5-server-push-technologies-part-1

答案 10 :(得分:0)

实现这一目标的一个解决方案是编写PHP套接字服务器。

<?php

// Set time limit to indefinite execution

set_time_limit (0);

// Set the ip and port we will listen on

$address = '192.168.0.100';

$port = 9000;

$max_clients = 10;

// Array that will hold client information

$clients = Array();

// Create a TCP Stream socket

$sock = socket_create(AF_INET, SOCK_STREAM, 0);

// Bind the socket to an address/port

socket_bind($sock, $address, $port) or die('Could not bind to address');

// Start listening for connections

socket_listen($sock);

// Loop continuously

while (true) {

    // Setup clients listen socket for reading

    $read[0] = $sock;

    for ($i = 0; $i < $max_clients; $i++)

    {

        if ($client[$i]['sock']  != null)

            $read[$i + 1] = $client[$i]['sock'] ;

    }

    // Set up a blocking call to socket_select()

    $ready = socket_select($read,null,null,null);

    /* if a new connection is being made add it to the client array */

    if (in_array($sock, $read)) {

        for ($i = 0; $i < $max_clients; $i++)

        {

            if ($client[$i]['sock'] == null) {

                $client[$i]['sock'] = socket_accept($sock);

                break;

            }

            elseif ($i == $max_clients - 1)

                print ("too many clients")

        }

        if (--$ready <= 0)

            continue;

    } // end if in_array



    // If a client is trying to write - handle it now

    for ($i = 0; $i < $max_clients; $i++) // for each client

    {

        if (in_array($client[$i]['sock'] , $read))

        {

            $input = socket_read($client[$i]['sock'] , 1024);

            if ($input == null) {

                // Zero length string meaning disconnected

                unset($client[$i]);

            }

            $n = trim($input);

            if ($input == 'exit') {

                // requested disconnect

                socket_close($client[$i]['sock']);

            } elseif ($input) {

                // strip white spaces and write back to user

                $output = ereg_replace("[ \t\n\r]","",$input).chr(0);

                socket_write($client[$i]['sock'],$output);

            }

        } else {

            // Close the socket

            socket_close($client[$i]['sock']);

            unset($client[$i]);

        }

    }

} // end while

// Close the master sockets

socket_close($sock);

?>

您可以通过命令行运行它来执行此操作,并且必须始终为您的PHP客户端运行以连接它。然后,您可以编写一个连接到套接字的PHP客户端。

<?php
$fp = fsockopen("www.example.com", 80, $errno, $errstr, 30);
if (!$fp) {
    echo "$errstr ($errno)<br />\n";
} else {
    $out = "GET / HTTP/1.1\r\n";
    $out .= "Host: www.example.com\r\n";
    $out .= "Connection: Close\r\n\r\n";
    fwrite($fp, $out);
    while (!feof($fp)) {
        echo fgets($fp, 128);
    }
    fclose($fp);
}
?>

你必须使用某种类型的ajax来调用jQuery将消息发布到这个PHP客户端。

http://devzone.zend.com/209/writing-socket-servers-in-php/ http://php.net/manual/en/function.fsockopen.php

答案 11 :(得分:0)

你没有说它必须所有写成PHP:)

安装RabbitMQ,然后使用基于websockets和RabbitMQ构建的聊天实现。

你的PHP几乎就是'聊天室铬'。您的大多数网站都可能符合离线HTML5内容的5兆额限制,并且您可以非常灵活(并且可能比您自己做的更健壮)聊天系统。

如果您离开房间,它甚至会有20条聊天记录消息。

https://github.com/videlalvaro/rabbitmq-chat

答案 12 :(得分:-1)

如果您只需要使用PHP,那么您可以在会话变量中存储聊天消息,会话可以像对象一样,存储大量信息。 如果您可以使用jQuery,那么您可以在发送消息之后将段落附加到div,但是如果刷新站点,则消息将消失。 或者组合,在会话中存储消息并使用jQuery和ajax更新它。

答案 13 :(得分:-1)

尝试查看像ZeroMQ这样的套接字库,它们允许即时传输消息,并且比TCP更快,并且是实时的。它们的基础设施允许在A点和B点之间发送即时数据,而不会将数据存储在任何地方(尽管您仍然可以选择)。 这是tutorial for a chat client in ZeroMQ

相关问题