1

我正在将 ReactPHP 用于 TCP 侦听器组件。该组件侦听传入连接并与它们交换数据。$connections 数组随着客户端与侦听器的连接/断开而更新。

$loop = React\EventLoop\Factory::create();
$connections = [];
$socket = new React\Socket\Server($loop);
$socket->on('connection', function ($conn) use($loop, $db){
    global $connections;
    $connections[] = $conn;
    $conn->on('data', function ($data) use ($conn,$loop, $db) {
        global $connections;
        // ...
        // ...
        $conn->on('close', function ($conn) use($loop, $db){
            global $connections;
            if(($key = array_search($conn, $connections, true)) !== FALSE) {
                unset($connections[$key]);
            }   
        });
});
$socket->listen(16555, '127.0.0.1');
$loop->run();

如果客户端通过 telnet 连接,将发出“关闭”,以便我可以从 $connection 数组中删除关闭的连接。但是,一些连接到我的听众的设备也有问题。如果我关闭设备“关闭”将不会发出。我试图用定期定时器解决问题:

$loop->addPeriodicTimer(10, function () use($db, $loop){
    global $connections;
    foreach($connections as $c) {
        $remoteAddress = $c->getRemoteAddress();
        $metaData = @stream_get_meta_data($c->stream);
        if(!$metaData) {
            if(($key = array_search($c, $connections, true)) !== FALSE) {
                unset($connections[$key]);
            }   
        }
    }
});

但似乎它不够可靠。即使客户端断开连接,函数 stream_get_meta_data 也会返回有效的元数据数组。

4

1 回答 1

0

自从提出这个问题以来已经有一段时间了,但我发现对我有用的是使用 SplObjectStorage() 作为连接池。这是一个没有(外部)索引的集合。它非常适合连接。

https://www.php.net/manual/en/class.splobjectstorage.php

我认为您最初问题的根源是您正在取消设置 foreach 中的一个元素,它不会自动更新键,并且您最终可能会导致您的对象不按顺序排列。

为了在循环中使用可能要删除一个或多个元素的代码迭代集合(或数组),使用clone会更安全。

所以你的游泳池在哪里:

$connections_pool = new SplObjectStorage();

迭代你会做(根据你的原始请求)

$loop->addPeriodicTimer(10, function () use($db, $loop){
    global $connections_pool;
    foreach(clone($connections_pool) as $c) {
        $remoteAddress = $c->getRemoteAddress();
        $metaData = @stream_get_meta_data($c->stream);
        if(!$metaData) {
            $connections_pool->offsetUnset($c);            
        }
    }
});
于 2020-06-29T11:58:22.147 回答