与 socket_recv 相比,fgets 读取非常慢

fgets reading very slow compared to socket_recv

我一直在从 Python 套接字服务器读取数据时遇到问题。我尝试了几种方法,几个月来一直在寻找解决方案。

我试图从 Python 套接字服务器获得的响应每次都不同。第一次可以是 40 个字符,下次可以超过 10k 个字符。

我尝试使用 socket_recv() and fgets(),到目前为止 fgets 最适合我的需要,因为 socket_recvfgets 时没有得到完整的响应。只有一个问题。与 socket_recv 相比,它要慢得多,而且并不总能得到响应。

我在使用 fgets 时遇到的问题是,无论如何,它都需要 2.02 秒才能在本地连接上获得响应它是大是小。 我需要它下降,但我无法为我的生活弄清楚如何修复它..
连接到 Python 套接字服务器只需要 22 毫秒,所以我不明白为什么要花很长时间才能得到整个响应。

哦,如果有帮助,响应是一个 JSON 字符串。

这是我使用的代码:

/*
 * Recieve
 */
public function recv() {
    if (!$this->connected()) {
        $this->_errorStr = 'Recieve timeout';
        $this->_error = true;

        return false;
    }

    $buf = '';        
    while ($line = fgets($this->_socket)) {
        $buf .= $line;
    }

    return json_decode($buf);
}

如果你需要整个class:

class Sockets {
    /*
     * Variables
     */
    private $_id,
            $_name,
            $_ip,
            $_port,
            $_socket,
            $_socketTimeout = 1,
            $_triedConnect = false,
            $_errorStr = '',
            $_error = false;

    /*
     * Construct class
     */
    public function __construct($ip, $port) {
        $this->_ip = $ip;
        $this->_port = $port;
        $this->_socket = false;
    }

    /*
     * Send command
     */
    public function command($cmd, $json = true) {
        if ($json) {
            $cmd = json_encode($cmd);
        }
        if (!$this->send($cmd)) {
            return $this->_errorStr;
        }
        $r = $this->recv();
        if (!$r) {
            return $this->_errorStr;
        }

        return $r;
    }

    /*
     * Connect to server
     */
    public function connect() {
        $this->_error = false;
        if ($this->_triedConnect) {
            $this->_errorStr = 'Failed to connect.';
            $this->_error = true;

            return false;
        }

        $this->_triedConnect = true;
        $this->_errorStr = '';
        $errno = 0;
        $errstr = '';

        $this->_socket = @pfsockopen($this->_ip, $this->_port, $errno, $errstr, $this->_socketTimeout);
        if (!$this->_socket) {
            $this->_errorStr = sprintf('Can\'t connect to server.. (%errno: %errstr)', $errno, $errstr);
            $this->_error = true;
            $this->_socket = false;

            return false;
        }
        stream_set_timeout($this->_socket, $this->_socketTimeout);

        // Clear stream
        while ($this->dataReady()) {
            if (!fgets($this->_socket)) {
                break;
            }
        }

        if (!$this->connected()) {
            $this->_errorStr = 'Lost connection to server!';
            $this->_error = true;
            $this->_socket = false;

            return $this->_errorStr;
        }

        return true;
    }

    /*
     * Authentication
     */
    public function auth() {

    }

    /*
     * Connected
     */
    public function connected() {
        return $this->_socket !== false;
    }

    /*
     * Data ready
     */
    public function dataReady() {
        if (!$this->connected()) {
            return false;
        }

        return @stream_select($r = array($this->_socket), $w = null, $x = null, 0) > 0;
    }

    /*
     * Send data
     */
    public function send($data) {
        if (!$this->connected()) {
            $this->_errorStr = 'Not connected!';
            $this->_error = true;

            return false;
        }

        if (@fwrite($this->_socket, $data) === false) {
            $this->_errorStr = 'Failed to send command!';
            $this->_error = true;

            return false;
        }
        return true;
    }

    /*
     * Recieve
     */
    public function recv() {
        if (!$this->connected()) {
            $this->_errorStr = 'Recieve timeout';
            $this->_error = true;

            return false;
        }

        $buf = '';        
        while ($line = fgets($this->_socket)) {
            $buf .= $line;
        }

        return json_decode($buf);
    }

    /*
     * Disconnect
     */
    public function disconnect() {
        if (!$this->connected()) {
            return;
        }

        fclose($this->_socket);
        $this->_socket = false;
        $this->_triedConnect = false;
    }
}

非常感谢任何帮助!


编辑

我用的机器是运行 Windows 8.1 Pro,有Media Center。

我正在为安装了以下依赖项的服务器使用 Python 2.7.9

psutil   <- https://pypi.python.org/pypi/psutil
colorama <- https://pypi.python.org/pypi/colorama
pycrypto <- http://www.voidspace.org.uk/python/modules.shtml#pycrypto

什么类型的 TCP 套接字服务器根本不重要。只是一个基本的应该只是为了测试这个。即使没有依赖项。 this 之类的东西有效。

对于 PHP 我正在使用 WampPHP 5.5.12 启用了以下模块:

php_bz2
php_com_dotnet
php_curl
php_exif
php_fileinfo
php_gd2
php_gettext
php_gmp
php_imap
php_intl
php_ldap
php_mbstring
php_mysql
php_mysqli
php_openssl
php_pdo_mysql
php_pdo_sqlite
php_shmop
php_soap
php_sockets
php_sqlite3
php_xmlrpc
php_xsl

有些(如果不是全部)默认启用。

要测试套接字 class,您只需要像这样:

// Import class file
require_once 'Sockets.php';
$socket = new Sockets('127.0.0.1', 21); // Change the port accordingly

// Connect to socket server
$socket->connect();

// Now in my case, the socket server responds to JSON strings, and nothing else.
// So I am going to show you show I send a command.
$command = array(
    'key' => 'encrypted key', // This key is to do some validation on the server-side
    'command' => 'command' // This is the command to be issued.
);

// Send command to socket server and dump the response
var_dump($socket->command($command));

// To send a plainstring command use this instead
var_dump($socket->command('command here', false));

经过相当多的调试和进一步搜索互联网但没有找到任何东西,我终于找到了我的问题的答案。


问题出在 Python 套接字服务器上。向套接字服务器发送命令后,它会发回带有请求数据的响应。套接字服务器应该然后关闭连接。这就是问题所在。它正在发送响应,但没有关闭连接,所以我所要做的就是在每个命令后关闭连接。响应时间从 2.02 秒下降到 20ms,这是我想要的。