2013-10-12 22 views
1

我想创建一个简单的PHP服务器(基于TCP),它将为实际时间提供服务并立即关闭连接。我已经做到了。我想添加crtl-C处理,所以我需要用非阻塞来替换阻塞的socket_accept(这是因为当到达阻塞的socket_accept指令并且发送了SIGINT/ctrl-C /时,服务器仍然活着,直到第一个客户端是服务器,然后关闭自己 - 我不希望这种行为)。PHP socket_accept在非阻塞模式下抛出警告

我当前的代码如下所示:

<?php 

error_reporting(E_ALL); 
ob_implicit_flush(); 

if ($argc != 2) 
    die("Wrong params"); 

$address = 'localhost'; 
$port = $argv[1]; 

if (($sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) === false) 
    die(socket_strerror(socket_last_error()) . "\n"); 

if (socket_bind($sock, $address, $port) === false) 
    die(socket_strerror(socket_last_error($sock)) . "\n"); 

if (socket_listen($sock, 5) === false) 
    die(socket_strerror(socket_last_error($sock)) . "\n"); 

socket_set_nonblock($sock); 

$remote_host = $remote_port = $msgsock = null; 

declare(ticks = 1); 

function sig_handler($signo) 
{ 
    switch ($signo) { 
     case SIGTERM: 
     case SIGINT: 
      global $sock; 
      socket_shutdown($sock); 
      socket_close($sock); 
      echo "Terminating...\n"; 
      exit; 
    } 
} 

pcntl_signal(SIGTERM, "sig_handler"); 
pcntl_signal(SIGINT, "sig_handler"); 

echo "Starting server\n"; 

while (1) { 
    do { 
     $msgsock = socket_accept($sock); 
     usleep(100000); 
    } while ($msgsock === false); 

    socket_getpeername($msgsock, $remote_host, $remote_port); 
    echo "Connection made from {$remote_host}:{$remote_port}\n"; 

    $msg = date('r', time()) . "\n"; 
    socket_write($msgsock, $msg, strlen($msg)); 
    socket_close($msgsock); 
}; 

socket_close($sock); 

一切工作正常,除了一个细节......我得到了以下PHP警告每0.1秒(= 100000微秒):

PHP Warning: socket_accept(): unable to accept incoming connection [11]: Resource temporarily unavailable in /home/tomasz/Development/Python/twisted/time-server.php on line 55 
PHP Stack trace: 
PHP 1. {main}() /home/tomasz/Development/Python/twisted/time-server.php:0 
PHP 2. socket_accept() /home/tomasz/Development/Python/twisted/time-server.php:55 

我试图实现的是非阻塞接受:PHP使用服务器套接字,检查是否有任何连接等待服务。我不 - 等待0.1秒。如果有未决连接,请提供。所有的功能都可以,除非我不知道为什么会抛出此警告 - 我只是想检查是否有任何连接要被提供。修改error_reportingE_ERROR使得警告安静,但我希望有一个更好的办法来解决......


编辑:

修改被甩出socket_accept($sock)@socket_accept($sock)只会suspress警告,但仍这并没有说明它为什么抛出...

回答

0

我找到你的问题,同时寻找解决方案完全相同的问题。另外,我发现其他一些帖子询问同样的事情,但也没有解决方案。有些帖子表示,没有办法阻止socket_accept()投掷警告,因为它在设计上表示“没有等待接受”。我无法证实这一点,因为它似乎没有在PHP手册页中提到socket_accept()

像你一样,我对使用任何形式的错误抑制都不满意。似乎下一个合乎逻辑的步骤是将socket_accept()换成某种条件,以便只有当我知道有连接在等待时才会执行。我找不到任何这样做。但是,当使用stream_*()函数而不是socket_*()函数时,它看起来像是存在的。所以我切换出来使用流(好像还有一些其他优点),具体如下:

if (($socket = stream_socket_server("tcp://$address:$port", $errno, $errstr)) === FALSE) { 
    die("failed to create socket: $errstr\n"); 
} 

echo "Waiting for clients to connect...\n"; 

while ($server_listening) { 
    $read = array($socket); 
    $array = array(); 

    if (stream_select($read, $array, $array, 0)) { 
     $connection = stream_socket_accept($socket, 0); 

     client_handler($socket, $connection); 
    } else { 
     usleep(100); 
    } 
} 

这似乎运作良好。如果它首先确定有连接在等待,我的脚本只会尝试接受连接。经过这个之后,我确实发现有一个等效套接字函数,socket_select(),它似乎以相同的方式工作。所以如果你想坚持socket_*()的功能,你也许可以做类似的事情。

在附注中,我很高兴我将流切换为流,因为它们看起来更容易处理。由于我的应用程序仅限于TCP,因此我似乎不会错过使用更多低级套接字获得的任何功能。

0

有这样做,它是select()。非阻塞套接字IO应该使用select(),句点。在PHP中,这意味着socket_select()。有关更多信息,请参阅有关BSD套接字的任何参考。