我想创建一个简单的PHP服务器(基于TCP),它将服务于实际时间并立即关闭连接。我已经做到了。我想添加crtl-C处理,所以我需要用非阻塞替换阻塞socket_accept(这是因为当达到阻塞socket_accept指令并且我发送SIGINT / ctrl-C /然后服务器仍然存活,直到第一个客户端是服务器,然后它自己关闭 - 我不想要这种行为。)
我目前的代码如下:
<?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);
除了一个细节之外,一切正常......我每0.1秒(= 100000微秒)得到以下PHP警告:
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_reporting
修改为E_ERROR
会使警告变得安静,但我希望有更好的解决方法......
编辑:
将socket_accept($sock)
修改为@socket_accept($sock)
只会阻止警告被抛出,但仍然没有说明它被抛出的原因......
答案 0 :(得分:0)
我在寻找完全相同问题的解决方案时找到了您的问题。另外,我发现了一些其他帖子询问同样的事情,但也没有解决方案。一些帖子表明没有办法阻止socket_accept()
发出警告,因为设计上表明“没有等待连接可以接受”。我无法确认这一点,因为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,因此我似乎没有错过任何使用更多低级套接字的功能。
答案 1 :(得分:0)
有一些东西可以做到这一点,它是select()。非阻塞套接字IO应使用select(),period。在PHP中,这意味着socket_select()。有关详细信息,请参阅有关BSD套接字的任何参考。