본문 바로가기
👨‍🏫Study/JAVA

[JAVA] 19. NIO 기반 입출력 및 네트워킹(6)

by 코푸는 개발자 2022. 4. 6.
728x90

3. TCP 비동기 채널

  • NIO는 TCP비동기 채널로 AsynchronousServerSocket과 AsynchronousSocketChannel를 제공한다.

 

비동기 채널의 특징

  • connect(), accept(), read(), write()를 호출하면 즉시 리턴한다. 이점은 넌블로킹 방식과 동일하나, 차이점은 이 메소드들을 호출하면 스레드풀에게 작업 처리를 요청하고 이 메소드들은 즉시 리턴된다.
    • 실질적인 작업 처리는 스레드풀의 작업 스레드가 담당한다.

 

  • 작업 스레드가 작업을 완료하게 되면 콜백(callback) 메소드가 자동 호출되기 때문에 작업 완료 후 실행해야 할 코드가 있다면 콜백 메소드에 작성하면 된다.

 

비동기 처리 방식

 

 

 

비동기 채널 그룹

  • 비동기 채널 그룹이란 스레드풀을 공유하는 비동기 채널들의 묶음이라고 볼 수 있다.
    • 여러 비동기 채널들이 하나의 스레드풀을 사용하다는 것은 같은 비동기 채널 그룹이라고 볼 수 있다.

 

  • 비동기 채널 생성 시 채널 그룹을 지정하지 않으면 기본 비동기 채널 그룹에 속하게 된다.

 

비동기 채널 그룹 생성 - 직접 지정할 경우

AsynchronousChannelGroup channelGroup = AsynchronousChannelGroup.withFixedThreadPool(
      최대스레드수,
      Executors.defaultThreadFactory()
);

 

ex. CPU 코어의 수만큼 스레드를 관리하는 스레드풀 생성하고 이것으로 비동기 채널 그룹을 생성 

AsynchronousChannelGroup channelGroup = AsynchronousChannelGroup.withFixedThreadPool(
      Runtime.getRuntime().availableProcessors(),
      Executors.defaultThreadFactory()
);

 

-> 위와 같이 생성된 비동기 채널 그룹은 비동기 채널을 생성 시 매개값으로 사용된다.

 

  • 비동기 채널 그룹을 더 이상 사용하지 않아 종료할 경우 shutdown()과 shutdownNow() 메소드 호출

 

channelGroup.shutdown();  // 비동기 채널 그룹을 즉시 종료하지않고 모든 비동기 채널이 닫히면 종료 시킴

channelGroup.shutdownNow();  //강제적으로 비동기 채널 그룹에 포함된 모든 비동기 채널을 닫아버리고 채널 그룹도 종료

 

 

비동기 서버소켓 채널 - AsynchronousServerSocketChannel

 

  • AsynchronousServerSocketChannel은 두 가지 정적 메소드인 open()을 호출해서 얻는다.

 

AsynchronousServerSocketChannel 생성 방법

//매개값이 없는 open()메소드 호출
AsynchronousServerSocketChannel asynchronousServerSocketChannel = AsynchronousServerSocketChannel.open();

//별도의 비동기 채널 그룹에 포함하기 위한 open()메소드 호출
AsynchronousServerSocketChannel asynchronousServerSocketChannel = AsynchronousServerSocketChannel.open(channelGroup);

 

  • AsynchronousServerSocketChannel을 생성 후 서버 포트 바인딩을 위해 bind()메소드 호출한다

 

AsynchronousServerSocketChannel 서버 포트 바인딩 방법

 

asynchronousServerSocketChannel.bind(new InetSocketAddress(5001));

 

  • AsynchronousServerSocketChannel을 더 이상 사용하지 않을 경우 close()메소드를 호출하여 포트를 언바인딩해준다.

 

asynchronousServerSocketChannel.close();

 

 

AsynchronousServerSocketChannel의 연락 수락 작업 - accept()

 

  • 연락 수락 작업을 스레드풀을 이용해서 비동기로 처리한다.

 

accept(A attachment, CompletionHandler<AsynchronousSocketChannel, A> handler);

 

  • 첫 번째 매개값 : 콜백 메소드의 매개값으로 제공할 첨부 객체, 연락 수락 작업에는 별도의 첨부 객체 필요하지 않다. (null로 설정)
  • 두 번째 매개값 : 콜백 메소드를 가지고 있는 CompletionHandler<AsynchronousSocketChannel, A> 구현 객체.
  • <AsynchronousSocketChannel, A> : AsynchronousSocketChannel는 리턴값이고 , A는 첨부 객체 타입이다.(Void로 지정)

 

ex. 비동기 채널 accept()

asynchronousServerSocketChannel.accept(null,

   new CompletionHandler<AsynchronousSocketChannel, Void>(){

   @Override
   public void completed(AsynchronousSocketChannel asynchronousSocketChannel , Void attachment){
    //연락 수락 후 실행할 코드
   asynchronousServerSocketChannel.accept(null,this);   //accept() 재호출
   }

   @Override
   public void failed(Throwable exc, Void attachment){
   //연결 수락 실패 시 실행할 코드
  }
});

 

