PHP Socket Accept_Connection 挂起

标签 php oop sockets

我最近发现PHP不仅有fsock*功能,还有自己创建服务器的功能。我决定尝试一下,并想出了这个。现在,问题是它卡在 accept_connection() 上(因为它正在等待连接。)我发现解决方案是使用 stream_set_blocking() >,正如你所看到的,我尝试过,但没有成功。

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

警告:socket_set_blocking():提供的资源不是第 68 行/home/insomniaque/workspace/PHP Socket RAT/rat.class.php 中的有效流资源

我知道 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;
        }
    }
}
?>

提前致谢, 约翰

最佳答案

使用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");

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

关于PHP Socket Accept_Connection 挂起,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2046442/

相关文章:

java - 在java中创建数字证书

php - mysql_query() 的问题;表示需要资源

javascript - http post 没有输入操作字段

php - 尝试按产品类别将标签与产品相关联

php - 如何从 PHP 中的索引数组创建多维数组?

c# - 接口(interface)继承是一种不好的做法吗?

python - 如何访问和修改 LabTestRepository 类中的 __list_of_hospital_lab_test_ids?

java - 如何将ArrayList数据插入数据库

java - 在 Java 中调用 ArrayList.get() 时出现 NullPointerException?

java - Netty 4 中的阻塞