我正在 PHP 中使用流式套接字从远程服务器读取数据。当远程服务器在连接后消失时,stream_select 继续在流的读取部分显示更改的流,但读入的数据是空白字符串。
这是一个重现该错误的小案例。它由两个组件组成,服务器组件和客户端组件。
为了复制该错误,您需要从命令行使用 php 执行以下操作:
1.启动server.php
2.启动client.php
此时,服务器应显示“按回车键继续...或 CTRL-C”,客户端应显示“终止您的服务器”。按回车键继续....'
- Ctrl-c server.php
- 在 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/