2014-10-28 1 views
2

나는 다음과 같은 형식으로 메시지를 처리하기 위해 내 handlerscodecs 테스트 할 EmbeddedChannel을 사용하고 있습니다 : 나는 무엇을 달성하고자, 첫째의 Netty ByteToMessageCodec <ByteBuf> 디코딩 메시지를 두 번 (부분적으로)

+------------+------------------+----------------+  
| Header | Payload Length | Payload  | 
| 16 bytes |  2 bytes  | "Some data" |  
+------------+------------------+----------------+ 

:

  1. 헤더 세부 정보를 저장할 개체를 생성하여 처음 16 바이트를 처리하고 나중에 사용하기 위해 ChannelHandlerContextAttributeMap에 디코딩 된 헤더 개체를 추가합니다.
  2. 전체 페이로드 데이터를 기다리거나 검색합니다.
  3. 헤더 객체와 전체 페이로드가 최종 처리기에서 메시지를 라우팅 할 수 있도록 ByteBuf으로 사용할 수 있습니다.

나는 다음과 같은 처리기를 사용하여 :

  1. ByteToMessageCodec<ByteBuf> 헤더 정보를 추출하고 속성 목록에 추가 할 수 있습니다.
  2. LengthFieldBasedFrameDecoder 페이로드 길이를 읽고 전체 프레임을 기다리거나 검색 할 수 있습니다.
  3. SimpleChannelInboundHandler이 속성 목록에서 검색된 헤더 객체를 사용하여 그에 따라 페이로드를 라우팅합니다.

메시지가 ByteToMessageCodecdecode 메서드로 전달되면 헤더가 올바르게 처리되고 추출됩니다. 그런 다음 AttributeMap에 Header 개체를 계속 추가하고 ByteBuf (이는 readableBytes = 2 바이트 (페이로드 길이 표시기 + 페이로드 길이)가 있음)을 추가합니다.

페이로드 길이가 1020 바이트라고 가정 해 봅니다. 메시지는 처음에 codec에 의해 수신되고 readableBytes = 16 bytes + 2 bytes + 1020 bytes이됩니다. 헤더는 decode 메서드에 의해 읽혀지고 나머지 바이트 (1022)는 List<Object> out에 추가됩니다.

내 이해가 정확하면

는, 바이트의 나머지는 이제 길이 표시기를 읽고 SimpleChannelHanlder에 페이로드 (1020 바이트)를 전달합니다 LengthFieldBasedFrameDecoder입니다 다음 핸들러에 전달 될 것입니다,하지만 난 착각한다.

decode 메서드는 에 추가 된 것과 동일한 1022 바이트를 사용하여으로 다시 이라고합니다. 이 디코드 방법의 JavaDoc을에서

은 다음
Decode the from one ByteBuf to an other. This method will be called till either the input ByteBuf 
has nothing to read when return from this method or till nothing was read from the input ByteBuf. 

decodereadableBytes == 0 때까지 호출됩니다 의미합니까?

나머지 메시지를 LengthFieldBasedFrameDecoder으로 전달하는 가장 효율적인 방법은 무엇입니까?

나는 그래서이 readerIndex = 0을 설정하고 List<Object> out에 ByteBuf의 사본을 추가해야이 평균이 수행의 LengthFieldBasedFrameDecoder이 입력으로 ByteBuf 필요 같은데요?

어떤 도움/조언/비판도 받아 들여질 것입니다. 가능한 한 가장 깨끗한 방법으로이 작업을 수행하고 싶습니다.

protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception { 
    byte [] headerBytes = new byte[HEADER_LENGTH]; 
    in.readBytes(headerBytes, 0, HEADER_LENGTH); 

    Header header = new Header(headerBytes); 
    System.out.println("Decoded Header: \n" + header); 

    //Set the header attribute so it can be used by routing handlers 
    ctx.attr(ChannelAttributes.HEADER).getAndSet(header); 
    //pass to next handler 
    out.add(in); 
} 

참고 : 여기에

decode 방법이다 나는 Netty in Action MEAP v8

답변

2

이가 디코드 readableBytes == 0 때까지 호출됩니다 의미합니까 읽고 있어요?

기본적으로 그렇습니다. 입력 버퍼가 소진 될 때까지이

while (in.isReadable()) { 
    int outputSizeBefore = out.size(); 
    int readableBytesBefore = in.readableBytes(); 

    callYourDecodeImpl(ctx, in, out); 

    int outputSizeAfter = out.size(); 
    int readableBytesAfter = in.readableBytes(); 

    boolean didNotDecodeAnything = outputSizeBefore == outputSizeAfter; 
    boolean didNotReadAnything = readableBytesBefore == readableBytesAfter; 

    if(didNotDecodeAnything && didNotReadAnything) { 
     break; 
    } 

    // next iteration, continue with decoding 
} 

