참고
채널 파이프라인, 코덱
이벤트 실행
NOTE
데이터를 처리하는 입출력은 Netty가 이벤트로 관리하기 떄문에 이벤트 핸들러만 구현하면 된다.
•
소켓 채널 데이터 수신
1.
Netty 이벤트 루프가 채널 파이프라인에 등록된 이벤트 핸들러를 가져온다.
2.
이벤트 메서드가 구현되어 있으면 실행
3.
마지막 이벤트 핸들러에 도달할 때 까지 다음 이벤트 핸들러를 가져와 1~2 반복
채널 파이프 라인
NOTE
입력 출력을 전달할 수 있도록 파이프라이닝 한다.
채널과 이벤트 핸들러 사이의 통로 역할을 수행
채널 파이프 라인 구조
등록
bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer<SocketChannel>() { // 1
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception { // 2
ChannelPipeline pipeline = socketChannel.pipeline(); // 3
pipeline.addLast(new LoggingHandler(LogLevel.INFO)); // 4
pipeline.addLast(new EchoServerHandler()); // 4
}
});
Java
복사
1.
childrenHandler 메소드를 통해서 설정
2.
initChannel은 클라이언트 소켓 채널이 생성될 때 호출됨
3.
Netty 내부에서 할당한 빈 채널 파이프라인 가져오기
4.
파이프라인에 이벤트 핸들러 등록
초기화 순서
@Component
@RequiredArgsConstructor
public class ImageNettyChannelInitializer extends ChannelInitializer<SocketChannel> {
private final ImageNettyInboundHandler imageNettyInboundHandler;
@Override
protected void initChannel(SocketChannel ch) {
ChannelPipeline pipeline = ch.pipeline();
ByteBuf delimiter = Unpooled.copiedBuffer("\n", CharsetUtil.UTF_8);
int maxFrameLength = 5 * 1024 * 1024; // 5MB
pipeline.addLast(new DelimiterBasedFrameDecoder(maxFrameLength, delimiter));
pipeline.addLast(imageNettyInboundHandler);
}
}
Java
복사
1.
클라이언트가 서버 소켓에 접속 요청
2.
해당 연결을 대응하는 클라이언트 소켓 채널 객체 생성
3.
빈 채널 파이프라인 객체 생성, 클라이언트 소켓 채널에 할당
4.
클라이언트 소켓 채널에 등록된 ChannelInitalizer객체를 가져와서 initChannel 호출
5.
클라이언트 소켓 채널에 할당되어있는 파이프라인을 가져와서 이벤트 핸들러 등록
인 바운드이벤트 핸들러
NOTE
연결 상대가 어떤 동작을 취했을 떄 발생하는 이벤트
•
채널 활성화, 데이터 수신 등
•
Bottom-UP 형식으로 동작하기 때문에, 가장 먼저 등록한 Handler부터 마지막에 등록한 Handler 순서로 동작
•
이벤트 순서
메서드 | 설명 |
channelRegistered | Channel이 EventLoop에 등록되고 입출력을 처리할 수 있으면 호출됨 |
channelUnregistered | Channel이 EventLoop에서 등록 해제되고 입출력을 처리할 수 없으면 호출됨 |
channelActive | Channel의 연결과 바인딩이 완료되어 활성화되면 호출됨 |
channelInactive | Channel이 활성 상태에서 벗어나 로컷 피어에 대한 연결이 해제되면 호출됨 |
channelReadComplete | Channel에서 읽기 작업이 완료되면 호출됨 |
channelRead | Channel에서 데이터를 읽을 때 호출됨 |
channelWritabilityChanged | Channel의 기록 가능 상태가 변경되면 호출된다. |
userEventTriggered | POJO가 ChannelPipeline을 통해서 전달돼서 ChannelInboundHandler.fireUserEventTriggered()가 트리거되면 호출됨. |
참고코드
아웃바운드 이벤트 핸들러
NOTE
소켓 채널에서 발생한 이벤트 중 프로그래머가 요청한 동작에 해당하는 이벤트
•
연결 요청, 데이터 요청, 소켓 닫기 등
•
Top-Down 형식으로 동작하기 떄문에, 가장 마지막에 등록한 Handler부터 가장 먼저 등록한 Handler 순서로 동작
•
이벤트 순서
메서드 | 설명 |
bind(ChannelHandlerContext, SocketAddress, ChannelPromise) | Channel을 로컬 주소로 바인딩 요청 시 호출됨 |
connect(ChannelHandlerContext, SocketAddress, SocketAddress, ChannelPromise) | Channel을 원격 피어로 연결 요청 시 호출됨 |
disconnect(ChannelHandlerContext, ChannelPromise) | Channel을 원격 피어로부터 연결 해제 요청 시 호출됨 |
close(ChannelhandlerContext, ChannelPromise) | Channel을 닫는 요청 시 호출됨 |
deregister(ChannelHandlerContext, ChannelPromise) | Channel을 EventLoop에서 등록 해제 요청 시 호출됨 |
read(ChannelHandlerContext) | Channel에서 데이터를 읽기 요청 시 호출 |
flush(ChannelHandlerContext) | Channel을 통해 원격 피어로 큐에 있는 데이터의 플러시 요청 시 호출됨 |
write(ChannelHandlerContext, Object, ChannelPromise) | Channel을 통해 원격 피어로 데이터 기록 요청 시 호출됨 |
참고코드
이벤트 이동 경로 및 메서드 실행
NOTE
•
서로 다른 이벤트 메서드를 구현한 이벤트 핸들러 등록
◦
이벤트 핸들러 등록 순서에 관계없이 이벤트 순서에 따라 실행
•
같은 이벤트 메서드를 구현한 이벤트 핸들러 등록
◦
먼저 등록된, 이벤트 핸들러의 메서드만 호출되고 이벤트를 소모함
◦
두번째 이벤트 핸들러의 메서드도 호출하고 싶으면 첫번째 이벤트 핸들러에서 ctx.fireChannelRead()같은 방법으로 직접 이벤트 발생시켜 넘겨줌
ChannelPipeline p = channel().pipeline();
p.addLast("1", new InboundReadHandler());
p.addLast("2", new InboundActiveHandler());
p.addLast("3", new OutboundWriteHandler());
p.addLast("4", new OutboundWriteHandler());
p.addLast("5", new ChannelDuplexHandler());
Java
복사
2 → 1 → 5 → 4 → 3
코덱
NOTE
•
인코더
◦
전송할 데이터를 전송 프로토콜에 맞춰 변환
◦
ChannelOutboundHandler
•
디코더
◦
수신한 데이터를 전송 프로토콜에 맞춰 변환
◦
ChannelInboundHandler