android - 如何确定在 Android 上通过 TLS/TCP OutputStream 发送了多少数据?
问题描述
关键是这个——我的 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
}
}
解决方案
有关对等方已接收到多少数据的信息基本上是不可恢复的(当讨论仅限于 TCP 协议本身时)。IOW:你不能仅仅通过修改客户端来做你想做的事情。
是的,TCP确实包含双向 ACK,其中包含有关已成功传输多少数据的信息,但该信息不能直接用于应用层。
即使您可以访问 TCP ACK 信息,也无法知道在连接失败时您已收到对等方发送的每个 ACK 。(见:两个将军的问题。)
请注意,即使您不使用 a BufferedOutputStream
,缓冲仍然发生在 TLS 层和多个网络层,包括内核中。
底线:不可能 100% 确定地知道对等方的“接收”状态,除非此信息被明确传输,或者两个对等方有某种方式就连接开始时存在的共同“基本”状态达成一致。
大多数实际系统将在对话开始时让客户端查询对等方的状态(HTTP HEAD
例如),或者它们将跟踪对等方发送的显式确认。请注意,即使是有序的连接关闭也可以作为明确的确认。
推荐阅读
- python - 熊猫 - 地图功能的错误行为
- cors - 如何为来自 Twisted 服务器的错误响应设置自定义标头?
- android - 扑动未定义的类'FirebaseFirestore'
- arrays - 如何在 Expect/Tcl 中使用数组 ssh?
- c# - 由 ElasticDb 自动生成的按 ID 更新嵌套
- javascript - 使用 Refs 将输入文本值设置为空白的问题
- vb.net - 舍入数字:如果数字更接近 100,则向下舍入到最接近的 250,否则向上舍入到最接近的 250
- kubernetes - 似乎有些东西正在捕获到 pod 的 TCP 流量
- python - Pandas 按前 2 列分组使用下一列作为两个新字段并使用单元格计数
- php - 在回声线中插入图像 - PHP