【发布时间】:2021-01-13 07:41:18
【问题描述】:
必须用 Netty 实现以下 TCP/IP 协议实现:
Message structure:
The messages are embedded in an STX-ETX frame:
STX MESSAGE ETX
0x02 7b20224d... 0x03
An `escaping` of STX and ETX within the message is not necessary since it is in JSON format
Escape sequence are following:
JSON.stringify({"a":"\x02\x03\x10"})→"{"a\":"\u0002\u0003\u0010\"}"。
这里有更多关于STX, ETX control codes的信息。
消息的长度可能不同,它将采用 JSON 格式,例如:
\0x02{"messageID": "心跳"}\0x03
我的想法是将自定义帧分隔符与 StringEncoder/StringDecoder 结合起来。
对于自定义帧分隔符 -> 使用 0x03 作为分隔符并跳过第一个字节 (0x02)。
于是创建了以下FrameDelimiterDecoder:
@Slf4j
public class FrameDelimiterDecoder extends DelimiterBasedFrameDecoder {
public FrameDelimiterDecoder(int maxFrameLength, ByteBuf delimiter) {
super(maxFrameLength, delimiter);
}
@Override
protected Object decode(ChannelHandlerContext ctx, ByteBuf buffer) throws Exception {
ByteBuf buffFrame = null;
Object frame = super.decode(ctx, buffer);
if (frame instanceof ByteBuf) {
buffFrame = (ByteBuf) frame;
} else {
log.info("frame: {}", frame);
}
if (buffFrame != null) {
buffFrame.writeBytes(buffer.skipBytes(1));
} else {
log.warn("buffer is <null>");
}
return buffFrame;
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
log.error(cause.getMessage(), cause);
}
}
并将其用于初始化:
@Slf4j
@Component
@RequiredArgsConstructor
public class QrReaderChannelInitializer extends ChannelInitializer<SocketChannel> {
private final StringEncoder stringEncoder = new StringEncoder();
private final StringDecoder stringDecoder = new StringDecoder();
private final QrReaderProcessingHandler readerServerHandler;
private final NettyProperties nettyProperties;
@Override
protected void initChannel(SocketChannel socketChannel) {
ChannelPipeline pipeline = socketChannel.pipeline();
pipeline.addLast(new FrameDelimiterDecoder(1024 * 1024, Unpooled.wrappedBuffer(FrameConstant.ETX)));
if (nettyProperties.isEnableTimeout()) {
pipeline.addLast(new ReadTimeoutHandler(nettyProperties.getClientTimeout()));
}
pipeline.addLast(stringDecoder);
pipeline.addLast(stringEncoder);
pipeline.addLast(readerServerHandler);
}
}
但是,它总是失败:
c.s.netty.init.FrameDelimiterDecoder : java.lang.IndexOutOfBoundsException: readerIndex(28) + length(1) exceeds writerIndex(28): PooledUnsafeDirectByteBuf(ridx: 28, widx: 28, cap: 1024)
io.netty.handler.codec.DecoderException: java.lang.IndexOutOfBoundsException: readerIndex(28) + length(1) exceeds writerIndex(28): PooledUnsafeDirectByteBuf(ridx: 28, widx: 28, cap: 1024)
at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:471)
无法理解那里缺少什么。
如何处理 STX-ETX 帧以使用 Netty 进行请求/响应?
【问题讨论】:
标签: java spring-boot sockets tcp netty