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 се извиква, което допълнително ще извика метод за освобождаване с истински параметър, което ще гарантира, че връзката е обединена.

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

Ето release внедряване на метода. Последната проверка 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 Да, окончателният блок се изпълнява в случай на изключение. - 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 block се изпълнява както в случай на нормален, така и в случай на изключение. Следователно няма нужда да се извиква disconnect в блока try. - person Manish Mulimani; 17.07.2014