netty 로 TCP 클라이언틀 개발하고 있습니다. encoder, decoder 구성 등에 대해 고민 중입니다.

1,394 views
Skip to first unread message

삼다수

unread,
Jan 17, 2019, 9:26:15 PM1/17/19
to Netty Korean User Group
파트너사의 TCP 서버에 요청 / 응답을 받는 클라이언트를 netty 로 개발해보고자 합니다. netty 를 처음 접하다 보니 고민이 많네요.
아래와 같은 형태로 전체 전문길이 4byte + 본문의 형태로 데이터를 수신하게 됩니다. 
이런 경우 어떤 형태로 encoder , decoder 를 구성하면 될까요 ?

전문길이 4byte|필드1 4byte|필드2 7byte|필드3 4byte
1000TEST1234567ABCD

pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4));

pipeline.addLast("decoder", new StringDecoder());

pipeline.addLast("encoder", new StringEncoder());

pipeline.addLast("handler", new MessageReceiveHandler()); // 비지니소 로직 처리 


위와 같은 형태로 처리하면 되지 않을까 생각하고 있는데 netty 선배님들의 조언 부탁드리겠습니다. 


감사합니다. 

Message has been deleted

삼다수

unread,
Jan 17, 2019, 9:47:59 PM1/17/19
to Netty Korean User Group

* LengthFieldBasedFrameDecoder - http://tinyurl.com/9rzasc
       * 메시지상의 길이 필드를 보고 프레임을 디코드

   * LengthFieldPrepender - http://tinyurl.com/852d57
       * 메시지에 메시지의 길이를 나타내는 바이너리 헤더를 추가


netty release 문서에 요런내용이 있는걸 보니 일단 방향은 제대로 잡은 것 같네요. 


LengthFieldBasedFrameDecoder 생성자 파라메터 마지막 2 값이 선뜻 이해가 안가는데요. 혹시 설명해주실 있는 계실까요? 


@param lengthAdjustment - 길이 필드의 값에 더하는 보정

@param initialBytesToStrip - 디코드 프레임으로부터 스트립하는 최초의 바이트



2019년 1월 18일 금요일 오전 11시 26분 15초 UTC+9, 삼다수 님의 말:

yong ki Lee

unread,
Jan 17, 2019, 10:04:01 PM1/17/19
to Netty Korean User Group
LengthFieldBasedFrameDecoder  는 Length 헤더 부분이 숫자 자료형이여야 합니다.

삼다수님께서 작성해주신 전문의 경우 

전문길이 4byte|필드1 4byte|필드2 7byte|필드3 4byte
1000TEST1234567ABCD

전문길이에 해당하는 자료가 숫자형이여야 한다는 의미 입니다. 얼핏 제가 보기엔 전문앞 4byte 를 읽어 숫자로 변환해서
길이를 처리하라는 것으로 보이는데요 만일 그렇다면 LengthFieldBasedFrameDecoder  는 사용해서는 안됩니다.

전체 전문이 String 형태로 확인되신다면 보통의 경우 줄바꿈 (/n/r) 로 처리되는 것이 보통인데요 
이럴경우라면 Delimiterbasedframedecoder 를 추천드립니다.




2019년 1월 18일 금요일 오전 11시 26분 15초 UTC+9, 삼다수 님의 말:
파트너사의 TCP 서버에 요청 / 응답을 받는 클라이언트를 netty 로 개발해보고자 합니다. netty 를 처음 접하다 보니 고민이 많네요.

이규남

unread,
Jan 17, 2019, 11:15:37 PM1/17/19
to Netty Korean User Group
와.. 답글 달았는데 답글이 안달렸네요;;
다시 쓸께요. ㅠㅠ

저 같은 경우 가변데이터 받을 땐 ReplayingDecoder 사용했습니다.
바이너리 형태로 데이터가 넘어올 땐 이게 제일 좋더라고요.
사용방법은 간단히 아래와 같습니다.

public SamdasuDecoder extends ReplayingDecoder {
 
Enum MSG {LENGTH, F1, F2, F3, BODY };

 
@Override
 
public void decode (...) throws Excpetion {
   
switch (state()) {
     
case LENGTH:
        length
= in.readInt();
        checkpoint
(F1);
      case F1:
       
.... read
        checkpoint
(F2);
      case F2:
       
.... read
        checkpoint
(F3);
      case F3:
       
.... read
        checkpoint
(BODY);
      case BODY:
       
byte[] buf = new byte[length];
       
in.readBytes(buf);
        out.add(buf);

   
}
 
}
}

이렇게 하시면 원하시는 데이터 모두 받을 수 있습니다.

2019년 1월 18일 금요일 오전 11시 26분 15초 UTC+9, 삼다수 님의 말:
파트너사의 TCP 서버에 요청 / 응답을 받는 클라이언트를 netty 로 개발해보고자 합니다. netty 를 처음 접하다 보니 고민이 많네요.

삼다수

unread,
Jan 17, 2019, 11:58:02 PM1/17/19
to Netty Korean User Group
말씀주신 대로 전체 전문이 String 이지만 개행 되지는 않습니다. 
Delimiterbasedframedecoder 도 살펴봐야겠네요. 감사합니다 !!

