【问题标题】:How to determine how much data was sent through a TLS/TCP OutputStream on Android?如何确定在 Android 上通过 TLS/TCP OutputStream 发送了多少数据?
【发布时间】:2020-05-29 10:31:15
【问题描述】:

真正的重点是——我的 Android TLS/TCP 套接字有时会断开中间数据流。通常是由于超时等原因。我试图确定在套接字切断之前实际成功发送了多少数据,以便我知道从哪里获取。

我该怎么做?

我的想法是从套接字中拉出输出流,一次写一点。如果我们断开连接,那么我可以从中断处继续并继续发送下一个块。

我选择不使用 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
    }
}

【问题讨论】:

  • 定义“发送成功”。这是否意味着“被对等方接收”,或“排队到操作系统进行传输”?
  • 这是个好问题。我支持“被同行接受”,但除非我问同行,否则我认为这可能是不可知的。

标签: android sockets ssl tcp


【解决方案1】:

有关对等方已接收到多少数据的信息基本上是不可恢复的(当讨论仅限于 TCP 协议本身时)。 IOW:你不能仅仅通过修改客户端来做你想做的事情。

是的,TCP 确实包含双向 ACK,其中包含有关已成功传输多少数据的信息,但该信息不能直接提供给应用层。

即使您可以访问 TCP ACK 信息,也无法知道在连接失败时您已收到对等方发送的每个 ACK​​。 (参见:Two General's Problem。)

请注意,即使您不使用BufferedOutputStream,缓冲仍然发生在 TLS 层和多个网络层,包括内核中。

底线:不可能 100% 确定地知道对等方的“接收”状态,除非此信息是明确传输的,或者两个对等方有某种方式就当前存在的共同“基础”状态达成一致。连接开始。

大多数实际系统会在对话开始时让客户端查询对等方的状态(例如HTTP HEAD),或者它们会跟踪对等方发送的明确确认。请注意,即使是有序的连接关闭也可以作为明确的确认。

【讨论】:

  • PS 我觉得这个答案太棒了。
猜你喜欢
  • 1970-01-01
  • 2015-11-01
  • 1970-01-01
  • 1970-01-01
  • 2017-08-13
  • 2019-01-15
  • 1970-01-01
  • 2016-04-11
  • 1970-01-01
相关资源
最近更新 更多