【问题标题】:Can 2 WritableByteChannels be used at the same time?可以同时使用 2 个 WritableByteChannel 吗?
【发布时间】:2012-03-02 09:09:35
【问题描述】:

当我直接写入 2 个输出流时,一切正常。但是,当我尝试写入 2 个频道时,第二个频道似乎没有收到它。

有人知道是否可以同时写入 2 个 WritableByteChannels 吗?如果没有,关于我仍然可以使用 NIO/Channels 执行相同操作的任何其他想法?

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;
    }
}

【问题讨论】:

  • mappedByteBuffer 在第一个通道使用后被擦除。我创建了第二个 mappedByteBuffer 执行相同的映射并且它可以工作。但是,它并不像我希望的那样有效。还有其他想法吗?

标签: android nio channel


【解决方案1】:

试试:

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

理解 NIO Buffers 的方法是牢记它的基本属性:

  • 底层数据存储(通常是普通字节数组,但也可以是其他东西,例如文件的内存映射区域);
  • 基础空间内的起点和容量
  • 您在缓冲区中的当前位置;和
  • 缓冲区的限制

NIO 提供的所有缓冲区操作都记录在操作如何影响这些属性方面。例如,WritableByteChannel.write() 文档告诉我们:

  • 0和src.remaining()(含)之间的字节将被写入通道;和
  • 如果写入了count 字节,则当write() 返回时,ByteBuffer 的位置 将增加count

所以看看你的原始代码:

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() 方法将 limit 设置为该位置并将位置设置回 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();
}

【讨论】:

    【解决方案2】:

    当然可以同时使用多个通道,但更重要的是,这是一种发送文件的糟糕方式。创建大量 MappedByteBuffers 会导致各种问题,因为底层映射区域永远不会被释放。只需将其作为普通通道打开并使用规范的 NIO 复制循环即可:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-04-15
      • 1970-01-01
      • 1970-01-01
      • 2013-11-17
      • 2021-04-07
      • 2013-07-10
      • 2018-08-20
      相关资源
      最近更新 更多