public function receive(int $length = self::DEFAULT_RECEIVE_LENGTH): string
    {
        $buffer = '';
        $metadata['unread_bytes'] = 0;
        $makeBlockingAfterRead = false;

        try {
            do {
                // feof means socket has been closed
                // also, sometimes in long running processes the system seems to kill the underlying socket
                if (!$this->socket || feof($this->socket)) {
                    $this->disconnect();

                    return $buffer;
                }

                $result = fread($this->socket, $length);

                if ($makeBlockingAfterRead) {
                    stream_set_blocking($this->socket, true);
                    $makeBlockingAfterRead = false;
                }

                // fread FALSE means socket has been closed
                if ($result === false) {
                    $this->disconnect();

                    return $buffer;
                }

                $buffer .= $result;

                // feof means socket has been closed
                if (feof($this->socket)) {
                    $this->disconnect();

                    return $buffer;
                }

                $continue = false;

                if (strlen($result) == 1) {
                    // Workaround Chrome behavior (still needed?)
                    $continue = true;
                }

                if (strlen($result) == $length) {
                    $continue = true;
                }

                // Continue if more data to be read
                $metadata = stream_get_meta_data($this->socket);

                if ($metadata && isset($metadata['unread_bytes'])) {
                    if (!$metadata['unread_bytes']) {
                        // stop it, if we read a full message in previous time
                        $continue = false;
                    } else {
                        $continue = true;
                        // it makes sense only if unread_bytes less than DEFAULT_RECEIVE_LENGTH
                        if ($length > $metadata['unread_bytes']) {
                            // http://php.net/manual/en/function.stream-get-meta-data.php
                            // 'unread_bytes' don't describes real length correctly.
                            //$length = $metadata['unread_bytes'];

                            // Socket is a blocking by default. When we do a blocking read from an empty
                            // queue it will block and the server will hang. https://bugs.php.net/bug.php?id=1739
                            stream_set_blocking($this->socket, false);
                            $makeBlockingAfterRead = true;
                        }
                    }
                }
            } while ($continue);

            return $buffer;
        } finally {
            if ($this->socket && !feof($this->socket) && $makeBlockingAfterRead) {
                stream_set_blocking($this->socket, true);
            }
        }
    }