🛠️Backend/JAVA

[Java] Netty를 사용하여 간단한 Echo 서버 및 클라이언트 구현

뉴발자 2024. 7. 24.
728x90

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Netty 란?

고성능 네트워킹 애플리케이션을 제작하기 위한 자바용 고급 프레임워크이다.

 

단일 API로 블로킹과 논블로킹 방식의 여러 전송 유형을 지원한다.

 

그리고 사용 방법이 간단하며 추가 의존성 없이 netty 라이브러리만을 설치하여 사용할 수 있다.

 

무엇보다 코어 자바 API보다 높은 처리량과 짧은 지연 시간, 리소스 소비 감소, 메모리 복사를 최소화한다는 장점이 있다.

 

 

준비하기

Netty를 사용하기 위해 의존성을 추가해준다.

 

필자는 Gradle을 사용해서 아래 의존성을 build.gradle에 추가했다.

implementation 'io.netty:netty-all:4.1.109.Final'

 

 

Echo Server

네티 서버 구성 시 다음 항목이 필요하다.

 

 • ChannelHandler

   - 클라이언트로부터 받은 데이터를 서버측에서 처리하는 비즈니스 논리를 구현한다.
   - 최소 1개 이상의 Handler가 필요하다.

 

 • Bootstrap

   - 서버를 구동하는 시동 코드를 의미한다.

   - 최소한 서버가 연결 요청을 수신하는 포트를 서버와 바인딩하는 코드가 있어야 한다.

 

 

EchoServerHandler

@ChannelHandler.Sharable
public class EchoServerHandler extends SimpleChannelInboundHandler<Object> {

  @Override
  public void channelActive(ChannelHandlerContext ctx) {
    System.out.println("Connected Client.");
  }
  
  @Override
  public void channelInactive(ChannelHandlerContext ctx) {
    System.out.println("Disconnected Client.");
  }
  
  @Override
  public void channelRead0(ChannelHandlerContext ctx, Object msg) {
    ByteBuf in = (ByteBuf) msg;
    System.out.println("Server received: " + in.toString(CharsetUtil.UTF_8));
    ctx.write(in);
  }
  
  @Override
  public void channelReadComplete(ChannelHandlerContext ctx) {
    // 대기 중인 메시지를 원격 피어로 플러시하고 채널을 닫는다.
    ctx.writeAndFlush(Unpooled.EMPTY_BUFFER)
       .addListener(ChannelFutureListener.CLOSE);
  }
  
  @Override
  public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
    cause.printStackTrace();
    ctx.close();
  }
}

 

 • @ChannelHandler.Sharable

   - ChannelHandler를 여러 채널 간에 안전하게 공유할 수 있음을 나타내는 어노테이션

 

 • channelActive()

   - 클라이언트 접속 시 실행되는 함수

 

 • channelInactive()

   - 클라이언트와 연결이 끊겼을 때 실행되는 함수


 •
channelRead0

   - 클라이언트 메시지 수신 시 호출되는 함수


 •
channelReadComplete()

   - 클라이언트 메시지 수신 완료 시 호출되는 함수

 

 • exceptionCaught()

   - 클라이언트 메시지 수신 후 오류 발생 시 호출되는 함수

 

 

Server Bootstrap

public class EchoServer {
  private final int port;
  
  public EchoServer(int port) {
    this.port = port;
  }
  
  public void start() throws Exception {
    final EchoServerHandler serverHandler = new EchoServerHandler();
    
    // EventLoopGroup을 생성한다.
    EventLoopGroup group = new NioEventLoopGroup();
    
    try {
      ServerBootstrap bootstrap = new ServerBootstrap();
      bootstrap.group(group)
               .channel(NioServerSocketChannel.class)
               .localAddress(new InetSocketAddress(port))
               .childHandler(new ChannelInitailizer<SocketChannel>() {
                 @Override
                 public void initChannel(SocketChannel ch) throws Exception {
                   ch.pipeline().addLast(serverHandler);
                 }
               });
       ChannelFuture future = bootstrap.bind().sync();
       future.channel().closeFuture().sync();
      
    } finally {
      // EventLoopGroup을 종료하고 모든 리소스를 해제한다.
      group.shutdownGracefully().sync();
    }
  }
  
