public function connect($addr = null, $port = null, $timeout = null)
    {
        if ($port == null && $timeout == null) {
            if ($addr !== null) {
                $timeout = $addr;
            } else {
                $timeout = 0;
            }
            if ($this->addr == null || $this->port == null) {
                throw new \InvalidArgumentException('Address and port were not specified during construction!');
            }
            $addr = $this->addr;
            $port = $this->port;
        } else {
            if ($timeout === null) {
                $timeout = 0;
            }
            if ($addr === null || $port === null) {
                throw new \InvalidArgumentException('Both address and port must be specified!');
            }
        }

        if (!$this->isBlocking() && $timeout !== 0) {
            throw new \InvalidArgumentException('Timeout isn\'t compatible with non-blocking socket.');
        }

        if ($timeout === 0) {
            if (@socket_connect($this->socket, $addr->getHostAddress(), $port) === false) {
                $this->throwSocketError();
            }
        } else {
            $this->setBlocking(false);
            try {
                if (@socket_connect($this->socket, $addr->getHostAddress(), $port) === false) {
                    if (socket_last_error() !== SOCKET_EINPROGRESS) {
                        $this->throwSocketError();
                    }

                    if ($this->selectWrite($timeout) === false) {
                        throw new SocketException('Connection timeout', SOCKET_ETIMEDOUT);
                    }

                    if ($this->getOption(SOL_SOCKET, SO_ERROR) !== 0) {
                        throw new SocketException('Connection refused', SOCKET_ECONNREFUSED);
                    }
                }
            }
            finally {
                $this->setBlocking(true);
            }
        }

        $this->addr = $addr;
        $this->port = $port;
    }