2019년 1월 18일 금요일 오후 12시 4분 1초 UTC+9, YK2 님의 말:

삼다수

unread,
Jan 18, 2019, 12:05:34 AM1/18/19
to Netty Korean User Group
좋은 예시 감사합니다. 코드를 보니 몇가지 궁금한 부분이 생기는데요... 
ReplayingDecoder 를 사용하면 전체 전문이 두 번이상으로 끊어져서 들어오는 경우에도 처리가 가능할까요? 
또 한가지는 예시 코드에서 checkpoint 가 읽기를 완료한 부분의 index 를 저장해두는 역할을 하는 듯 한데... 
해당 필드의 length 는 어떤식으로 알 수 있는 건가요? 코드 내용만으로는 그 부분이 이해가 되질 않아서요. 

감사합니다 !! 


2019년 1월 18일 금요일 오후 1시 15분 37초 UTC+9, 이규남 님의 말:

이규남

unread,
Jan 18, 2019, 12:31:46 AM1/18/19
to Netty Korean User Group
ReplayingDecoder의 경우 ByteToMessageDecoder를 상속받아 Byte 메세지를 모두 다 받을 수 있도록 구현돼 있습니다.
in.readInt() 와 같은 함수를 사용할 경우 4바이트가 모두 다 받아질때까지 대기입니다.
(ByteToMessageDecdoer의 경우 in.readableBytes() 체크 후 in.readInt()로 읽어올 기능을 알아서 해줍니다)

그렇기 때문에 끊어져서 오더라도 삼다수님이 정의해놓은 byte만큼 받을때까지 대기를 타고 있습니다.
length를 보내고나서 f1을 보낼 때 3초 쉬었다 보내도 decoder에서는 f1을 받으려고 대기중이겟죠.

그리고 length의 경우 멤버변수로 그냥 가지고 있으면 됩니다.
public SamdasuDecdoer extends ReplayingDecdoer {
 
private int length;
}
이걸 가져다 쓰면 되죠!

삼다수

unread,
Jan 18, 2019, 1:11:26 AM1/18/19
to Netty Korean User Group
상세한 설명 감사합니다. 조언 주신 내용 참고해서 ReplayingDecoder 로 구현해봐야겠네요. 
완전 감사합니다 +______+!!

2019년 1월 18일 금요일 오후 2시 31분 46초 UTC+9, 이규남 님의 말:

삼다수

unread,
Jan 21, 2019, 3:33:48 AM1/21/19
to Netty Korean User Group
ReplayingDecoder 로 전문을 받은 후 다른 처리를 위해서 다음 handler 로 넘기려면 어떻게 처리해야 하나요? 
별도의 이벤트를 트리거주어야 다른 handler 에서 처리 가능한 걸까요 ? 

pipeLine 에는 2개의 handler 를 추가해뒀는데 앞순서의 replayingDecoder 만 수행되네요


2019년 1월 18일 금요일 오후 2시 31분 46초 UTC+9, 이규남 님의 말:
ReplayingDecoder의 경우 ByteToMessageDecoder를 상속받아 Byte 메세지를 모두 다 받을 수 있도록 구현돼 있습니다.

이규남

unread,
Jan 21, 2019, 3:46:27 AM1/21/19
to Netty Korean User Group
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object>out) throws Exception {
  int message = in.readInt();
  int bodys   = in.readInt();
  // recv
 
// passing
 
out.add(....);

}


위처럼 3번째 인자인 out에 add를 하면 decoder 다음의 handler로 데이터가 넘어가게 됩니다.
주로 ChannelInboundHandler를 사용하실테니 channelRead에서 처리하시면 됩니다.

삼다수

unread,
Jan 21, 2019, 5:00:45 AM1/21/19
to Netty Korean User Group

decode 메서드에서 out.add(pojo); 하고 

pipeline.addLast(new Decoder1()); 
pipeline.addLast(new Decoder2()); 
로 handler 를 추가해뒀는데 Decoder2 의 channelRead 로만 인입이 되고 있어서... 뭔가 더 event 를 트리거해야 하나 했네요. 
흠 의아하군요... 어쩌서 Decoder1 의 channelRead 가 처리되지 못하는지...


2019년 1월 21일 월요일 오후 5시 46분 27초 UTC+9, 이규남 님의 말:

이규남

unread,
Jan 21, 2019, 5:08:51 AM1/21/19
to Netty Korean User Group
음?
pipeline.addLast(new replaying());
pipeline.addLast(new handler1());
pipeline.addLast(new handler2());

이렇게 돼 있으면 handler1으로 가야 합니다.
오히려 handler2로 넘길려면 handler1의 channelRead에서 fire로 넘겨야 하는데....
다시 한 번 소스를 쭉 보시고 테스트 해보셔요.

삼다수

unread,
Jan 21, 2019, 5:59:54 AM1/21/19
to Netty Korean User Group
넵 ~ 조언 감사합니다. 다시 찬찬히 확인해봐야겠네요 

2019년 1월 21일 월요일 오후 7시 8분 51초 UTC+9, 이규남 님의 말:
Reply all
Reply to author
Forward
0 new messages