If you want to make a daemon process that forks on each client connection, you'll find out that there's a bug in PHP. The child processes send SIGCHLD to their parent when they exit but the parent can't handle signals while it's waiting for socket_accept (blocking).
Here is a piece of code that makes a non-blocking forking server.
#!/usr/bin/php -q
<?php
/**
* Listens for requests and forks on each connection
*/
$__server_listening = true;
error_reporting(E_ALL);
set_time_limit(0);
ob_implicit_flush();
declare(ticks = 1);
become_daemon();
/* nobody/nogroup, change to your host's uid/gid of the non-priv user */
change_identity(65534, 65534);
/* handle signals */
pcntl_signal(SIGTERM, 'sig_handler');
pcntl_signal(SIGINT, 'sig_handler');
pcntl_signal(SIGCHLD, 'sig_handler');
/* change this to your own host / port */
server_loop("127.0.0.1", 1234);
/**
* Change the identity to a non-priv user
*/
function change_identity( $uid, $gid )
{
if( !posix_setgid( $gid ) )
{
print "Unable to setgid to " . $gid . "!\n";
exit;
}
if( !posix_setuid( $uid ) )
{
print "Unable to setuid to " . $uid . "!\n";
exit;
}
}
/**
* Creates a server socket and listens for incoming client connections
* @param string $address The address to listen on
* @param int $port The port to listen on
*/
function server_loop($address, $port)
{
GLOBAL $__server_listening;
if(($sock = socket_create(AF_INET, SOCK_STREAM, 0)) < 0)
{
echo "failed to create socket: ".socket_strerror($sock)."\n";
exit();
}
if(($ret = socket_bind($sock, $address, $port)) < 0)
{
echo "failed to bind socket: ".socket_strerror($ret)."\n";
exit();
}
if( ( $ret = socket_listen( $sock, 0 ) ) < 0 )
{
echo "failed to listen to socket: ".socket_strerror($ret)."\n";
exit();
}
socket_set_nonblock($sock);
echo "waiting for clients to connect\n";
while ($__server_listening)
{
$connection = @socket_accept($sock);
if ($connection === false)
{
usleep(100);
}elseif ($connection > 0)
{
handle_client($sock, $connection);
}else
{
echo "error: ".socket_strerror($connection);
die;
}
}
}
/**
* Signal handler
*/
function sig_handler($sig)
{
switch($sig)
{
case SIGTERM:
case SIGINT:
exit();
break;
case SIGCHLD:
pcntl_waitpid(-1, $status);
break;
}
}
/**
* Handle a new client connection
*/
function handle_client($ssock, $csock)
{
GLOBAL $__server_listening;
$pid = pcntl_fork();
if ($pid == -1)
{
/* fork failed */
echo "fork failure!\n";
die;
}elseif ($pid == 0)
{
/* child process */
$__server_listening = false;
socket_close($ssock);
interact($csock);
socket_close($csock);
}else
{
socket_close($csock);
}
}
function interact($socket)
{
/* TALK TO YOUR CLIENT */
}
/**
* Become a daemon by forking and closing the parent
*/
function become_daemon()
{
$pid = pcntl_fork();
if ($pid == -1)
{
/* fork failed */
echo "fork failure!\n";
exit();
}elseif ($pid)
{
/* close the parent */
exit();
}else
{
/* child becomes our daemon */
posix_setsid();
chdir('/');
umask(0);
return posix_getpid();
}
}
?>socket_accept
Почист и полокален преглед на PHP референцата, со задржана структура од PHP.net и подобра читливост за примери, секции и белешки.
socket_accept
Референца за `function.socket-accept.php` со подобрена типографија и навигација.
socket_accept
(PHP 4 >= 4.1.0, PHP 5, PHP 7, PHP 8)
socket_accept — Прифаќа врска на сокет
= NULL
Откако сокетот socket е креиран со користење на socket_create(), поврзан со име со
socket_bind(), и му е кажано да слуша за врски со socket_listen(), оваа функција ќе прифати дојдовни врски на тој сокет. Откако ќе се воспостави успешна врска, нов Сокет инстанца се враќа, која може да се користи за комуникација. Ако има повеќе врски во ред на сокетот, ќе се користи првата. Ако нема чекачки врски, socket_accept() ќе блокира додека не се појави врска. Ако socket
е направен неблокирачки со користење на
socket_set_blocking() or
socket_set_nonblock(), false ќе биде вратено.
На Сокет инстанца вратена од
socket_accept() не може да се користи за прифаќање нови врски. Оригиналниот сокет за слушање
socket, сепак, останува отворен и може повторно да се користи.
Вратени вредности
параметарот специфицира само излезна кодировка. Ако се помине празен стринг, парсерот се обидува да идентификува во која кодировка е документот кодиран со гледање на првите 3 или 4 бајти. Стандардната излезна кодировка е UTF-8. Поддржаните кодировки се Сокет инстанца при успех, или false Протоколот за контрола на пренос (TCP) е сигурен, базиран на врска, ориентиран кон стриминг, протокол со целосен дуплекс. TCP гарантира дека сите пакети со податоци ќе бидат примени по редоследот по кој биле испратени. Ако некој пакет некако се изгуби за време на комуникацијата, TCP автоматски ќе го пренесе пакетот додека домаќинот на дестинацијата не го потврди тој пакет. Од причини за сигурност и перформанси, самата имплементација на TCP одлучува за соодветните граници на октети на основниот слој за комуникација на датаграми. Затоа, апликациите на TCP мора да дозволат можност за пренос на делумни записи.
socket_last_error()ако имало грешка. Вистинскиот код за грешка може да се добие со повикување на
socket_strerror() . This code may be passed to
Дневник на промени
| Верзија | = NULL |
|---|---|
| 8.0.0 | При успех, оваа функција враќа Сокет инстанца сега; претходно, а resource . |
Види Исто така
- socket_connect() - Тестира за крај на датотеката на покажувач на датотека
- socket_listen() - Binds a name to a socket
- socket_create() - Креирај сокет (крајна точка за комуникација)
- socket_bind() IPC пример
- socket_strerror() - Reads a maximum of length bytes from a socket
Белешки од корисници 5 белешки
If you want to have multiple clients on a server you will have to use non blocking.
<?php
$clients = array();
$socket = socket_create(AF_INET,SOCK_STREAM,SOL_TCP);
socket_bind($socket,'127.0.0.1',$port);
socket_listen($socket);
socket_set_nonblock($socket);
while(true)
{
if(($newc = socket_accept($socket)) !== false)
{
echo "Client $newc has connected\n";
$clients[] = $newc;
}
}
?>The socket returned by this resource will be non-blocking, regardless of what the listening socket is set to. This is actually true for all FCNTL modifiers.<explain>
This case is a very simple interaction between the TCP client side and the TCP server side
<?php
// create for simple-tcp-server
$sock = socket_create(AF_INET, SOCK_STREAM, getprotobyname('tcp'));
socket_bind($sock, '127.0.0.1',5000);
socket_listen($sock,1);
$clnt_sock = socket_accept($sock); //阻塞
$st = "hello world ^_^";
socket_write($clnt_sock, $st,strlen($st));
socket_close($clnt_sock);
socket_close($sock);
?>
<?php
//create for simple-tcp-client
$clnt_sock = socket_create(AF_INET, SOCK_STREAM, getprotobyname('tcp'));
socket_connect($clnt_sock, '127.0.0.1', 5000);
$ret= socket_read($clnt_sock, 1024);
print "from simple-tcp-server:".$ret.PHP_EOL;
socket_close($clnt_sock);
?>
<fruit>
from simple-tcp-server:hello world ^_^Be aware signal handler functions set with pcntl_signal are not called while a socket is blocking waiting for a connection; the signal is absorbed silently and the handler called when a connection is made.