Мое приложение Android отправляет большие файлы (может быть 100 МБ) между двумя пирами, используя WebRTC. Когда я изначально писал код, который передает данные между пирами, используя DataChannel, я просто отправлял куски ByteBuffer так быстро, как мог. Я быстро обнаружил, что при этом переполняет внутренний буфер в DataChannel. Итак, я понимаю, что мне нужно реализовать «политику регулирования».
Я предпочитаю, чтобы я мог динамически регулировать скорость в зависимости от устройств в игре и текущих сетевых условиях. Но у меня есть фундаментальные вопросы о том, как это сделать.
В настоящее время я использую подход статического газа, как это:
// Settings for DataChannel
private static final int DATACHANNEL_BUFFER_HIGH_WATER_MARK = (1024*512); // pause when buffer gets to half MB
private static final int DATACHANNEL_BUFFER_LOW_WATER_MARK = (1024*256); // resume when buffer gets down to 256k
И мой код передачи выглядит так:
// Open file for reading, and send chunks via the data channel
int bytesRead = 0;
boolean keepGoin = true;
byte[] fileChunk = new byte[WEBRTC_TRANSFER_BUFFER_SIZE];
long bytesSent = 0;
origin = new BufferedInputStream(new FileInputStream(zipFile), WEBRTC_TRANSFER_BUFFER_SIZE);
while (keepGoin && ((bytesRead = origin.read(fileChunk, 0, WEBRTC_TRANSFER_BUFFER_SIZE)) != -1)) {
bytesSent += bytesRead;
ByteBuffer wrap = ByteBuffer.wrap(fileChunk, 0, bytesRead);
peerDataChannel.send(new DataChannel.Buffer(wrap, false));
if (peerDataChannel.bufferedAmount() > DATACHANNEL_BUFFER_HIGH_WATER_MARK) {
while (keepGoin && peerDataChannel.bufferedAmount() > DATACHANNEL_BUFFER_LOW_WATER_MARK) {
Thread.sleep(250);
}
}
}
Хотя это работает в процессе разработки, я уверен, что в некоторых случаях я «оставляю скорость на столе» (потому что верхняя отметка слишком низкая), а в других я могу себе представить, что на самом деле это все еще может быть переполнением. буфер в канале данных (на устройствах с не такой большой памятью и т. д.).
Вопросы:
- Как узнать, какой размер внутреннего буфера по умолчанию используется дляактивный канал данных? Можно ли его отрегулировать?
- Является ли подход установления "отметки максимальной воды" и "отметки низкой воды" и позволяет каналу данных пропускать между ними правильный подход?
- Является ли целесообразным "Политика корректировки «для поддержания более тесного промежутка между высоким и низким уровнем?»
- Существует ли «наилучшая практика» для определения наиболее подходящего уровня максимума / минимума для любой конкретной ситуации?
На принимающей стороне я в настоящее время ставлю в очередь каждый массив byte [], полученный через DataChannel.onMessage () при передаче файла в LinkedBlockingQueue, который является субъектом отдельного потока, который извлекает куски ByteBuffer из очереди и записываетих на диск так быстро, как это возможно. В DataChannel.onMessage () это выглядит так:
public void onMessage(DataChannel.Buffer buffer) {
// Recipient's data channel
byte[] bytes;
boolean isReceivingFileData = false;
if (buffer.data.hasArray()) {
bytes = buffer.data.array();
} else {
bytes = new byte[buffer.data.remaining()];
buffer.data.get(bytes);
}
// fileTransferQueue starts as null, and is set to not null
// when a file transfer starts and back to null when xfer is complete
isReceivingFileData = (fileTransferQueue != null);
if ( isReceivingFileData ) {
try {
fileTransferQueue.put(bytes); // Placing bytes in queue for write thread
} catch (InterruptedException e) {
Timber.e("Lifecycle (t: %d, id: %s): InterruptedException transferring data: %s", Thread.currentThread().getId(),shareId, e.getMessage());
//TODO - should we bail or rety or...?
}
Затем в моем отдельном потоке FileTransferReceiver мы имеем:
private class FileTransferReceiver extends Thread {
private String fileTransferInfo;
private long bytesWritten = 0;
private boolean keepGoin = false;
FileTransferReceiver(String fileTransferInfo) {
super("FileTransferReceiver");
this.fileTransferInfo = fileTransferInfo;
}
public void run() {
BufferedOutputStream bos = null;
boolean interruptedByException = false;
String[] fileData = fileTransferInfo.split(BODY_COMPONENTS_SEPARATOR);
File fileToWrite = new File(getExternalTransferCacheDir(), fileData[BODY_ZIPFILE_NAME]);
long bytesToWrite = Long.parseLong(fileData[BODY_ZIPFILE_SIZE]);
int nextMediaIndexToShare = Integer.parseInt(fileData[BODY_MEDIA_INDEX]);
InTouchMessage shareMessage = new InTouchMessage(shareId,webRTCLocalUserName,webRTCRemoteUserName,"", RECEIVER_SEZ_SENDING_FILE_PROGRESS,null);
try {
Timber.d("Lifecycle (t: %d, id: %s): writing transfer file to location: %s", Thread.currentThread().getId(),shareId, fileToWrite.getCanonicalPath());
keepGoin = true;
bos = new BufferedOutputStream(new FileOutputStream(fileToWrite));
int chunkedProgress = 0;
int numberOfFives = 0;
while (keepGoin && (bytesWritten < bytesToWrite)) {
byte[] chunkToWrite = fileTransferQueue.take(); // Grabbing bytes to write from queue
bos.write(chunkToWrite);
bytesWritten += chunkToWrite.length;
Это кажется разумнымПодход?
Кстати, я сделал обзор этого поста , который, как кажется, написан для работы браузер-браузер вместо мобильного приложения, имел некоторые интересные идеи. Это где я получил некоторые из моих настроек буфера по умолчанию. Но я все же хотел бы понять из подхода, основанного исключительно на мобильных приложениях, что такое «лучшие практики».