forked from netty/netty
-
Notifications
You must be signed in to change notification settings - Fork 0
코덱, 코덱의 구조, 기본 제공 코덱
Gump edited this page Aug 22, 2019
·
6 revisions
바이트 형태의 데이터를 애플리케이션에서 해석 가능한 데이터 포맷으로 변환하는 인코딩 과정과 분석하는 디코딩 과정이 필요
- 인코더와 디코더
- 수신 : XXX-encoded Stream -> Decoder -> ByteBuf
- ChannelInboundHandler 인터페이스를 구현
- MessageToByteDecoder : 바이트 스트림을 메세지(특정 의미를 나타내는 바이트 구조) 형태로 디코딩
- MessageToMessageDecoder : 메세지를 다른 메세지 유형으로 디코딩
- 송신 : ByteBuf -> Encoder -> XXX-encoded Stream
- ChannelOutboundHandler 인터페이스를 구현
- MessageToByteEncoder : 메세지를 바이트 스트림 형태로 인코딩
- MessageToMessageEncoder : 메세지를 다른 메세지 형태로 인코딩
- 수신 : XXX-encoded Stream -> Decoder -> ByteBuf
- 추상 코덱
- 인바운드/아웃바운드 데이터와 메세지 변환을 한 클래스에서 관리
- ChannelInboundHandler, ChannelOutboundHandler를 모두 구현
- ByteToMessageCodec : ByteToMessageDecoder + MessageToByteEncoder
- MessageToMessageCodec : MessageToMessageDecoder + MessageToMessageEncoder
- 템플릿 메서드 패턴으로 구현
<Base64Encoder.java 코드>
package io.netty.handler.codec.base64;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPipeline;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.Delimiters;
import io.netty.handler.codec.MessageToMessageEncoder;
import java.util.List;
// 여러 채널간에 안전하게 공유할 수 있음 (???)
@Sharable
// MessageToMessageEncoder 상속
public class Base64Encoder extends MessageToMessageEncoder<ByteBuf> {
private final boolean breakLines;
private final Base64Dialect dialect;
public Base64Encoder() {
this(true);
}
public Base64Encoder(boolean breakLines) {
this(breakLines, Base64Dialect.STANDARD); // Base64Dialect.STANDARD : RFC3548
}
public Base64Encoder(boolean breakLines, Base64Dialect dialect) {
if (dialect == null) {
throw new NullPointerException("dialect");
}
this.breakLines = breakLines;
this.dialect = dialect;
}
// encode 추상 메서드를 구현
@Override
protected void encode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) throws Exception {
out.add(Base64.encode(msg, msg.readerIndex(), msg.readableBytes(), breakLines, dialect));
}
}
<MessageToMessageEncoder.java 코드>
package io.netty.handler.codec;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelOutboundHandler;
import io.netty.channel.ChannelOutboundHandlerAdapter;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.ChannelPromise;
import io.netty.util.ReferenceCountUtil;
import io.netty.util.ReferenceCounted;
import io.netty.util.internal.StringUtil;
import io.netty.util.internal.TypeParameterMatcher;
import java.util.List;
// ChannelOutboundHandlerAdapter 클래스 상속
public abstract class MessageToMessageEncoder<I> extends ChannelOutboundHandlerAdapter {
private final TypeParameterMatcher matcher;
protected MessageToMessageEncoder() {
matcher = TypeParameterMatcher.find(this, MessageToMessageEncoder.class, "I");
}
protected MessageToMessageEncoder(Class<? extends I> outboundMessageType) {
matcher = TypeParameterMatcher.get(outboundMessageType);
}
public boolean acceptOutboundMessage(Object msg) throws Exception {
return matcher.match(msg);
}
// write 메서드를 오버라이드
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
CodecOutputList out = null;
try {
if (acceptOutboundMessage(msg)) {
out = CodecOutputList.newInstance();
@SuppressWarnings("unchecked")
I cast = (I) msg;
try {
// 코덱 구현체의 encode 메서드를 호출
encode(ctx, cast, out);
} finally {
ReferenceCountUtil.release(cast);
}
if (out.isEmpty()) {
out.recycle();
out = null;
throw new EncoderException(
StringUtil.simpleClassName(this) + " must produce at least one message.");
}
} else {
ctx.write(msg, promise);
}
} catch (EncoderException e) {
throw e;
} catch (Throwable t) {
throw new EncoderException(t);
} finally {
if (out != null) {
final int sizeMinusOne = out.size() - 1;
if (sizeMinusOne == 0) {
ctx.write(out.get(0), promise);
} else if (sizeMinusOne > 0) {
// Check if we can use a voidPromise for our extra writes to reduce GC-Pressure
// See https://github.com/netty/netty/issues/2525
ChannelPromise voidPromise = ctx.voidPromise();
boolean isVoidPromise = promise == voidPromise;
for (int i = 0; i < sizeMinusOne; i ++) {
ChannelPromise p;
if (isVoidPromise) {
p = voidPromise;
} else {
p = ctx.newPromise();
}
ctx.write(out.getUnsafe(i), p);
}
ctx.write(out.getUnsafe(sizeMinusOne), promise);
}
out.recycle();
}
}
}
// encode 메서드를 추상 메서드로 정의
protected abstract void encode(ChannelHandlerContext ctx, I msg, List<Object> out) throws Exception;
}
- 자주 사용되는 프로토콜의 인코더와 디코더를 기본 제공
- netty 4.1 기준
패키지 | 설명 |
---|---|
io.netty.handler.codec.base64 | Base64 인코딩 데이터 송수신 지원 |
io.netty.handler.codec.bytes | 바이트 배열 데이터 송수신 지원 |
io.netty.handler.codec.compression | 압축 데이터 송수신 지원 |
io.netty.handler.codec.dns | DNS 지원 (@UnstableApi) |
io.netty.handler.codec.haproxy | HAProxy의 프록시 프로토콜 지원 |
io.netty.handler.codec.http | HTTP 프로토콜 지원 |
io.netty.handler.codec.http.cookie | HTTP 프로토콜의 쿠키 송수신 지원 |
io.netty.handler.codec.http.cors | HTTP 프로토콜의 CORS 송수신 지원 |
io.netty.handler.codec.http.multipart | HTTP 프로토콜 multipart 송수신 지원 |
io.netty.handler.codec.http.websocketx | 웹 소켓 지원 |
io.netty.handler.codec.http2 | HTTP 프로토콜 지원 |
io.netty.handler.codec.json | JSON 인코딩 데이터 수신 지원 |
io.netty.handler.codec.marshalling | JBoss 마샬링 송수신 지원 |
io.netty.handler.codec.memcache | Memcached 프로토콜 지원 (@UnstableApi) |
io.netty.handler.codec.mqtt | MQTT 프로토콜 지원 |
io.netty.handler.codec.protobuf | Google Protocol Buffer 송수신 지원 |
io.netty.handler.codec.redis | RESP (REdis Serialization Protocol) 지원 |
io.netty.handler.codec.rtsp | RTSP (Real Time Streaming Protocol) 지원 |
io.netty.handler.codec.sctp | SCTP (SCTP/IP) 지원 |
io.netty.handler.codec.serialization | 객체 직렬화 송수신 지원 |
io.netty.handler.codec.smtp | SMTP (Simple Mail Transfer Protocol) 프로토콜 지원 |
io.netty.handler.codec.socks | SOCKS 지원 |
io.netty.handler.codec.spdy | Google SPDY (SPeeDY) 지원 |
io.netty.handler.codec.stomp | STOMP (Streaming Text Oriented Messaging Protocol) 지원 |
io.netty.handler.codec.string | 문자열 데이터 송수신 지원 |
o.netty.handler.codec.xml | XML 데이터 송수신 지원 |