socket_select 立即返回 false,但没有错误代码。

4
我正在使用 `socket_select`,但在某个主机上,该函数会发生奇怪的事情:
  • 立即返回而不是等待5秒
  • 返回 false,表明存在某些错误
  • 但是 `socket_last_error()` 返回 0(表示成功)。
此服务器的 `phpinfo()` 信息: http://jsfiddle.net/Lmrfe/embedded/result/
$server = socket_create( AF_UNIX, SOCK_STREAM, 0 );
$r = socket_bind( $server, '/some/file/somewhere');
$r = socket_listen( $server );
// none of the above socket_* returns false

$t = microtime(true);
socket_clear_error();

$read = array( $server ); $write = null; $except = null;
$read = array( $server ); $write = array(); $except = array();
$read = array(); $write = array(); $except = array();
$read = null; $write = null; $except = null;

$changed = socket_select( $read, $write, $except, 5,0 );
$changed = socket_select( $read, $write, $except, null );
$changed = socket_select( $read, $write, $except, 5000000 );
$changed = socket_select( $read, $write, $except, 5000000, 0 );
$changed = socket_select( $read, $write, $except, 5000000, 5000000 );

/* Results:

   microtime(true) - $t  == almost zero

   $changed === false

   socket_last_error() === 0
   socket_strerror(socket_last_error()) === Success

   $read === array(1) {
     [0]=>
     resource(2) of type (Socket)
   }
   $write  === NULL
   $except === NULL
*/

$s = socket_read( $server, 1024, PHP_BINARY_READ );
// $s === false

这里发生了什么?
更新的测试脚本:仍然在瞬间运行:
header('Content-Type: text/plain; charset=utf-8');
error_reporting(-1);

for( $i = 0; $i < 4; $i++ ){
for( $j = 0; $j < 5; $j++ ){
  echo "\n\n\n\n\n[i,j]=[{$i},{$j}]\n";

  $socket = socket_create( AF_UNIX, SOCK_STREAM, 0 );
  var_dump('socket_create', $socket ); echo "\n"; if( $socket === false ) continue;

  $socket_file = dirname(__FILE__)."/test_socket_i{$i}_j{$j}";
  if( file_exists( $socket_file )) unlink( $socket_file );

  $r = socket_bind( $socket, $socket_file );
  var_dump('socket_bind', $r ); echo "\n"; if( $r === false ) continue;

  $r = socket_listen( $socket );
  var_dump('socket_listen', $r ); echo "\n"; if( $r === false ) continue;

  $t = microtime(true);
  socket_clear_error();

  if($i==0){ $read = null;             $write = null;    $except = null; }
  if($i==1){ $read = array();          $write = array(); $except = array(); }
  if($i==2){ $read = array( $socket ); $write = null;    $except = null; }
  if($i==3){ $read = array( $socket ); $write = array(); $except = array(); }

  if($j==0){ $changed = socket_select( $read, $write, $except, 5,0 ); } // 5 seconds
  if($j==1){ $changed = socket_select( $read, $write, $except, null ); } // forever
  if($j==2){ $changed = socket_select( $read, $write, $except, 5000000 ); }
  if($j==3){ $changed = socket_select( $read, $write, $except, 5000000, 0 ); }
  if($j==4){ $changed = socket_select( $read, $write, $except, 5000000, 5000000 ); }

  var_dump('•socket_select returned:', $changed );echo "\n";
  var_dump('•$read/$write/$except:',$read,$write,$except);echo "\n";
  var_dump('•error:', socket_last_error(), socket_strerror(socket_last_error()) );echo "\n";
  var_dump('time: ', microtime(true) - $t );echo "\n";               // almost zero
}}
4个回答

3
您可能希望使用流(stream)_*函数而不是socket。

流(stream)函数更通用,是PHP核心的一部分,而Socket支持需要安装。流(stream)函数基本上给您更多的控制权。

http://www.php.net/manual/zh/intro.stream.php


3
请查看此链接:http://php.net/manual/zh/function.socket-select.php 由于当前Zend Engine的限制,无法直接将常量修饰符(如NULL)作为参数传递给期望通过引用传递此参数的函数。相反,请使用临时变量或带有最左成员为临时变量的表达式:
因此,问题在于您正在初始化$write = null

嗨,我做的和你引用部分下面的例子一模一样:$e = NULL; socket_select($r, $w, $e, 0); 我认为这是“禁止”的语法:socket_select($r, null, null, 0); - biziclop
虽然没有任何答案对这个错误进行了解释,但我还是授予了你赏金,因为你的声望最低。把它当作一个欢迎礼物 :) - biziclop

2
正如@Amez所指出的,您不应将空值传递给select。相反,为未使用的socket_select()参数创建一个空数组:
$read    = array( $server );
$write   = array();
$except  = array();
$changed = socket_select( $read, $write, $except, 5,0 );

最近我在Python中遇到了同样的问题(我不知道为什么PHP/Python不会在内部将null值替换为空数组,对于select来说,这显然是期望的行为)。


谢谢您的回答,我已经更新了我的问题,并进行了更广泛的测试,仍然没有等待。服务器可能以某种方式配置不正确。在我的实际代码中,我已经创建了一个简单的自适应等待解决方案,以防止100%的CPU使用率。嗯,没有什么是完美的(尤其是PHP :)。 - biziclop

0
如果你遇到这个 bug,你可以使用类似的方法来解决它:
$socket_select_timeout_seconds = 5;

while(true){
  $time_start = microtime(true);
  $c = socket_select($r,$w,$e, $socket_select_timeout_seconds );

  if( /* something happened */ ){
    // handle socket events
  }else if( microtime(true) - $time_start < 0.01){
    // nothing happened, but buggy socket_select didn't wait
    // wait manually
    usleep( $socket_select_timeout_seconds * 1000000 );
  }

}

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