본문 바로가기

IT for developer/Netty

Netty 예제 분석 - Quote of the Moment

Quote of the Moment  UDP/IP 브로드캐스트 예제

 기존에 예제들 처럼 1:1 통신이 아니라 브로드캐스트 통신 예제이다.


우선 클리이언트를 살펴보면 

49          ConnectionlessBootstrap b = new ConnectionlessBootstrap(f);
62          b.setOption("broadcast", "true");

ClientBootstrap 이 아니라 ConnectionlessBootstrap을 사용하고 있다.ConnectionlessBootstrap은 UDP/IP와 같은 연결지속성이 없는 네트워크를위해 사용된다. 그리고 브로드캐스트 형태로 통신하도록 옵션을 설정했다. 

74          b.setOption(
75                  "receiveBufferSizePredictorFactory",
76 new FixedReceiveBufferSizePredictorFactory(1024)); 
UDP는 버퍼사이즈보다 더 큰 패킷을 받는 경우 짤라내거나 드랍시켜버릴 수 있다.  일단 여기서는 1024로 지정했다. 그러나 라우터 옵션에 따라 일정 사이즈 이상으로 보내면 패킷이 소실될 수 있다.
78          DatagramChannel c = (DatagramChannel) b.bind(new InetSocketAddress(0));
79  
80          // Broadcast the QOTM request to port 8080.
81          c.write("QOTM?", new InetSocketAddress("255.255.255.255", 8080));
브로드캐스트를 위해 255.255.255.255 로 패킷을 전송한다.

86          if (!c.getCloseFuture().awaitUninterruptibly(5000)) {
87              System.err.println("QOTM request timed out.");
88              c.close().awaitUninterruptibly();
89          }

5초안에 연결 종료가 되지 않으면 에러메시지를 출력하고 접속을 종료한다.
 
45          DatagramChannelFactory f =
46              new NioDatagramChannelFactory(Executors.newCachedThreadPool());
채널 팩토리도 기존에 NioClientSocketChannelFactory 또는 NioServerSocketChannelFactory와 다르게 하나의 Executor만을 사용하는 NioDatagramChannelFactory를 사용한다. 이는 연결 지속형이 아니므로 boss와 work 쓰레드가 따로 필요없어서 인듯 하다.

52          b.setPipelineFactory(new ChannelPipelineFactory() {
53              public ChannelPipeline getPipeline() throws Exception {
54                  return Channels.pipeline(
55                          new StringEncoder(CharsetUtil.ISO_8859_1),
56                          new StringDecoder(CharsetUtil.ISO_8859_1),
57                          new QuoteOfTheMomentClientHandler());
58              }
59          });

파이프라인팩토리를 설정하는 것은 그다지 특이하지 않다.

QuoteOfTheMomentClientHandler를 살펴보면 될듯 하다.

30      @Override
31      public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
32              throws Exception {
33          String msg = (String) e.getMessage();
34          if (msg.startsWith("QOTM: ")) {
35              System.out.println("Quote of the Moment: " + msg.substring(6));
36              e.getChannel().close();
37          }
38      }

받은 메시지들 중에 QOTM을 시작하하는 것이 있으면 이를 출력하고 연결을 종료한다.



이제 서버측 프로그램을 살펴보자.


서버측은 클라이언트측과 거의 동일한데 차이점은 등록할 핸들러가 다르다는것과 8080 포트를 바인딩한다는 것이다.

50      @Override
51      public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
52              throws Exception {
53          String msg = (String) e.getMessage();
54          if (msg.equals("QOTM?")) {
55              e.getChannel().write("QOTM: " + nextQuote(), e.getRemoteAddress());
56          }
57      }


연결형 통신과 다르게 write 함수 호출할 때 전달할 대상의 주소를 전달한다. channel은 연결되어 있는 상태가 아니므로 채널에 주소 없이 write 하면 안된다.