我有一个Netty 4.x应用程序需要发送和接收以固定长度(10位,零填充)字段开头的ASCII消息,其中包含字符数的消息大小。消息如下:
0000000059{message_info={message_type=login}|login_id=abc|password=}
0000000114{message_info={message_type=pricefeed_toggle}|instrument_id={feedcode=1234|market=xyz}|toggle=true|best_only=true}
我看到使用LengthFieldPrepender和LengthFieldBasedFrameDecoder的示例是放置二进制而不是ASCII的大小。
消息不是由CR / LF或其他字符分隔的。
还有一种最好的方法来处理基于可能的message_type值的传入消息,如上所示?
由于
答案 0 :(得分:1)
有同样的问题,我的解决方案是扩展LengthFieldBasedFrameDecoder并覆盖getUnadjustedFrameLength方法。这是我的班级:
public class StringLengthFieldBasedFrameDecoder extends LengthFieldBasedFrameDecoder {
private Charset charset;
public StringLengthFieldBasedFrameDecoder(
int maxFrameLength,
int lengthFieldOffset, int lengthFieldLength) {
this(maxFrameLength, lengthFieldOffset, lengthFieldLength, 0, 0);
}
public StringLengthFieldBasedFrameDecoder(
int maxFrameLength,
int lengthFieldOffset, int lengthFieldLength,
int lengthAdjustment, int initialBytesToStrip) {
this(
maxFrameLength,
lengthFieldOffset, lengthFieldLength, lengthAdjustment,
initialBytesToStrip, true);
}
public StringLengthFieldBasedFrameDecoder(
int maxFrameLength, int lengthFieldOffset, int lengthFieldLength,
int lengthAdjustment, int initialBytesToStrip, boolean failFast) {
this(
ByteOrder.BIG_ENDIAN, maxFrameLength, lengthFieldOffset, lengthFieldLength,
lengthAdjustment, initialBytesToStrip, failFast, Charset.forName("US-ASCII"));
}
public StringLengthFieldBasedFrameDecoder(
ByteOrder byteOrder, int maxFrameLength, int lengthFieldOffset, int lengthFieldLength,
int lengthAdjustment, int initialBytesToStrip, boolean failFast, Charset charset) {
super(byteOrder, maxFrameLength, lengthFieldOffset, lengthFieldLength, lengthAdjustment, initialBytesToStrip, failFast);
this.charset = charset;
}
/**
* Decodes the specified region of the buffer into an unadjusted frame length. This implementation will
* read a String of length bytes from the ByteBuf at the given offset. This string will then be parsed into a
* long using the charset specified on initialization (default "US-ASCII"). Note that this method must not
* modify the state of the specified buffer (e.g. {@code readerIndex}, {@code writerIndex}, and the content of
* the buffer.)
*
* @throws io.netty.handler.codec.DecoderException if failed to decode the specified region
*/
protected long getUnadjustedFrameLength(ByteBuf buf, int offset, int length, ByteOrder order) {
try {
return Long.parseLong(buf.toString(offset, length, charset));
} catch (NumberFormatException nfe) {
throw new DecoderException(nfe);
}
}
}
还有一些测试代码:
public class StringLengthFieldBasedFrameDecoderTest {
@DataProvider
private static final Object[][] getTestData() throws UnsupportedEncodingException {
try {
String message = "Hello World!";
ByteBuf asciiLength = Unpooled.buffer();
asciiLength.writeBytes(String.format("%08d", message.length()).getBytes("US-ASCII"));
asciiLength.writeBytes(message.getBytes("US-ASCII"));
ByteBuf utf16LELength = Unpooled.buffer();
utf16LELength.writeBytes(String.format("%08d", message.length()).getBytes("UTF-16LE"));
utf16LELength.writeBytes(message.getBytes("US-ASCII"));
return new Object[][]{
{new StringLengthFieldBasedFrameDecoder(1024, 0, 8, 0, 8), asciiLength, message},
{new StringLengthFieldBasedFrameDecoder(ByteOrder.nativeOrder(), 1024, 0, 16, 0, 16, true, Charset.forName("UTF-16LE")), utf16LELength, message}
};
} catch (UnsupportedEncodingException uee) {
System.out.println(uee.getMessage());
throw uee;
}
}
@Test(dataProvider = "getTestData")
public void testReturnsCorrectMessage(StringLengthFieldBasedFrameDecoder decoder, ByteBuf buffer, String expectedMessage) {
EmbeddedChannel channel = new EmbeddedChannel(decoder);
channel.writeInbound(buffer);
Assert.assertTrue(channel.finish());
ByteBuf input = (ByteBuf) channel.readInbound();
assertEquals(input.toString(0, input.readableBytes(), Charset.forName("US-ASCII")), expectedMessage);
}
}