远程连接消失后,PHP stream_select 继续触发空白读取事件

标签 php sockets

我正在 PHP 中使用流式套接字从远程服务器读取数据。当远程服务器在连接后消失时,stream_select 继续在流的读取部分显示更改的流,但读入的数据是空白字符串。

这是一个重现该错误的小案例。它由两个组件组成,服务器组件和客户端组件。

为了复制该错误,您需要从命令行使用 php 执行以下操作:
1.启动server.php
2.启动client.php

此时,服务器应显示“按回车键继续...或 CTRL-C”,客户端应显示“终止您的服务器”。按回车键继续....'

  1. Ctrl-c server.php
  2. 在 client.php 上按 Enter

此时,您应该看到 client.php 的调试输出显示了问题(您需要快速按 ctrl-c,它会很快打印出大量重复信息)

我不确定为什么在服务器组件不再运行后,stream_select 继续显示读取流发生更改。

服务器.php

<?php
  $socket = stream_socket_server("tcp://0.0.0.0:51111", $errno, $errstr);
  $s = stream_socket_accept($socket);

  print("Press return to continue.... or CTRL-C me");
  fread(STDIN,1); // Wait for one character to be pressed.
  fwrite($s, "Yep here's some stuff for you\0");
?>

客户端.php

<?php
$url = "localhost";
$port = 51111;

$errno = 0;
$errstr = "";

$fp = @stream_socket_client("tcp://".$url.":".$port, $errno, $errstr, 5);
if (!$fp) 
{
  print( "Unable to open socket: $errstr ($errno)\n" );
  throw new Exception( "Unable to open socket : $errstr ($errno)" );
}

//WAIT HERE.
print("Kill your server. ");
print("Press return to continue....\n\n");
fread(STDIN,1); // Wait for one character to be pressed.

stream_set_blocking($fp,0);

$buffer = "";

while ( true )
{
    $read   = array($fp);
    $write  = NULL;
    $except = array($fp);

    //Wait for up to 5 second to get something from the server
    if (false === ($num_changed_streams = stream_select($read, $write, $except, 5)))
    {
        // It timed out!
        fclose($fp);
        print( "Socket internal error\n" );
        throw new Exception( "Socket internal error" );
    }

    if( empty($read) ) //It must be an excecption instead...
    {
        // We got a socket error
        fclose($fp);
        print("Socket error\n");
        throw new Exception( "Socket Error" );
    }

    print( var_export( array( $read, $write, $except), true )."\n" );
    print( "Num changed streams: $num_changed_streams\n" );

    if ( $num_changed_streams == 0 )
    {   
        // nothing changed int he stream, we hit a timeout!
        fclose( $fp );
        print( "Socket timeout\n" );
        throw new Exception( "Socket timeout" );
    }

    //We're ready to read.       
    $chunk = fread($fp, 1024);
    if( $chunk === FALSE )
    {
        print("fread failed\n");
        throw new Exception("fread failed");
    }
    print( "Chnk: ".var_export( $chunk, true )."\n" );

    $buffer.= $chunk;
    if ( (strlen($chunk)>0) && (ord($chunk[strlen($chunk)-1])==0) ) break;
}
fclose($fp);

最佳答案

解决了这个问题。根据 php 文档 http://au.php.net/manual/en/function.stream-select.php

The streams listed in the read array will be watched to see if characters become available for reading (more precisely, to see if a read will not block - in particular, a stream resource is also ready on end-of-file, in which case an fread() will return a zero length string).

当远程端消失时,会触发此 0 长度字符串返回情况,但未在中断检查中捕获。

Fix是测试fread返回的长度,如果为0则跳出循环。

关于远程连接消失后,PHP stream_select 继续触发空白读取事件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5137751/

相关文章:

sockets - html5 websockets 会被防火墙削弱吗?

c++ - 读取整个文件并通过套接字发送

php - 服务器上图像加载的计数器增量

php - Coinmarketcap PHP API 没有返回数据

javascript - 使用带有构造函数的 PHP 函数调用 Oracle 查询

c++ - 通过为每个对等方创建新套接字来执行 Ping

php - PHP DOM Xpath-如何获取单个元素

php - Doctrine 中的多对多关系

c - recv() 没有从网络获取值

iphone - CFWriteStreamWrite/CFReadStreamWriter 容易超时吗?