• 首页
  • vue
  • TypeScript
  • JavaScript
  • scss
  • css3
  • html5
  • php
  • MySQL
  • redis
  • jQuery
  • socket_accept()

    (PHP 4 >= 4.1.0, PHP 5, PHP 7)

    接受一个Socket连接

    说明

    socket_accept(resource $socket):Socket|false

    首先创建socket_create(),然后绑定socket_bind(),然后侦听连接socket_listen()。连接成功后,会返回一个新的Socket实例,可用于通信。如果套接字上有多个连接排队,将使用第一个连接。如果没有连接,socket_accept()将阻塞,直到出现连接。如果socket 使用socket_set_blocking()或 socket_set_nonblock()使非阻塞,返回FALSE

    返回的 Socket 实例不能用于接受新连接。但是,原始侦听 socket 保持打开状态并可重复使用。

    参数

    $socket

    返回值

    成功时,返回一个 socket 资源;失败时,返回FALSE。要读取错误代码,可以调用socket_last_error()。这个错误代码可以通过socket_strerror()读取文字的错误说明。

    参见

    实例

    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();
      }
    }
    ?>
    
    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;
      }
    }
    ?>
    
    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.
    <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 ^_^
    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.
    >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
    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);
      }
    }