  public static void main(String[] args) {
    // 사용할 포트 값을 설정한다.
    int port = 80;
    // 설정한 포트 값으로 서버를 실행시킨다.
    new EchoServer(port).start();
  }
}

 

 

 • channel(NioServerSocketChannel.class)

   - NIO 전송 채널을 이용하도록 지정

 

 • bootstrap.bind().sync()

   - 서버를 비동기식으로 바인딩한다.

   - sync()는 바인딩이 완료 되기를 기다린다.

 

 • future.channel().closeFuture().sync()

   - 채널의 CloseFuture를 얻고 완료될 때까지 현재 스레드를 블로킹한다.

 

 

Echo Client

Echo Client는 다음과 같이 동작한다.

 

 • 서버와 연결 > 메시지 전송 > 메시지마다 대기하고 서버로부터 동일한 메시지 수신 > 연결 종료

 

클라이언트도 Echo Server와 동일하게 Handler와 Bootstrap을 작성한다.

 

EchoClientHandler

@ChannelHandler.Sharable
public class EchoClientHandler extends SimpleChannelInboundHandler<Object> {
  @Override
  public void channelActive(ChannelHandlerContext ctx) {
    ctx.writeAndFlush(Unpooled.copiedBuffer("Netty rocks!", CharsetUtil.UTF_8);
  }
  
  @Override
  public void channelRead0(ChannelHandlerContext ctx, ByteBuf in) {
    System.out.println("Client received: " + in.toString(CharsetUtil.UTF_8));
  }
  
  @Override
  public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
    cause.printStackTrace();
    ctx.close();
  }
}

 

 • channelActive()

   - 서버와 연결 성공 시 호출되는 함수


 •
 channelRead0

   - 서버 메시지 수신 시 호출되는 함수

 

 • exceptionCaught()

   - 서버 메시지 수신 후 오류 발생 시 호출되는 함수

Client Bootstrap

public class EchoClient {
  private final String host;
  private final int port;
  
  public EchoClient(String host, int port) {
    this.host = host;
    this.port = port;
  }
  
  public void start() throws Exception {
    final EchoClientHandler clientHandler = new EchoClientHandler();
    
    // EventLoopGroup을 생성한다.
    EventLoopGroup group = new NioEventLoopGroup();
    
    try {
      Bootstrap bootstrap = new Bootstrap();
      bootstrap.group(group)
               .channel(NioSocketChannel.class)
               .remoteAddress(new InetSocketAddress(host, port))
               .handler(new ChannelInitializer<SocketChannel>() {
                 @Override
                 public void initChannel(SocketChannel ch) {
                   ch.pipeline().addLast(clientHandler);
                 }
               });
       ChannelFuture future = bootstrap.bind().sync();
       future.channel().closeFuture().sync();
      
    } finally {
      // EventLoopGroup을 종료하고 모든 리소스를 해제한다.
      group.shutdownGracefully().sync();
    }
  }
  
  public static void main(String[] args) {
    // 서버 주소를 설정한다.
    String host = "localhost";
    // 사용할 포트 값을 설정한다.
    int port = 80;
    // 설정한 포트 값으로 서버를 실행시킨다.
    new EchoClient(host, port).start();
  }
}

 

 

빌드 및 실행

EchoServer를 실행시킨 후 EchoClient를 실행시키면 각각 콘솔창에 다음과 같은 로그가 나온다.

 

EchoServer

Connected Client.
Server received: Netty rocks!
Disconnected Client.

 

EchoClient

Client received: Netty rocks!

 

클라이언트가 보낸 문자열이 서버 콘솔에 찍힌 후 다시 같은 문자열을 클라이언트로 전송하여 로그에 찍히게 된다.

 

 

참고 문헌

https://www.yes24.com/Product/Goods/25662949

 

네티 인 액션 - 예스24

네티는 복잡한 네트워킹, 멀티스레드, 동시성을 관리하는 자바 기반 네트워킹 프레임워크로서, 반복적인 저수준 코드를 내부로 감춤으로써 비즈니스 논리를 분리하고 쉽게 재사용할 수 있게 해

www.yes24.com

 

현재 위 교재는 절판되어 중고로 구매하거나 근처 도서관을 이용해야만 한다.

 

 

 

 

 

 

 

 

 

 

728x90

'🛠️Backend > JAVA' 카테고리의 다른 글

[Java] 순수 자바 프로젝트에서 MS-SQL 연결하기  (0) 2024.07.17

댓글