PHP Unconference Europe 2015

socket_accept

(PHP 4 >= 4.1.0, PHP 5)

socket_acceptAcepta una conexión de un socket

Descripción

resource socket_accept ( resource $socket )

Después de que se haya creado el socket socket usando socket_create(), vinculado a un nombre con socket_bind(), e indicado que escuche conexiones con socket_listen(), esta función aceptará conexiones entrantes sobre ese socket. Una vez que se realice una conexión con éxito, se devolverá un nuevo recurso socket, que puede ser usado para comunicación. Si hay múltiples conexiones en cola sobre el socket se usará el primero. Si no hay conexiones pendientes, socket_accept() bloqueará hasta que que esté presente una conexión. Si socket ha sido hecho como de no-bloqueo usando socket_set_blocking() o socket_set_nonblock(), se devolverá FALSE.

El recurso socket devuelto por socket_accept() puede no ser usado para aceptar nuevas conexiones. Sin embargo, el socket de escucha original, socket, permanece abierto y puede ser reutilizado.

Parámetros

socket

Un recurso socket válido creado con socket_create().

Valores devueltos

Devuelve un nuevo recurso socket en caso de éxito, o FALSE en caso de error. El código de error real se puede recuperar llamanado a socket_last_error(). Este código de error se puede pasar a socket_strerror() para obtener una explicación textual del error.

Ver también

add a note add a note

User Contributed Notes 6 notes

up
15
lars at opdenkamp dot eu
6 years ago
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();

    }
}

?>
up
2
gmkarl at gmail dot com
7 years ago
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.
up
5
Boylett
6 years ago
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;
    }
}

?>
up
2
Greg MacLellan
10 years ago
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.
up
0
mightyplow at gmail dot com
5 months ago
In order to make many clients connectable to the server, you have to set the accepted socket also to non-blocking. Otherwise the accepted connection will block further incoming connections.

while (true) {
    $newSock = @socket_accept($sock);

    if ($newSock) {
        echo 'client connected';

        socket_set_nonblock($newSock);
    }
}
up
-2
simon at 180solutions dot com
9 years ago
>Accepting a connection using php-sockets:
>
>$fd = socket_create(AF_INET, SOCK_STREAM, 6 /* OR >getprotobyname("TCP")*/);
>
>$PORT = 5000;
>
>socket_bind($fd, "0.0.0.0", $PORT);
>
>while(true)
>{
>$remote_fd = socket_accept($fd);
>
>remote_socket_client_handle($remote_fd);
>
>}
>
>It is simple!

This example doesn't work. You have to call socket_listen($fd) after your bind in order to accept incoming connections.

Simon
To Top