Netty:奇怪的 IndexOutOfBoundsException:readerIndex + 长度超过 writerIndex
Netty: Weird IndexOutOfBoundsException: readerIndex + length exceeds writerIndex
我目前正在通过 netty 发送不同的数据包,并且我经常收到这样的异常 在接收它们时:
java.lang.IndexOutOfBoundsException: readerIndex(39) + length(32) exceeds writerIndex(64): UnpooledUnsafeDirectByteBuf(ridx: 39, widx: 64, cap: 2048)
at io.netty.buffer.AbstractByteBuf.checkReadableBytes(AbstractByteBuf.java:1166)
at io.netty.buffer.AbstractByteBuf.readBytes(AbstractByteBuf.java:655)
at <snip>.PacketDataHolder.readString(PacketDataHolder.java:79)
……
或
java.lang.IndexOutOfBoundsException: readerIndex(0) + length(1) exceeds writerIndex(0): UnpooledUnsafeDirectByteBuf(ridx: 0, widx: 0, cap: 2048)
at io.netty.buffer.AbstractByteBuf.checkReadableBytes(AbstractByteBuf.java:1166)
at io.netty.buffer.AbstractByteBuf.readByte(AbstractByteBuf.java:570) ......
这是我发送数据包的方式:
public void sendPacket(Packet packet) {
PacketDataHolder packetDataHolder = new PacketDataHolder(); //I'm creating a packetDataHolder, code is below
packetDataHolder.writeHeader(packet); //Then i'm adding the packet ID as a header
packet.writeData(packetDataHolder); //Then I'm writing the data of the packet (String for example)
socketChannel.writeAndFlush(packetDataHolder.getByteBuf()); //Then I'm sending the packet
}
以及我如何接收和阅读它们:
protected void channelRead0(ChannelHandlerContext ctx, ByteBuf byteBuf) throws Exception {
PacketDataHolder packetDataHolder = new PacketDataHolder(byteBuf); //First I'm creating a PacketDataHolder containing the bytebuf
Packet packet = PacketParser.parsePacket(packetDataHolder); //Then I'm parsing the header I wrote (The packet id)
相关代码如下:
(PacketDataHolder class)
public synchronized void writeHeader(Packet packet) {
this.writeByte(packet.getId());
}
public synchronized byte readHeader() {
return this.readByte();
}
public synchronized void writeByte(int value) {
this.byteBuf.writeByte(value);
}
public synchronized byte readByte() {
return this.byteBuf.readByte();
}
public synchronized void writeString(String value) {
if(value == null)
value = "An error occurred while displaying this message";
byte[] bytes = value.getBytes(StandardCharsets.UTF_8);
if (bytes.length > 32767)
throw new EncoderException("String too big (was " + value.length()
+ " bytes encoded, max 32767)");
else {
writeVarInt(bytes.length);
byteBuf.writeBytes(bytes);
}
}
public synchronized String readString() {
int length = readVarInt();
if (length < 0)
throw new DecoderException(
"The received encoded string buffer length is less than zero! Weird string!");
return new String(byteBuf.readBytes(length).array(),
StandardCharsets.UTF_8);
}
我描述的第一个错误总是发生在 writeString 方法中,所以我认为它与此有关。
我认为主要是在发送很长的字符串时,但我不确定。
如果您需要更多示例代码,请随时询问!
我希望你能帮帮我,
燃烧器
当开始从 ByteBuf 中读取内容时,您是否确保 ByteBuf 中有足够的可读字节。好像你没有。检查 ByteToMessageDecoder 以了解如何在解码器中确保它
您的代码假定您的通信通道正在处理框架。也就是说,它期望当您发送 1024 个字节时,接收处理程序将接收到恰好 1024 个字节的 bytebuf。据我所知,您的代码是直接写入 TCP,这是一种基于流的协议,无法以这种方式工作。如果您写入 1024 个字节,对等方将收到所有 1024 个字节,但字节可能会分段到达 - 例如两次调用 channelRead0(第一个为 300,第二个为 724)。
幸运的是,Netty 包含几个 out-of-the-box 帧编解码器来为您处理这个问题。请参阅 http://netty.io/4.0/xref/io/netty/example/worldclock/WorldClockServerInitializer.html
中的示例
它展示了如何使用 ProtobufVarint32FrameDecoder(确保管道中的下一个处理程序每次始终接收到一帧字节)和 ProtobufVarint32LengthFieldPrepender(负责添加帧长度)配置服务器通道 header 所有外发邮件)
我建议您更改代码以将这些(或类似的成帧编解码器)插入到您的管道中。然后将您的应用程序代码修改为 而不是 调用 readVarInt 和 writeVarInt,因为编解码器会为您处理这件事。接收到的 ByteBuf 的大小将始终与发送的 ByteBuf 的大小匹配,因为解码器负责聚合任何片段。
我目前正在通过 netty 发送不同的数据包,并且我经常收到这样的异常 在接收它们时:
java.lang.IndexOutOfBoundsException: readerIndex(39) + length(32) exceeds writerIndex(64): UnpooledUnsafeDirectByteBuf(ridx: 39, widx: 64, cap: 2048)
at io.netty.buffer.AbstractByteBuf.checkReadableBytes(AbstractByteBuf.java:1166)
at io.netty.buffer.AbstractByteBuf.readBytes(AbstractByteBuf.java:655)
at <snip>.PacketDataHolder.readString(PacketDataHolder.java:79)
…… 或
java.lang.IndexOutOfBoundsException: readerIndex(0) + length(1) exceeds writerIndex(0): UnpooledUnsafeDirectByteBuf(ridx: 0, widx: 0, cap: 2048)
at io.netty.buffer.AbstractByteBuf.checkReadableBytes(AbstractByteBuf.java:1166)
at io.netty.buffer.AbstractByteBuf.readByte(AbstractByteBuf.java:570) ......
这是我发送数据包的方式:
public void sendPacket(Packet packet) {
PacketDataHolder packetDataHolder = new PacketDataHolder(); //I'm creating a packetDataHolder, code is below
packetDataHolder.writeHeader(packet); //Then i'm adding the packet ID as a header
packet.writeData(packetDataHolder); //Then I'm writing the data of the packet (String for example)
socketChannel.writeAndFlush(packetDataHolder.getByteBuf()); //Then I'm sending the packet
}
以及我如何接收和阅读它们:
protected void channelRead0(ChannelHandlerContext ctx, ByteBuf byteBuf) throws Exception {
PacketDataHolder packetDataHolder = new PacketDataHolder(byteBuf); //First I'm creating a PacketDataHolder containing the bytebuf
Packet packet = PacketParser.parsePacket(packetDataHolder); //Then I'm parsing the header I wrote (The packet id)
相关代码如下:
(PacketDataHolder class) public synchronized void writeHeader(Packet packet) { this.writeByte(packet.getId()); }
public synchronized byte readHeader() {
return this.readByte();
}
public synchronized void writeByte(int value) {
this.byteBuf.writeByte(value);
}
public synchronized byte readByte() {
return this.byteBuf.readByte();
}
public synchronized void writeString(String value) {
if(value == null)
value = "An error occurred while displaying this message";
byte[] bytes = value.getBytes(StandardCharsets.UTF_8);
if (bytes.length > 32767)
throw new EncoderException("String too big (was " + value.length()
+ " bytes encoded, max 32767)");
else {
writeVarInt(bytes.length);
byteBuf.writeBytes(bytes);
}
}
public synchronized String readString() {
int length = readVarInt();
if (length < 0)
throw new DecoderException(
"The received encoded string buffer length is less than zero! Weird string!");
return new String(byteBuf.readBytes(length).array(),
StandardCharsets.UTF_8);
}
我描述的第一个错误总是发生在 writeString 方法中,所以我认为它与此有关。 我认为主要是在发送很长的字符串时,但我不确定。
如果您需要更多示例代码,请随时询问! 我希望你能帮帮我, 燃烧器
当开始从 ByteBuf 中读取内容时,您是否确保 ByteBuf 中有足够的可读字节。好像你没有。检查 ByteToMessageDecoder 以了解如何在解码器中确保它
您的代码假定您的通信通道正在处理框架。也就是说,它期望当您发送 1024 个字节时,接收处理程序将接收到恰好 1024 个字节的 bytebuf。据我所知,您的代码是直接写入 TCP,这是一种基于流的协议,无法以这种方式工作。如果您写入 1024 个字节,对等方将收到所有 1024 个字节,但字节可能会分段到达 - 例如两次调用 channelRead0(第一个为 300,第二个为 724)。
幸运的是,Netty 包含几个 out-of-the-box 帧编解码器来为您处理这个问题。请参阅 http://netty.io/4.0/xref/io/netty/example/worldclock/WorldClockServerInitializer.html
中的示例它展示了如何使用 ProtobufVarint32FrameDecoder(确保管道中的下一个处理程序每次始终接收到一帧字节)和 ProtobufVarint32LengthFieldPrepender(负责添加帧长度)配置服务器通道 header 所有外发邮件)
我建议您更改代码以将这些(或类似的成帧编解码器)插入到您的管道中。然后将您的应用程序代码修改为 而不是 调用 readVarInt 和 writeVarInt,因为编解码器会为您处理这件事。接收到的 ByteBuf 的大小将始终与发送的 ByteBuf 的大小匹配,因为解码器负责聚合任何片段。