Можно ли использовать 2 WritableByteChannels одновременно? - PullRequest
0 голосов
/ 10 февраля 2012

Когда я пишу напрямую в 2 выходных потока, все работает нормально.Когда я пытаюсь записать на 2 канала, второй, похоже, не получает его.

Кто-нибудь знает, можно ли одновременно записывать 2 WritableByteChannels?Если нет, есть ли другие идеи о том, что я могу сделать, чтобы выполнить то же действие, все еще используя NIO / каналы?

connection2 = new Socket(Resource.LAN_DEV2_IP_ADDRESS, Resource.LAN_DEV2_SOCKET_PORT); 
out2 = connection2.getOutputStream(); 

connection = new Socket(Resource.LAN_HOST_IP_ADDRESS, Resource.LAN_HOST_SOCKET_PORT); 
out = connection.getOutputStream();         

File f = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), filename);

in = new FileInputStream(f);
fic = in.getChannel();
fsize = fic.size();
channel2 = Channels.newChannel(out2); 
channel = Channels.newChannel(out); 

//Send Header
byte[] p = createHeaderPacket(filename, f.length());
out2.write(p); // Received correctly
out.write(p);  // Received correctly

//Send file
long currPos = 0;
while (currPos < fsize)
{
    if (fsize - currPos < Resource.MEMORY_ALLOC_SIZE)
    {                       
        mappedByteBuffer = fic.map(FileChannel.MapMode.READ_ONLY, currPos, fsize - currPos);
        channel2.write(mappedByteBuffer); // Received correctly
        channel.write(mappedByteBuffer);  // Never received
        currPos = fsize;
    }
    else
    {
        mappedByteBuffer = fic.map(FileChannel.MapMode.READ_ONLY, currPos, Resource.MEMORY_ALLOC_SIZE);
        channel2.write(mappedByteBuffer); // Received correctly
        channel.write(mappedByteBuffer);  // Never received
        currPos += Resource.MEMORY_ALLOC_SIZE;
    }
}

Ответы [ 2 ]

2 голосов
/ 14 апреля 2012

Попробуйте:

channel2.write(mappedByteBuffer.duplicate());
channel.write(mappedByteBuffer);

Чтобы понять NIO Buffers, нужно помнить о его основных свойствах:

  • базовое хранилище данных (обычно это обычный байтовый массив)., но могут быть и другие вещи, такие как отображаемая в памяти область файла);
  • начало и емкость в пределах этого базового пространства;
  • ваш текущий позиция в буфере;и
  • предел буфера.

Все операции с буфером, предоставляемые NIO, задокументированы с точки зрения того, как операция влияет на эти свойства.Например, документация WritableByteChannel.write() сообщает нам, что:

  • От 0 до src.remaining() (включительно) байтов будет записано в канал;и
  • Если записано count байт, позиция байтового буфера будет увеличена на count при возврате write().

Итак, глядя на вашисходный код:

channel2.write(mappedByteBuffer); // Received correctly
channel.write(mappedByteBuffer);  // Never received

Если при первой записи все оставшиеся значения mappedByteBuffer записываются в channel2, то после этого оператора mappedByteBuffer.remaining() будет равен нулю, поэтому при записи в channel не будут записываться никакие байты.вообще.

Следовательно, мое предложение выше использовать ByteBuffer.duplicate() при первой записи.Этот метод возвращает новый объект ByteBuffer, который:

  • разделяет базовое хранилище исходного буфера (поэтому вы не создаете ненужную копию в памяти фактических байтов, которые хотите записать дважды);но
  • имеет свои собственные позиции (и оставшиеся) значения, поэтому, когда channel2.write() корректирует эту (дублирующую) позицию ByteBuffer, он не изменит позицию в исходном буфере,

поэтому channel.write() все равно получит предполагаемый диапазон байтов.

В качестве альтернативы вы также можете написать:

mappedByteBuffer.mark(); // store the current position
channel2.write(mappedByteBuffer);
mappedByteBuffer.reset(); // move position to the previously marked position
channel.write(mappedByteBuffer);

Я также склонен согласиться с точкой зрения EJPчто вы, вероятно, не используете MappedByteBuffer здесь наилучшим образом.Вы можете упростить свой цикл копирования до:

ByteBuffer buffer = ByteBuffer.allocate(Resource.MEMORY_ALLOC_SIZE);
while (fic.read(buffer) >= 0) {
    buffer.flip();
    channel2.write(buffer.duplicate());
    channel.write(buffer);
}

Здесь метод read() увеличивает позицию на количество байтов, считанных с канала, а затем метод flip() устанавливает предел в эту позицию и обратно в 0, что означает, что только что прочитанные вами байты находятся в оставшемся диапазоне, который будет использовать write().

Однако вы заметите, что цикл EJP немного большесложнее, чем это.Это связано с тем, что операции записи на каналах не обязательно записывают каждый оставшийся байт.(В документации write() приведен пример сетевого сокета, открытого в неблокирующем режиме.) Однако этот пример кода (и аналогичный пример в документации ByteBuffer.compact()) основан на том факте, что вы пишете только водин канал;когда вы пишете на два разных канала, вы должны учитывать тот факт, что два канала могут принимать разное количество байтов.Итак:

ByteBuffer buffer = ByteBuffer.allocate(Resource.MEMORY_ALLOC_SIZE);
while (fic.read(buffer) >= 0) {
    buffer.flip();

    buffer.mark();
    while (buffer.hasRemaining()) {
        channel2.write(buffer);
    }

    buffer.reset():
    while (buffer.hasRemaining()) {
        channel.write(buffer);
    }

    buffer.clear();
}
1 голос
/ 10 февраля 2012

Конечно, можно использовать несколько каналов одновременно, но, главное, это ужасный способ отправки файла.Создание большого количества MappedByteBuffers вызывает всевозможные проблемы, поскольку базовые отображаемые области никогда не освобождаются.Просто откройте его как обычный канал и используйте канонический цикл копирования NIO:

while (in.read(buffer) >= 0 || buffer.position() > 0)
{
    buffer.flip();
    out.write(buffer);
    buffer.compact();
}
...