Как определить, сколько данных было отправлено через TLS / TCP OutputStream на Android? - PullRequest
0 голосов
/ 13 февраля 2020

Дело в том, что мой Android сокет TLS / TCP иногда отключает поток данных. Обычно из-за тайм-аутов, et c. Я пытаюсь определить, сколько данных на самом деле было успешно отправлено, ДО того, как сокет обрезается, поэтому я знаю, где взять.

Как мне это сделать?

Я думаю, чтобы вытащить выводите поток из сокета и пишите чуть-чуть за раз. Если мы получим разъединение, то я могу выбрать, где мы остановились, и продолжить отправку следующего чанка.

Я выбрал НЕ использовать BufferedOutputStream, потому что после разъединения я не буду уверен, сколько было отправлено и как много не было. Я бы хотел ошибиться.

Это моя лучшая попытка:

/**
 * Static class that holds the currently writable data
 */
final private class WritingByteArray {
    /**
     * Easy C-tor
     */
    public WritingByteArray( final byte[] bytes ) {
        this.bytes = bytes;
        this.offset = 0;
    }

    /**
     * Convenience
     */
    public int length() {
        return( bytes.length );
    }

    /**
     * Convenience
     */
    public int remaining( int max ) {
        return( Math.min( max, length()-offset ) );
    }

    /**
     * Consume bytes. Really just increment the offset with a safety catch
     */
    private void consume( int size ) {
        offset = Math.min( bytes.length, offset+size );
    }

    /**
     * Remaining without a max
     */
    public int remaining() {
        return( length()-offset );
    }

    /**
     * Are we empty?
     */
    public boolean empty() {
        return( offset>=length() );
    }

    public byte[] bytes; //!< The byte array we're working with
    public int offset; //!< Our reading offset
}

/**
 * Our write data function new
 */
private void writeDataNew() {
    final int WRITE_BUFFER_SIZE = ( 8*1024 ); //!< 8kb of write buffer size

    try {
        OutputStream outputStream = sslSocket.getOutputStream();

        // Do we have data we are currently writing
        if( currentByteArray==null ) {
            Log.d(
                    TAG,
                    "We don't have anything in the current byte array. Pulling from the output buffer."
                 );

            // Pull from the outputBuffer
            synchronized( outputBuffer ) {
                final int size = outputBuffer.size();

                Log.d( TAG, "OutputBuffer has "+size );

                // This was one done outside of synchronization... that's bad!
                if( size==0 ) {
                    Log.d( TAG, "Our output buffer is actually empty." );
                    return;
                }

                // Copy the bytes to our private byte array
                currentByteArray = new WritingByteArray( outputBuffer.toByteArray() );

                // Get the data
                //                    outputStream.write( outputBuffer.toByteArray() );
                // Reset the buffer
                outputBuffer.reset();
            } // Free the mutex so bytearray can be written again
        }

        // Now write the data
        final int size = currentByteArray.remaining( WRITE_BUFFER_SIZE );

        // Do we have bytes to send?
        if( size>0 ) {
            // Alert how much we'll write
            Log.d( TAG, "We have "+size+" data to write" );
            // Write our deduced size
            outputStream.write( currentByteArray.bytes, currentByteArray.offset, size );
            // Flush the output so we can block???
            // DOES THIS BLOCK?
            // If this blocks maybe this will work...
            outputStream.flush();
            // Just a log tag
            Log.d( TAG, "Data wrote "+size+" bytes of data." );

            // Now consume
            currentByteArray.consume( size );

            // Do we have any left?
            if( currentByteArray.empty() ) {
                Log.d( TAG, "Clearing currentByteArray" );

                currentByteArray = null; // Clear it so we know
            }
        }
        else {
            Log.d( TAG, "No more data left " );
            // Just in case
            currentByteArray = null;
        }

        // Call again
        writeDataNew();
    }
    catch( final Exception e ) {
        e.printStackTrace();

        reportError( e.getLocalizedMessage() );

        // Here we can reconnect and send more data if we failed out
    }
}

1 Ответ

2 голосов
/ 13 февраля 2020

Информация о том, сколько данных было получено одноранговым узлом, по существу не подлежит восстановлению (когда обсуждение ограничено самим протоколом TCP). IOW: Вы не можете сделать то, что пытаетесь сделать, просто изменив клиент.

Да, TCP действительно включает двунаправленные ACK, которые включают информацию о том, сколько данных было успешно передано , но эта информация напрямую недоступна для прикладного уровня.

Даже если бы вы могли получить доступ к информации TCP ACK, нет никакого способа узнать, что вы получили каждое подтверждение ACK от партнера отправлено , в данный момент соединение не установлено. (См .: Проблема двух генералов .)

Обратите внимание, что даже если вы не используете BufferedOutputStream, буферизация по-прежнему происходит на уровне TLS и на нескольких сетевых уровнях, в том числе в пределах ядро.

Итог: невозможно узнать состояние «получателя» однорангового узла со 100% -ной уверенностью, если только эта информация не передана явно, или оба партнера не имеют некоторого способа согласовать общее «основное» состояние которая существует в момент начала соединения.

В большинстве практических систем клиентский запрос о состоянии однорангового узла будет иметь место в начале разговора (HTTP HEAD, например), или они будут отслеживать явные подтверждения, отправленные партнером. Обратите внимание, что даже упорядоченное закрытие соединения может служить явным подтверждением.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...