그래서, 당신의 디코더가 지속적으로 헤더를 읽을 것 같은 ByteToMessageDecoder의 단순화보기 보인다.

원하는 동작을 얻으려면 true로 isSingleDecode 플래그를 설정해야합니다 : 당신의 디코드 구현 뭔가를 디코딩 한 후

class MyDecoder extends ByteToMessageDecoder { 

    MyDecoder() { 
     setSingleDecode(true); 
    } 

    // your decode impl as before 
} 

또는

MyDecoder decoder = new MyDecoder(); 
decoder.setSingleDecode(true); 

이 루프를 중지합니다. 이제 LengthFieldBasedFrameDecoderout 목록에 추가 된 ByteBuf으로 호출됩니다. 설명대로 프레임 디코딩이 작동하므로 목록에 사본을 추가 할 필요가 없습니다. SimpleChannelInboundHandler은 페이로드 프레임이 msg으로 호출됩니다. ChannelHandlerContext 각 채널 핸들러에 대해 다른 하나이기 때문에

그러나, 당신이 당신의 SimpleChannelInboundHandlerAttributeMap에서 헤더를 읽을 수 없습니다의 atrributes는 공유되지 않습니다.

이 문제를 해결하는 한 가지 방법은 이벤트를 사용하는 것입니다. 대안은 아래 두 개체를 전송하는 것입니다 귀하의 SimpleChannelInboundHandler

class MyMessageHandler extends SimpleChannelInboundHandler<ByteBuf> { 

    private Header header = null; 

    MyMessageHandler() { 
      super(true); 
    } 

    @Override 
    public void userEventTriggered(final ChannelHandlerContext ctx, final Object evt) throws Exception { 
     if (evt instanceof Header) { 
      header = (Header) evt; 
     } else { 
      super.userEventTriggered(ctx, evt); 
     } 
    } 

    @Override 
    protected void channelRead0(final ChannelHandlerContext ctx, final ByteBuf msg) throws Exception { 
     if (header != null) { 
      System.out.println("header = " + header); 
      // continue with header, such as routing... 
     } 
     header = null; 
    } 
} 

과 같이 쓰기, 그리고

// instead of 
// ctx.attr(Header.ATTRIBUTE_KEY).getAndSet(header); 
// do this 
ctx.fireUserEventTriggered(ChannelAttributes.HEADER); 

: 대신 HeaderAttributeMap에 추가로, 이벤트로 보내 당신의 decoder에서 , 파이프 라인을 사용하고 SimpleChannelInboundHandler 대신 ChannelInboundHandlerAdapter을 사용하십시오.당신의 decoder에서 대신 HeaderAttributeMap에 추가로, out에 추가 :

// ... 
out.add(header); 
out.add(in); 

을 그리고, 쓰기 간단하지 않은 메시지를 무시하여 ChannelInboundHandlerLengthFieldBasedFrameDecoder

class MyMessageHandler extends ChannelInboundHandlerAdapter { 
    private Header header = null; 

    @Override 
    public void channelRead(final ChannelHandlerContext ctx, final Object msg) throws Exception { 
     if (msg instanceof Header) { 
      header = (Header) msg; 
      System.out.println("got the header " + header); 
     } else if (msg instanceof ByteBuf) { 
      ByteBuf byteBuf = (ByteBuf) msg; 
      System.out.println("got the message " + msg); 
      try { 
       // continue with header, such as routing... 
      } finally { 
       ReferenceCountUtil.release(msg); 
      } 
     } else { 
      super.channelRead(ctx, msg); 
     } 
    } 
} 

과 같이 ByteBuf s, 이므로 헤더가 전달되고 ( ByteBuf이 구현되지 않은 경우) 이에 도착합니다.. 그런 다음 메시지가 페이로드 프레임으로 디코딩되어 ChannelInboundHandler으로 전달됩니다.

+1

감사합니다. 'setSingleDecode (true)'가 필요한 것입니다. 그러나 'ByteToMessageCodec'을 구현할 때 메서드를 찾지 못하고 ByteToMessageDecoder 만 나타납니다. 상관없이 인코딩 및 디코딩 프로세스가 분리됩니다. 그리고 '헤더'를 다음 핸들러에 전달하는 팁을 주셔서 감사합니다. 나는'ctx.channel(). attr (ChannelAttributes.HEADER) .set (header)'를 실행하여 채널의'AttributeMap'에 그것을 추가함으로써'header'를 설정하고 가져올 수있었습니다. 이 모든 단점은? – Ian2thedv

+1

아, 네,'setSingleDecode'는'ByteToMessageDecoder'에만 존재합니다. 채널의 속성 맵에 대해서는 단점이없는 것으로 나타났습니다. 이는 확실히 실용적인 솔루션입니다. – knutwalker

관련 문제