Netty处理以消息长度开头的ASCII消息的最佳方式?

时间:2014-06-13 06:41:17

标签: netty

我有一个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值的传入消息,如上所示?

由于

1 个答案:

答案 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);
    }
}