【问题标题】:Best way for Netty to handle ASCII messages beginning with message length?Netty 处理以消息长度开头的 ASCII 消息的最佳方式?
【发布时间】:2017-08-16 23:13:18
【问题描述】:

我有一个 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 值处理传入消息的最佳方法吗?

谢谢

【问题讨论】:

    标签: netty


    【解决方案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);
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2018-10-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-03-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多