当我直接写入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;
}
}
答案 0 :(得分:2)
尝试:
channel2.write(mappedByteBuffer.duplicate());
channel.write(mappedByteBuffer);
理解NIO缓冲区的方法是牢记其基本属性:
NIO提供的所有缓冲操作都记录了操作如何影响这些属性。例如,WritableByteChannel.write()
文档告诉我们:
src.remaining()
之间(包括)字节将被写入通道;和count
字节被写入,当count
返回时,ByteBuffer的位置将增加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 :(得分:1)
当然可以同时使用多个频道,但更多的是发送文件的可怕方式。创建大量MappedByteBuffers会导致各种问题,因为底层映射区域永远不会被释放。只需将其作为普通通道打开并使用规范的NIO复制循环:
while (in.read(buffer) >= 0 || buffer.position() > 0)
{
buffer.flip();
out.write(buffer);
buffer.compact();
}