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  |

该 |只是作为数据类型的分隔符,而不是真正的任何东西。

【问题讨论】:

创建第二个ByteBufferput 标题然后put 第一个ByteBuffer? 有没有办法一起发送两个缓冲区?我不知道。谢谢 你想要的是在开头为标题保留空间,添加有效负载然后倒退到byte[0]并制作标题。 我会研究倒带和标记——我完全忘记了这一点。谢谢! 【参考方案1】:

您正在寻找的东西称为“分散-收集 I/O”,它受到 ScatteringByteChannel.read(ByteBuffer[])GatheringByteChannel.write(ByteBuffer[]) 的支持。注意数组。 FileChannelSocketChannelDatagramSocketChannel 以及管道通道支持这些接口。

【讨论】:

谢谢!我完全忘记了这一点。【参考方案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&lt;Consumer&lt;ByteBuffer&gt;&gt;,如下所示:

/**
 * 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<>();

接下来,我创建了putByteputInt等方法

/**
 * 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:写入,然后回到缓冲区的开头并在之前写入的所有数据之前写入(前置?)的主要内容,如果未能解决你的问题,请参考以下文章

带有移位字节的新 ByteBuffer (Java)?

一线程一ByteBuffer NIO

[nio] 1 - ByteBuffer

玩转 ByteBuffer

从 ByteBuffer 获取到 byte[] 不写入到 byte[]

JAVA NIO 之Channel