Android: HttpURLConnection не отключается

запустив netstat на сервере:
НЕСКОЛЬКО ДНЕЙ НАЗАД: я мог видеть, что соединение было ESTABLISHED только около секунды, а затем оно исчезало из списка
СЕЙЧАС: он остается как ESTABLISHED примерно 10 секунд, затем переходит в FIN_WAIT1 и FIN_WAIT2

код Android тот же, сервер тот же

Возможно ли, что какое-то обновление Android могло что-то изменить?

Я не могу это объяснить.

Я сообщаю код ниже. urlConnection.disconnect() выполняется, но соединение с сервером остается установленным.

    HttpURLConnection urlConnection = null;
    System.setProperty("http.keepAlive", "false");

    try {
        URL url = new URL(stringUrl);
        urlConnection = (HttpURLConnection) url.openConnection();
        InputStream instream = new BufferedInputStream(urlConnection.getInputStream());
        ...
        instream.close();
    } catch (MalformedURLException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (urlConnection!=null) {
            urlConnection.disconnect();
        }
    }

person Daniele B    schedule 06.07.2014    source источник
comment
Попробуйте установить для заголовка Connection значение close.   -  person hgoebl    schedule 07.07.2014
comment
Я пробовал это, и вместо FIN_WAIT1 и FIN_WAIT2 он переходит в TIME_WAIT   -  person Daniele B    schedule 07.07.2014


Ответы (3)


В тот момент, когда все данные во входном потоке используются, соединение автоматически освобождается и добавляется в пул соединений. Базовое соединение сокета не освобождается, при условии, что соединение будет повторно использоваться в ближайшем будущем. Хорошей практикой является вызов disconnect в блоке finally, так как он обеспечивает разрыв соединения в случае exceptions.

Вот реализация метода чтения FixedLengthInputStream:

@Override public int read(byte[] buffer, int offset, int count) throws IOException {
        Arrays.checkOffsetAndCount(buffer.length, offset, count);
        checkNotClosed();
        if (bytesRemaining == 0) {
            return -1;
        }
        int read = in.read(buffer, offset, Math.min(count, bytesRemaining));
        if (read == -1) {
            unexpectedEndOfInput(); // the server didn't supply the promised content length
            throw new IOException("unexpected end of stream");
        }
        bytesRemaining -= read;
        cacheWrite(buffer, offset, read);
        if (bytesRemaining == 0) {
            endOfInput(true);
        }
        return read;
    }

Когда переменная bytesRemaining становится равной 0, endOfInput, который в дальнейшем вызовет метод освобождения с параметром true, что гарантирует объединение соединения.

protected final void endOfInput(boolean reuseSocket) throws IOException {
    if (cacheRequest != null) {
            cacheBody.close();
    }
    httpEngine.release(reuseSocket);
}

Вот ссылка освобождение реализации метода. Последняя проверка if определяет, нужно ли закрывать соединение или добавлять его в пул соединений для повторного использования.

public final void release(boolean reusable) {
    // If the response body comes from the cache, close it.
    if (responseBodyIn == cachedResponseBody) {
        IoUtils.closeQuietly(responseBodyIn);
    }
    if (!connectionReleased && connection != null) {
        connectionReleased = true;
        // We cannot reuse sockets that have incomplete output.
        if (requestBodyOut != null && !requestBodyOut.closed) {
            reusable = false;
        }
        // If the headers specify that the connection shouldn't be reused, don't reuse it.
        if (hasConnectionCloseHeader()) {
            reusable = false;
        }
        if (responseBodyIn instanceof UnknownLengthHttpInputStream) {
            reusable = false;
        }
        if (reusable && responseBodyIn != null) {
            // We must discard the response body before the connection can be reused.
            try {
                Streams.skipAll(responseBodyIn);
            } catch (IOException e) {
                reusable = false;
            }
        }
        if (!reusable) {
            connection.closeSocketAndStreams();
            connection = null;
        } else if (automaticallyReleaseConnectionToPool) {
            HttpConnectionPool.INSTANCE.recycle(connection);
            connection = null;
        }
    }
}

Примечание. Ранее я ответил на несколько вопросов SO, связанных с HttpURLConnection, которые могут помочь вам понять базовую реализацию. Вот ссылки: Link1 и Ссылка2.

person Manish Mulimani    schedule 11.07.2014
comment
вызывается ли наконец {} в случае исключения? - person jesses.co.tt; 17.07.2014
comment
@jesses.co.tt Да, блок finally выполняется в случае исключения. - person Manish Mulimani; 17.07.2014

В соответствии с тем, как работает протокол TCP, когда вы закрываете соединение, оно не исчезает автоматически из вашего списка сокетов.

Когда вы отправляете другой стороне сигнал завершения, запускается протокол (точнее, процедура), где первым шагом является именно ваше намерение закрыть соединение. Вы отправляете сигнал другому узлу, и это будет включать статус FIN_WAIT1.

Когда пользователь получил этот сигнал, следующим шагом будет его подтверждение с удаленной стороны. Это означает, что противоположный сервер отправляет вам еще один сигнал, символизирующий, что узел также готов закрыть соединение. Это будет статус FIN_WAIT2.

Между этими двумя шагами может случиться так, что удаленный узел еще не ответил (поэтому вам не было подтверждено, что вы хотите закрыть соединение). В это время вы будете находиться в промежуточном состоянии, называемом CLOSE_WAIT (возобновление: после того, как вы отправили сигнал FIN на удаленный сервер, а они еще не ответили).

Состояние TIME_WAIT будет означать, что вы даете серверу некоторое время, прежде чем окончательно закрыть его для приема некоторых пакетов. Вы делаете это, потому что могут возникнуть аномалии соединения, и удаленный сервер мог не получить сообщение «отключение» и отправить вам какой-то пакет. Поэтому, когда это происходит, вместо того, чтобы создавать новый сокет между обоими узлами, вы связываете его с тем, который у вас есть в состоянии TIME_WAIT, и просто отбрасываете этот пакет, потому что, вероятно, порядковый номер не будет упорядочен.

Есть и другие состояния, вы можете увидеть, но в зависимости от того, как вы описываете это, мне это кажется вполне нормальным, если только при вызове метода .disconnect() не будет продолжаться статус ESTABLISHED. В этом случае что-то работает не так, как ожидалось (это может быть связано с какой-то перегрузкой или неоптимизированным кодом, который может сделать ваше выполнение очень медленным).

person nKn    schedule 10.07.2014
comment
CLOSE_WAIT не является «промежуточным состоянием» между FIN_WAIT_1 и FIN_WAIT_2 и не означает, что вы отправили партнеру FIN. Это означает, что вы получили его от партнера и еще не выдали его. - person user207421; 26.03.2018

Вы должны отключить свой открытый UrlConnection

HttpURLConnection urlConnection = null;
System.setProperty("http.keepAlive", "false");

try {
    URL url = new URL(stringUrl);
    urlConnection = (HttpURLConnection) url.openConnection();
    InputStream instream = new BufferedInputStream(urlConnection.getInputStream());
    ...
    instream.close();
    urlConnection.disconnect(); //HERE
} catch (MalformedURLException e) {
    e.printStackTrace();
} catch (IOException e) {
    e.printStackTrace();
} finally {
    if (urlConnection!=null) {
        urlConnection.disconnect();
    }
}
person Eefret    schedule 15.07.2014
comment
блок finally выполняется как в случае нормального сценария, так и в случае исключения. Следовательно, нет необходимости вызывать disconnect в блоке try. - person Manish Mulimani; 17.07.2014