2017-04-20 2 views
2

파일 및 구조화 된 메시지를 전송할 수있는 프로토콜에 대해 netty 파이프 라인을 작성하고 있습니다. 파일 전송은 구조화 된 메시지 (핸드 셰이크)와 파일을 나타내는 바이트 스트림으로 시작됩니다.내 netty 파이프 라인에서 디코딩하는 동안 정확히 5 바이트를 잃어 버리는 이유는 무엇입니까?

  +---------+         +---------+ 
      | Client |         | Server | 
      +---------+         +---------+ 
       |            | 
       | Connect (1)         | 
       |--------------------------------------------->| 
       |            | 
       | Handshake to announce incoming file (2)  | 
       |--------------------------------------------->| 
       |            | 
       |    Acknowledge file transfer (3) | 
       |<---------------------------------------------| 
       |            | 
       | Send file (4)        | 
       |--------------------------------------------->| 

프로토콜 메시지는 다음과 같습니다 : 입력

  +---------+----------------+----------------+ 
      | Length |  Type  | Actual Message | 
      | 4 bytes |  1 byte  | N bytes  | 
      +---------+----------------+----------------+ 

들어오는 파일에 대한 정확한 메시지 흐름 (서버, 클라이언트 다른 소프트웨어 내 그물코 구현되는) 다음과 같습니다 핸드 셰이크 메시지의 경우 Actual Message은 단일 Long 값인 token으로 구성됩니다.

public class ProtocolDecoder extends ReplayingDecoder<State> { 

     private Integer msgType; 
     private Long token; 


     public enum State { 
     LENGTH, 
     MSG_TYPE, 
     TOKEN 
     } 

     public ProtocolDecoder() { 
     super(State.LENGTH); 
     } 

     @Override 
     protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception { 

     switch (state()) { 

      case LENGTH: 
      // (2) read the message length from the handshake 
      long messageLength = in.readUnsignedIntLE(); 
      checkpoint(State.MSG_TYPE); 

      case MSG_TYPE: 
      // (2) read the message type from the handshake 
      msgType = in.readBoolean(); 
      checkpoint(State.TOKEN); 

      case TOKEN: 
      try { 
       // (2) read the token from the handshake 
       token = in.readUnsignedIntLE(); 
       // (3) write back the acknowledgement 
       ctx.channel().writeAndFlush(new Acknowledgement(token)); 
       // (4) done reading the protocol message 
       // now switch to the file protocol 
       ctx.pipeline().addLast(new FileInboundHandler()); 
       // write everything that is still in the buffer to the 
       // modified pipeline 
       ByteBuf rest = in.readBytes(super.actualReadableBytes()); 
       out.add(rest); 
       // remove the protocol handshake decoder and pass 
       // the rest of this channel to the `FileInboundHandler` 
       ctx.pipeline().remove(this); 
      } finally { 
       reset(); 
      } 
      break; 
      default: 
      throw new Error("Shouldn't reach here."); 
     } 
     } 

     private void reset() { 
     token = null; 
     msgType = null; 
     } 

FileInboundHandler 단순히 파일을 생성하고 모든 ByteBuf들 기록 :

다음은 ReplayingDecoder입니다.

이 문제는 원칙적으로 각 파일이 처음부터 정확히 5 바이트를 놓치는 유일한 문제로 작동합니다.

나는이 개 질문이 :

1) I 파이프 라인의 첫 번째 핸들러로 LoggingHandler을 넣으면, 내가 소켓의 모든 트래픽이 기록되어 있는지 확인 할 수 있습니다, 내 디코더는 버그가있는 경우에 상관없이?

2) ctx.channel().writeAndFlush()을 호출 할 때 "아웃 바운드"버퍼 만 플러시합니까? 즉, 인바운드 버퍼에서 아직 소비하지 않은 바이트를 플러시하지 않는다는 것을 의미합니까?

답변

1

각 스위치 케이스의 문장 끝에 break;을 넣는 것을 잊어 버리셨습니까? 지금부터는 처음에 LENGTH에서 유형과 토큰 읽기로 넘어가는데, 부울 (1 바이트)과 토큰 (unsigned int, 4 바이트로 읽음)이 5 바이트가 너무 많습니다.

+0

나는 이전 버전의 디코더에서 (모든 경우에';;를 사용한다.) 시도했지만, 그 행동은 동일했다. 또한 [ReplayingDecoder 설명서] (https://github.com/firebase/netty/blob/master/codec/src/main/java/io/netty/handler/codec/ReplayingDecoder.java#L149-L199)는 너무 메커니즘을 통해 가을 - 나는 그 체크 포인트 뒤에 아이디어라고 생각. – pulse00

+0

@ pulse00 음, 이해가가는 것 같습니다. 결국 한 번에 헤더를 읽을 수 있고 끝에있는 파이프 라인에서 디코더가 제거됩니다. 파일 처리기는 무엇입니까?이 프로토콜 헤더가 포함되어 있습니까? –