如何管理标识协议的前缀字节序列

时间:2013-08-26 22:40:27

标签: netty

这与我关于统一设置(port unification with persistent channel

的问题有关

我正在尝试为我的协议发送一个前缀为所有流量的双字节序列;我正在这样做,所以当我更新以支持多个协议时,在统一处理程序中有一些东西可以嗅探。

在客户端上,我在管道末尾有一个简单的出站处理程序,它在ByteBuf前面加上两个协议标识字节,在服务器管道前面有一个简单的入站处理程序,它将提取它们。我设法让这个小消息工作。

服务器队列中的后续处理程序是LengthFieldBasedFrameDecoder,我用它来构建传入流量(protobuf对象)。似乎正在发生的是客户端发送大量请求,比如5M。我得到了一系列来自服务器管道的64k缓冲区,最后一个缓冲区超过了LengthFieldBasedFrameDecoder正在等待的阈值,它提取帧并将其传递以进行处理 - 这种情况正确发生。 / p>

此时,一切都破裂了。据我所知,来自客户端的最后64k缓冲区包含帧的其余数据,2字节序列表示下一个请求的开始,然后是一些更多内容。我认为这个数据位于帧解码器中,现在它将使用两个协议魔术字节作为下一帧的长度,这是不正确的,并且事情从那里分解。

DelimiterBasedFrameDecoder看起来在这种情况下会起作用,因为两个字节的序列会将每个逻辑帧/请求分开,但在这种情况下这似乎有些过分。

是否有其他解码器可以在这种情况下工作,或者我应该坚持使用DelimiterBasedFrameDecoder

3 个答案:

答案 0 :(得分:2)

如果你查看LengthFieldBasedFrameDecoder的文档,就会有一个很好的例子来说明如何做你想做的事情。第四个示例在长度字段前面有一个固定宽度的标题,它配置FrameDecoder不剥离任何标题信息。

修改

对于您的服务器,应首先使用LengthFieldBasedFrameDecoder,然后检查标头的处理程序可以删除标头和长度字段并相应地传递数据。

在客户端,您可以使用LengthFieldPrepender,然后添加一个额外的处理程序来预先添加标题数据。

答案 1 :(得分:0)

通常当您使用长度字段帧解码器时,它应该首先在管道中,除非您还使用SSL或压缩处理程序,它将在帧处理程序之前。最简单的解决方案就是将你的协议id处理程序放在帧处理程序之后而不是之前。

答案 2 :(得分:0)

DelimiterBasedFrameDecoder在这种情况下大致是你想要的,但它不会处理分隔符作为框架的前面而不是结束,所以如果你在这种情况下使用它,它基本上会吃你的流量。

使用indexOf中的DelimitedBasedFrameDecoder方法,您可以将一个非常简单的解码器放在一个服务器通道上使用,该通道会在吃掉分隔符时按分隔符顺序分割传入的流量:

public static final class DelimitingProtocolDecoder extends ByteToMessageDecoder {
    private final ByteBuf mDelimiter;

    public DelimitingProtocolDecoder(final ByteBuf theDelimiter) {
        mDelimiter = theDelimiter;
    }

    @Override
    protected void decode(final ChannelHandlerContext theContext, final ByteBuf theInput, final List<Object> theOutput) throws Exception {
        int aIndex = indexOf(theInput, mDelimiter);

        while (aIndex != -1) {
            if (aIndex > 0) {
                ByteBuf aBuffer = theContext.alloc().buffer(aIndex, aIndex);

                theInput.readBytes(aBuffer);

                theOutput.add(aBuffer);
            }

            theInput.skipBytes(mDelimiter.capacity());

            aIndex = indexOf(theInput, mDelimiter);
        }

        theOutput.add(theInput.readBytes(theInput.readableBytes()));
    }
}