위 주소에서 직접 볼 수 있고, [SPC]를 누르면 다음 슬라이드로, [Shift-SPC]를 누르면 이전 슬라이드로 이동합니다.
주의: 커서키로 이동하면 슬라이드를 건너뛰는 경우도 있습니다.
Netty의 기본 설명 문제 풀이 예제 이해 / 실습 개발
매시간 직접 작성하셔야 하는 (간단한) 코드가 있습니다.
이 자료와 실습은 4.1.6.Final을 기준으로 합니다.
모든 상담원이 통화중입니다.
연결될 때까지 기다리기 전화해달라는 기록 남기고 끊기Blocking call
// 동기 방식 void greeting(Context ctx) { String req = ctx.readLine(); ctx.write("안녕, " + req); System.out.println("완료"); }
Non-blocking call
// 비동기 방식 void greeting(Context ctx) { ctx.readLine().done(line -> { ctx.write("안녕, " + req); }); System.out.println("완료"); }
$("button").on("click", function(event) { console.log(event); alert("클릭"); });
어? 그럼 그냥 Java의 NIO를 쓰면 안되나요
ChannelFuture write(Object obj) ChannelFuture flush(Object obj) ChannelFuture writeAndFlush(Object obj) ChannelFuture closeFuture() ChannelPipeline pipeline() SocketAddress remoteAddress()
ChannelFuture addListener(GenericFutureListener<...> listener) Channel channel() boolean isSuccess(); Throwable cause(); ChannelFuture await() ChannelFuture sync()
void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) void handlerAdded(ChannelHandlerContext ctx) void handlerRemoved(ChannelHandlerContext ctx)
Channel channel() ChannelPipeline pipeline() ChannelFuture write(Object msg) ChannelHandlerContext fireChannelActive(Object msg) ChannelHandlerContext fireChannelRead(Object msg)
ChannelPipeline addLast(ChannelHandler... handlers) ChannelPipeline addLast(String name, ChannelHandler handler) ChannelHandler remove(String name) <T extends ChannelHandler> T remove(Class<T> handlerType)
boolean inEventLoop() <T> Future<T> submit(Callable<T> task) <V> Promise<V> newPromise() <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit)
https://github.com/hatemogi/netty-startup
git clone https://github.com/hatemogi/netty-startup
처음에는 실패하는 유닛테스트가 준비돼 있고, 실습문제를 모두 풀면 유닛 테스트가 모두 통과돼야합니다.
src/nettystartup/h1/discard
public final class DiscardServer { public static void main(String[] args) throws Exception { EventLoopGroup bossGroup = new NioEventLoopGroup(1); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .handler(new LoggingHandler(LogLevel.INFO)) .childHandler(new DiscardServerHandler()); ChannelFuture f = b.bind(8010).sync(); System.err.println("Ready for 0.0.0.0:8010"); f.channel().closeFuture().sync(); } finally { workerGroup.shutdownGracefully(); bossGroup.shutdownGracefully(); } } }
class DiscardServerHandler extends ChannelInboundHandlerAdapter { @Override public void channelRead(C..H..Context ctx, Object msg) throws Exception { ByteBuf buf = (ByteBuf) msg; try { // discard } finally { buf.release(); // 이 부분은 두번째 시간에 설명합니다. } } @Override public void exceptionCaught(C..H..Context ctx, Throwable cause) { cause.printStackTrace(); ctx.close(); } }
telnet localhost 8010
Trying ::1... Connected to localhost. Escape character is '^]'. helo test ^] telnet> close Connection closed.
EmbeddedChannel ch = new EmbeddedChannel(new HandlerToBeTested());
public class EmbeddedChannel extends AbstractChannel { public EmbeddedChannel(ChannelHandler... handlers) {} public Object readInbound() { ... } public Object readOutbound() { ... } public boolean writeInbound(Object... msgs) { ... } public boolean writeOutbound(Object... msgs) { ... } public void checkException() { ... } }
public class DiscardServerHandlerTest { @Test public void discard() { String m = "discard test\n"; EmbeddedChannel ch = new EmbeddedChannel(new DiscardServerHandler()); ByteBuf in = Unpooled.wrappedBuffer(m.getBytes()); ch.writeInbound(in); ByteBuf r = ch.readOutbound(); assertThat(r, nullValue()); } }
public final class EchoServer { public static void main(String[] args) throws Exception { EventLoopGroup bossGroup = new NioEventLoopGroup(1); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class); // TODO: [실습 1-1] 이 부분을 채워서 EchoServerHandler를 등록합니다 ChannelFuture f = b.bind(8020).sync(); f.channel().closeFuture().sync(); } finally { workerGroup.shutdownGracefully(); bossGroup.shutdownGracefully(); } } }
class EchoServerHandler extends ChannelInboundHandlerAdapter { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) { // TODO: [실습1-2] 받은대로 응답하는 코드를 한 줄 작성합니다. release는 필요하지 않습니다. } @Override public void channelReadComplete(ChannelHandlerContext ctx) { ctx.flush(); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { cause.printStackTrace(); ctx.close(); } }
public class EchoServerHandlerTest { @Test public void echo() { String m = "echo test\n"; EmbeddedChannel ch = new EmbeddedChannel(new EchoServerHandler()); ByteBuf in = Unpooled.copiedBuffer(m, CharsetUtil.UTF_8); ch.writeInbound(in); ByteBuf r = (ByteBuf)ch.readOutbound(); releaseLater(r); assertThat("응답이 없습니다", r, notNullValue()); assertThat("참조수는 1이어야 합니다",r.refCnt(), is(1)); assertThat("수신한 텍스트가 결과로 와야합니다", r.toString(CharsetUtil.UTF_8), equalTo(m)); } }