PHP套接字accept_connection挂起

时间:2010-01-12 03:02:56

标签: php oop sockets

我最近发现PHP不仅具有fsock *功能,还具有创建服务器本身的功能。我决定尝试一下,然后想出来。现在,问题是它挂起accept_connection()(由于它正在等待连接。)我发现解决方案是使用stream_set_blocking(),正如您所看到的,我尝试过,但无济于事。

我收到一条错误消息,内容如下:

Warning: socket_set_blocking(): supplied resource is not a valid stream resource in /home/insomniaque/workspace/PHP Socket RAT/rat.class.php on line 68

我知道accept_connection()之前是问题,因为当我连接第二个连接时,它会输出数据。

<?php
/*
 * Project: iRAT
 * 
 * Created on Jan 11, 2010
 * Written by Insomniaque
 * 
 */

class rat
{
    /**
     * Holds the PHP socket handle for use within the class.
     */
    private $socket;

    /**
     * Holds an array of all the spawned sockets (child sockets) that were
     * created when a user connected to the server.
     */
    private $spawns = array ();

    /**
     * Holds the maximum number of connections.
     */
    private $maxconn;

    /**
     * Sets all of the variables required for the class and starts the socket.
     * Then it'll start looping, connecting clients and running commands.
     * 
     * @access public
     * @param $port The port to bind.
     * @param $maxconn The maximum number of client connections.
     */
    public function __construct($port = 0, $maxconn = 1)
    {
        /**
         * Check to see if the user has entered 0 as the port, and create a
         * random port, if so.
         */
        if($port == 0)
            $this->port = rand(81, 8079);
        else
            $this->port = $port;

        /**
         * Save the maximum connection number.
         */
        $this->maxconn = $maxconn;

        /**
         * Run our function to create the socket now.
         */
        if(!$this->createSocket())
        {
            echo "Failed creating or binding socket.\n";
            return false;
        }
        else
        {
            echo "Socket has been created and binded.\n";
        }

        /**
         * Turn non-blocking on so we can run multiple clients.
         */
        socket_set_blocking($this->socket, 0);

        echo "Starting the data receiving loop.\n";
        $this->startLoop();

        return true;
    }

    /**
     * This function will create the socket for later use.
     * 
     * @access private
     * @return bool Returns true if the socket was created successfully,
     *              returns false if there was an error.
     */
    private function createSocket()
    {
        /**
         * Create a socket of IPv4 type using the TCP gateway.
         */
        $this->socket = socket_create(AF_INET, SOCK_STREAM, getprotobyname('tcp'));
        if(!$this->socket)
            return false;
        echo "Socket has been created.\n";

        /**
         *  Attempt to bind the socket to localhost:[port]
         */
        do
        {
            if(!isset($i))
                $i++;

            $port = $this->port;
            $bind = socket_bind($this->socket, 0, $port);

            if(!$bind)
            {
                $i++;
                $this->port = rand(79, 8079);
            }
        } while(!$bind && $i <= 5);

        if($i == 5)
            return false;
        echo "Port ".$this->port." has been binded to the RAT.\n";

        return true;
    }

    /**
     * Start a loop running on our socket. We will check if we are getting
     * data, accept connections and run any commands. When the connection
     * is closed, we will return true.
     * 
     * @access private
     * @return bool Returns false if the socket can't be listened to. Otherwise
     *              returns true when the socket is closed.
     */
    private function startLoop()
    {
        if(socket_listen($this->socket, 3))
        {
            while(true)
            {   
                if(($newspawn = socket_accept($this->socket)) !== false)
                {
                    $this->spawns[] = $newspawn;
                    echo "A new spawn has connected.";
                } else
                    echo "No new socket";
                sleep(1000);

                foreach($this->spawns as $key => $spawn)
                {
                    $data = trim(socket_read($spawn, 1024));
                    if(strlen($data) > 0)
                    {
                        if($data == "exit")
                        {
                            socket_close($spawn);
                            unset($this->spawns[$key]);
                            echo "Spawn killed.\n";
                        }
                        if($data == "kill")
                        {
                            foreach($this->spawns as $key => $spawn)
                            {
                                socket_close($spawn);
                                unset($this->spawns[$key]);
                            }
                            socket_close($this->socket);
                            echo "Socket closed.\n";
                            return true;
                        }
                        else
                        {
                            echo "Data: " . $data . "\n";
                        }
                    }
                }
            }
        }
        else
        {
            echo "Failure receiving data.\n";
            return false;
        }
    }
}
?>

提前致谢, 约翰

2 个答案:

答案 0 :(得分:3)

socket_set_nonblock()创建的套接字资源使用socket_create()而不是socket_set_blocking()。

socket_set_blocking()是别名 stream_set_blocking()仅适用于(php-)流,例如fopen()或stream_socket_create()

的结果

编辑:您还可以使用socket_select()或stream_select()来处理新连接和客户端数据包,例如。

private function createSocket()
{
  $this->socket = stream_socket_server('tcp://0.0.0.0:'.(int)$this->port, $errno, $errstr);
  if(!$this->socket) {
    $this->socket = null;
    echo "stream_socket_server failed : $errstr ($errno)\n";
    return false;
  }
  echo "Port ".$this->port." has been bound to the RAT.\n";
  return true;
}

public function startLoop() {
  if ( is_null($this->socket) ) {
    return false;
  }

  $write = array(); $exception=array();
  while( !$this->shutdown ) {
    // stream_select() alters the array, so $read has to be re-constructed in each iteration somehow
    $read = array_merge(array($this->socket), $this->spawns);
    // you might want to check $exception as well
    if ( 0!==stream_select($read, $write, $exception, 4) ) {
      // $now $read only contains those sockets, that will not block
      // for fread/accept operations
      foreach($read as $s) {
        if ( $s===$this->socket ) {
          $this->onAccept();
        }
        else {
          $this->onClientPacket($s);
        }
      }
    }
  }
  $this->shutdown();
}

请记住,如果客户端发送两个命令,例如例如

$fp1 = stream_socket_client("tcp://localhost:81", $errno, $errstr, 30);
fwrite($fp1, "1 blabla");
fwrite($fp1, "exit");

这并不一定意味着您的服务器会收到两条不同的消息。

答案 1 :(得分:1)

我相信您会对使用stream_socket_create()而不是socket_create()感兴趣,socket_set_blocking()应返回与{{1}}一起使用的有效资源。

请注意,您还需要利用stream_socket_accept()开始接受新创建的套接字上的连接。