completed() 메소드

  • 연결 수락이 완료되었을 때 스레드풀의 스레드가 호출한다.
  • 첫번째 매개값 : 연결 수락 후 리턴된AsynchronousSocketChannel 대입,
  • 두번째 매개값 : 첨부 객체로 accept()의 첫 번째 매개값이 null이므로 Void가 된다.

 

failed() 메소드

  • 스레드풀의 스레드가 연락 수락에 문제가 생겨 예외를 발생시키면 failed() 호출된다.
  • 첫번째 매개값 : 예외 객체

 

※ 주의점 : accept()를 반복해서 호출하는 무한 루프가 없는데, 대신 completed()메소드 끝에 accept()를 재호출해서 반복적으로 클라이언트의 연결 수락 작업을 수행

 

비동기 소켓 채널 - AsynchronousSocketChannel 

  • AsynchronousSocketChannel은 서버와 클라이언트에 각각 존재, 클라이언트가 서버에 연결 요청과 서버와 클라이언트의 통신할 수 있도록 만들어준다.
  • AsynchronousServerSocketChannel이 생성하는 AsynchronousSocketChannel은 자동적으로 AsynchronousServerSocketChannel과 같은 비동기 채널 그룹에 속하게 된다.

 

AsynchronousSocketChannel 생성 방법 - open()메소드

//매개값이 없는 open()메소드 호출
AsynchronousSocketChannel  asynchronousSocketChannel = AsynchronousSocketChannel.open();

//별도의 비동기 채널 그룹에 포함하기 위한 open()메소드 호출
AsynchronousChannelGroup channelGroup = AsynchronousChannelGroup.withFixedThreadPool(
    Runtime.getRuntime().availableProcessors(),
    Executors.defaultThreadFactory()
);
AsynchronousSocketChannel  asynchronousSocketChannel = AsynchronousSocketChannel.open(channelGroup);

 

 

  • AsynchronousSocketChannel 을 더 이상 사용하지 않을 경우 close() 메소드를 호출해서 연결을 끊어준다.
asynchronousSocketChannel.close();

 

 

AsynchronousSocketChannel의 연락 요청 작업 - connect()

 

  • 클라이언트가 생성하는 AsynchronousSocketChannel은 서버 연결 요청 작업을 스레드풀을 이용해서 비동기로 처리한다.
connect(SocketAddress remote, A attachment, CompletionHandler<Void, A> handler);

 

  • 첫 번째 매개값 : 서버 IP와 연결 포트 정보를 가진 InetSocketAddress 객체
  • 두 번째 매개값 : 콜백 메소드의 매개값으로 제공할 첨부 객체, 연결 요청 작업은 별도의 첨부 객체 필요없다.
  • 세 번째 매개값 : CompletionHandler<Void, A> 구현 객체, 콜백 메소드를 정의한다.

 

ex. 비동기 채널 connect()

asynchronousSocketChannel.connect(new InetSocketAddress("localhost", 5001), null,

   new CompletionHandler<Void, A>(){

   @Override
   public void completed(Void result, Void attachment){
   //연결 성공 후 실행할 코드
   }

   @Override
   public void failed(Throwable e, Void attachment){
   //연결 실패 후 실행할 코드
  }
});

 

비동기 소켓 채널 데이터 통신 - read(), write()

  • 클라이언트와 서버가 연결되면 양쪽 AsynchronousSocketChannel의 read()와 write()메소드로 데이터 통신을 할 수 있다.
  • 이 메소드들은 호출하는 즉시 리턴되고, 실질적인 입출력 작업은 스레드플의 스레드가 담당한다.

 

read(ByteBuffer dst, A attachment, CompletionHandler<Integer, A> handler);
write(ByteBuffer src, A attachment, CompletionHandler<Integer, A> handler);

 

  • 첫 번째 매개값 : 읽고 쓰기 위한 ByteBuffer.
  • 두 번째 매개값 : 콜백 메소드의 매개값으로 제공할 첨부 객체
  • 세 번째 매개값 :CompletionHandler<Void, A> 구현 객체, 콜백 메소드 정의함

 

ex. 비동기 채널 read()

asychronousSocketChannel.read(byteBuffer, attachment,

    new CompletionHandler<Integer, A>(){

    @Override
    public void completed(Integer result, A attachment){
     //받은 데이터를 처리하는 코드
    asynchronousSocketChannel.read(byteBuffer, attachment,this); //read() 재호출
   }

  @Override
   public void failed(Throwable exc, A attachment){
     //실패된 경우 실행할 코드
   }
});

 

completed() 메소드

 

  •  첫 번째 매개값 : 읽은 바이트 수, 두 번째 매개값 : 첨부 객체로 read()메소드 호출 시 사용된 두 번째 매개값이 대입된다.

 

※주목할 점 : read()를 반복해서 호출하는 무한 루프가 없다는 점, 대신 completed()메소드 끝에 read()를 재호출해서 반복적으로 데이터를 받아 작업을 수행한다.

 

ex. 비동기 채널 write()

asynchronousSocketChannel.write(byteBuffer, attachment,

      new CompletionHandler<Integer, A>(){

      @Override
      public void completed(Integer result, A attachment){
      //성공한 경우 실행할 코드     
      }

      @Override
       public void failed(Throwable exc, A attachment){
        //실패된 경우 실행할 코드
     }
});

 

 

728x90

댓글