如何通过stream_get_contents()连续监听多个TCP流


How to listen muliple TCP streams continously by stream_get_contents()?

默认情况下,stream_get_contents等待并侦听60秒。如果我有多个流,并且想连续地听所有的流。

在foreach内部,如果我在听一条流,我就听不到其他流。

连续监听和捕获所有流的流输出的解决方案是什么?

while(true){
   //$streamArray is an array of streams obtained by stream_socket_client("tcp://..);
   foreach($streamArray as $stream){
        fputs($stream,$command);
        stream_get_contents($stream);  // and update file/DB (Blocking call)
        }
    }

注意:对于每个流,我已经完成了stream_set_blocking( $stream , true );

更新:

我的要求是听所有的流一段时间,比如30分钟。同时我听不到2流。如果我有5个流,我的代码只有time division multiplexing,在30分钟中,每个单独的流将只记录6分钟的

我有一个解决方案,让AJAX请求单独的流和记录独立。当然,我不想做这种多AJAX调用方法,因为它会产生更多的代码和CPU。

使用stream_set_blocking($resource, true),您开始同步读取流,这意味着每次对fread()的调用都要等待,直到有数据要读取。然后调用stream_get_contents(),它从阻塞流中读取,直到它到达EOF(关闭)。结果是你读了一个又一个流,而不是"读";同时";。

以这种方式读取流是常见的,也是最容易编码的,但当你想同时处理多个流时,你必须处理缓冲区、定时和";流的末端";检测自己。在您当前的代码中,这一部分是通过阻塞流和stream_get_contents()从您那里抽象出来的。

要同时读取多个流,您应该重写代码以异步读取流。

// $streamArray is an array of streams obtained by stream_socket_client("..");
$buffers = array();
$num_streams = 0;
foreach($streamArray as $stream) {
    // set streams non-blocking for asynchroneous reading
    stream_set_blocking($stream, false);
    // write command to all streams - multiplexing
    fputs($stream, $command);
    // initialize buffers
    $buffers[$stream] = '';
    $num_streams++;
}
while($num_streams > 0) {
    // use stream_select() to wait for new data and not use CPU at 100%.
    // note that the stream arrays are passed by reference and are modified
    // by stream_select() to reflect which streams are modified / have a
    // new event. Because of this we use a copy of the original stream array.
    // also note that due to a limitation in ZE you can not pass NULL directly
    // for more info: read the manual    https://php.net/stream_select
    $no_stream = NULL;
    $select_read = $streamArray;
    stream_select($select_read, $no_stream, $no_stream, null);
    // if there is new data, read into the buffer(s)
    foreach($select_read as $stream) {
        $buffers[$stream] .= fread($stream, 4096);
        // check if the stream reached end
        if (feof($stream)) {
            $key = array_search($stream, $streamArray);
            // close stream properly
            $num_streams--;
            fclose($stream);
            // remove stream from array of open streams
            unset($streamArray[$key]);
        }
    }
}
// do something with your buffers - our use them inside the while() loop already
print_r($buffers);

PHP在服务器上作为单个进程运行。这样做的"最好"方法是为每个流生成一个线程。每个线程都有一个可监听的流。多线程自动利用多个CPU,并允许它们并行运行。

不幸的是,PHP不允许创建线程。然而,有几个解决办法的选择(注意:这个问题可能对你有一些好处)。

选项1:克隆进程

PHP允许您使用pcntl_fork等函数克隆当前进程。基本上,您可以创建当前PHP进程的副本,该进程将接管并开始运行。只需对每个需要侦听的流执行此操作。复杂的问题是确保每个进程都侦听正确的流。(提示:有一个流的列表,并使用一个标志到列表中的下一个流。然后执行fork〔克隆进程〕。子进程将开始侦听该流。但是,在父进程中,将标志更新到列表中下一个流量,并创建另一个子进程,等等…)

选项2:多个PHP实例

你可以使用一个Cronjob、多个调用或其他服务(甚至是命令行调用)来调用每个PHP脚本,或者向脚本传递一个参数来确定要监听哪个流。这将并行运行每个PHP脚本/文件,并实现你想要的。

选项3:不使用PHP

从技术上讲,PHP并不是为这样的东西而构建的,这可能很容易在C、C++或Java中实现。你应该重新考虑你的设置。

选项4:创建PHP或Apache模块

您可以用C为PHP或Apache编写一个模块,然后在PHP中使用函数调用。这可能会非常复杂,我不建议这样做

总而言之,我建议重新考虑您的设置,不要使用PHP。但是,如果您仅限于PHP,那么可以进行多次AJAX调用。

连续监听和捕获所有流的流输出的解决方案是什么?

这个问题困扰程序员很长一段时间了。如果你想为你的问题找到一个像样的解决方案,那么W·理查德·史蒂文斯写的一本名为《UNIX网络编程》的书将是一个很好的起点。