ByteBuffer:写入,然后回到缓冲区的开头并在之前写入的所有数据之前写入(前置?)
Posted
技术标签:
【中文标题】ByteBuffer:写入,然后回到缓冲区的开头并在之前写入的所有数据之前写入(前置?)【英文标题】:ByteBuffer: Write to, then go back to the beginning of the buffer and write before all the data that was previously written (prepending?) 【发布时间】:2012-06-18 00:34:19 【问题描述】:我在使用 ByteBuffer 时还是有点犹豫。我想要做的是将数据写入 ByteBuffer,然后转到 ByteBuffer 的开头并在所有数据之前写入一个字节(写入数据包的有效负载,然后附加标头。)我该怎么做那个?
图表:
缓冲区开始于:
| PAYLOAD |
添加操作码头后的缓冲区(在我想做的之后):
| HEADER | PAYLOAD |
该 |只是作为数据类型的分隔符,而不是真正的任何东西。
【问题讨论】:
创建第二个ByteBuffer
、put
标题然后put
第一个ByteBuffer
?
有没有办法一起发送两个缓冲区?我不知道。谢谢
你想要的是在开头为标题保留空间,添加有效负载然后倒退到byte[0]
并制作标题。
我会研究倒带和标记——我完全忘记了这一点。谢谢!
【参考方案1】:
您正在寻找的东西称为“分散-收集 I/O”,它受到 ScatteringByteChannel.read(ByteBuffer[])
和 GatheringByteChannel.write(ByteBuffer[])
的支持。注意数组。 FileChannel
、SocketChannel
和 DatagramSocketChannel
以及管道通道支持这些接口。
【讨论】:
谢谢!我完全忘记了这一点。【参考方案2】:ByteBuffer bbuf = ByteBuffer.allocate(HEADER_SZ + PAYLOAD_SZ);
bbuf.position(HEADER_SZ);
for(int i=0; i < PAYLOAD_SZ; i++)
bbuf.put(payload[i]);
bbuf.rewind();
for(int i=0; i < HEADER_SZ; i++)
bbuf.put(header[i]);
我已经对您的源数据进行字节索引做出了假设。批量放置会更好,但这是一个开始。
【讨论】:
【参考方案3】:我将为这个问题添加另一个答案,因为我今天遇到了这个问题,并且接受的解决方案对我的情况没有那么有用。
为了解决我的问题,我定义了一个int
,它表示ByteBuffer
将保存的数据量(以字节为单位),以及一个Queue<Consumer<ByteBuffer>>
,如下所示:
/**
* An @code int representing the amount
* of bytes that this @link OutgoingPacket
* will send.
*/
private int size;
/**
* A @link Queue that lazily writes data to the
* backing @link ByteBuffer.
*/
private final Queue<Consumer<ByteBuffer>> queue = new ArrayDeque<>();
接下来,我创建了putByte
、putInt
等方法
/**
* Writes a single @code byte to this
* @link Packet's payload.
*
* @param b
* An @code int for ease-of-use,
* but internally down-casted to a
* @code byte.
* @return
* The @link Packet to allow for
* chained writes.
*/
public OutgoingPacket putByte(int b)
size++;
queue.offer(payload -> payload.put((byte) b));
return this;
最后,我创建了一个send
方法,其中分配了ByteBuffer
并传递了相应的数据。
/**
* Transmits this @link OutgoingPacket to
* a specific client.
*
* @param channels
* A variable amount of @link AsynchronousSocketChannels.
*
* TODO: Send to @link Client instead.
*/
public void send(AsynchronousSocketChannel... channels)
/*
* Allocate a new buffer with the size of
* the data being added, as well as an extra
* two bytes to account for the opcode and the
*/
ByteBuffer payload = ByteBuffer.allocate(size + 2);
/*
* Write the opcode to the buffer.
*/
payload.put((byte) opcode);
/*
* Write the length to the buffer.
*/
payload.put((byte) size);
/*
* Add the rest of the data to the buffer.
*/
queue.forEach(consumer -> consumer.accept(payload));
/*
* Flip the buffer so the client can immediately
* read it on arrival.
*/
payload.flip();
/*
* Write the buffer to the channels.
*/
for (AsynchronousSocketChannel channel : channels)
channel.write(payload);
希望这将为将来遇到此问题的人提供见解!
【讨论】:
以上是关于ByteBuffer:写入,然后回到缓冲区的开头并在之前写入的所有数据之前写入(前置?)的主要内容,如果未能解决你的问题,请参考以下文章