通过Websockets发送popen的输出

17

我正在使用popen和fgets异步读取tcpdump的输出。

以下代码应该在命令行中运行,而不是在apache中并在浏览器中查看它。

$handle = popen('tcpdump -nnX', 'r');

while (true) {
    $output = fgets($handle);
    print $output . "\n";
}

当我尝试通过websockets输出这些信息时问题就出现了。

Websockets也使用无限循环(用于管理其套接字、时钟和消息)。

它看起来像:

while (true) {
    @socket_select($read,$write,$except,1);
    foreach ($read as $socket) {
        if ($socket == $this->master) {
            $client = socket_accept($socket);
...

我使用 $websocket->sendToAll($message); 通过 websocket 发送数据。

  • 我不能把 while 循环放在一起,因为只会运行我先放置的循环,while (true) { A() }; while (true) { B() }; B() 永远不会被调用。

  • 我也不能合并 while 循环,因为 websocket 会减慢 popen 的读取速度,反之亦然。while (true) { A(); B(); } 如果B花费了很长时间才能完成,A将变得缓慢。

在这种情况下,我该怎么办?我可以尝试线程、分支脚本间的通信或其他任何方法。


不好,它让我的浏览器崩溃了,还让资源管理器疯狂,一定要加个警告 >.< - Craig van Tonder
@IndigoIdentity已添加。我的问题仅关注cli,这里没有涉及浏览器。(除了使用websockets进行查看,但该代码在此处被省略) - Dave Chen
好的,这部分是我的错,因为我之前正在通过套接字ping IP,并将输出放入浏览器中,所以我已经在思维上根深蒂固了。现在想想,在PHP中使用异步套接字连接会很棒。好问题! - Craig van Tonder
我认为可以通过使用异步Websockets或异步shell_exec解决这个问题。这个不考虑输出。由于这个,我可能会使用node.js。 - Dave Chen
这很有趣:https://segment.com/blog/how-to-make-async-requests-in-php/ - Craig van Tonder
2个回答

7
这是一个典型的“生产者-消费者”问题场景,只不过有两个队列。为了更容易理解这个问题,可以将其拆分为以下几个部分:
  • WebSocket消费者:该代码将通过WebSocket发送数据。您可以将其视为一个独立的线程,在该线程中从Q1(仅名称)中出列并发送数据。

  • WebSocket生产者:一旦某些数据到达WebSocket门,它就会被排入缓冲区中。它只是与上面不同的另一个队列而已。让我们将其命名为Q2。这也需要是一个单独的线程,并且在它排队并通知适当的消费者后,该线程将休眠。

  • HDD消费者:该代码将执行与WebSocket消费者相同的操作,唯一的区别是它将数据存储在硬盘而不是WebSocket上。它将拥有自己的线程,与Q2一起工作。

  • HDD生产者:我相信您可以猜到它的作用。此代码将从硬盘中读取数据并将其放入Q1队列中。与所有生产者一样,它需要通知其消费者有新的队列项可用。

现在回到您的代码,虽然完全可能,但PHP不适合多线程编程。这就是为什么您找不到那么多示例的原因。但是,如果您坚持,以下是您需要的:

  1. PHP的Thread类

  2. PHP的Mutex类。该类将帮助您防止多个线程同时访问同一数据。

  3. 称为Signaling的东西,在PHP中无法找到!它用于告诉其他线程有些数据准备好了可以被使用,或者换句话说,当它有事可做时,它会唤醒消费者线程。

最后一个要点是,在正确的多线程软件中,您不会使用sleep函数来降低系统的负载/防止系统崩溃。多线程编程全部都是关于线程之间信号和对话的。


POSIX信号在PHP中的应用:https://secure.php.net/manual/en/function.pcntl-signal.php -- 这个函数定义了一个信号处理器。 - Ghedipunk
@Ghedipunk 谢谢,但我认为这些函数是用于进程间通信而不是线程间通信的。由于我们正在讨论多线程,整个脚本将在一个进程中运行,因此使用PCNTL函数暂停一个线程应该会暂停进程中的所有线程。我必须承认我自己还没有尝试过,这只是我的想法。 - Mehran
不错的观点,我也没有在多线程环境下尝试过。至少可以尝试一下。我必须承认,个人更喜欢使用分叉而避免使用多线程。 - Ghedipunk
1
我相信你知道多进程与多线程编程的优缺点。但为了记录,多线程中共享内存很容易实现,因为所有线程都在同一上下文中。但在多进程编程中,即使你有共享内存,但它并不像多线程那样容易实现。 - Mehran

2
如何使用 wscat?以下是命令行:
$ printf "hello\\nbye\\n^C" | wscat -c ws://echo.websocket.org

将以下两行发送到ws://echo.websocket.org
hello
bye

请注意,在命令行中,^C 表示 Control-C(而不是由 ^C 组成的两个字母)。

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