ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Java] NIO 기반 입출력 및 네트워킹 - UDP 채널
    CSE/Java 2015. 9. 6. 14:26
    NIO 기반 입출력 및 네트워킹은 여러 절로 구성되어 있습니다.






    8. UDP 채널

     NIO에서 UDP 채널은 DatagramChannel 입니다. 


     DatagramChannel도 TCP 채널과 마찬가지로 블로킹과 넌블로킹 방식으로 사용할 수 있지만, 이번 절에서는 블로킹 방식만 설명하겠습니다.






     8.1 발신자 만들기

      발신자 프로그램을 구현해보면서 DatagramChannel을 사용하는 방법에 대해 알아보도록 하겠습니다.


      DatagramChannel을 생성하려면 open() 메소드를 호출해야 합니다. 


      open () 메소드는 protocalFamily 인터페이스 타입의 파라미터를 갖는데, 이 객체의 역할은 IPv4와 IPv6를 구분하기 위해서 입니다.


      구현 객체는 StandardProtocalFamily 열거 상수를 사용하면 됩니다. 다음은 IPv4를 사용하는 DatagramChannel을 생성하는 코드입니다.




    1
    2
    DatagramChannel datagramChannel = DatagramChannel.open(StandardProtocolFamily.INET);
     
    cs




      DatagramChannel을 이용해서 데이터를 보내기 위해서는 send() 메소드를 이용합니다. 


      send() 메소드의 첫 번째 파라미터는 보낼 데이터를 가지고 있는 ByteBuffer이고, 두 번째 파라미터는 수신자 IP와 포트 정보를 가지고 있는 SocketAddress입니다. 


      SocketAddress는 추상 클래스이므로 하위 클래스인 InetSocketAddress 객체를 생성하고 대입하면 됩니다. 


      send() 메소드의 리턴 값은 실제로 보낸 바이트 수 입니다. 


      다음은 로컬 PC 5001번을 수신자로하고 데이터를 보냅니다.



    1
    2
    int byteCount = datagramChannel.send(byteBuffer, new InetSocketAddress("localhost"5001));
     
    cs




      더 이상 보낼 데이터가 없을 경우에는 DatagramChannel을 닫기 위해 close() 메소드를 호출합니다.



    1
    datagramChannel.close();
    cs




      다음 예제는 UDP 발신 프로그램입니다. for 문을 2 번 반복하는데 각각 "메시지 1", "메시지 2" 문자열을 전송합니다.



      * UdpSendExam.java


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    package pathexam;
     
    import java.net.InetSocketAddress;
    import java.net.StandardProtocolFamily;
    import java.nio.ByteBuffer;
    import java.nio.channels.DatagramChannel;
    import java.nio.charset.Charset;
     
    public class UdpSendExam {
     
        public static void main(String[] args) throws Exception {
            DatagramChannel datagramChannel = DatagramChannel.open(StandardProtocolFamily.INET);
     
            System.out.println("[발신 시작]");
     
            for (int i = 1; i < 3; i++) {
                String data = "메시지 " + i;
                Charset charset = Charset.forName("UTF-8");
                ByteBuffer byteBuffer = charset.encode(data);
     
                int byteCount = datagramChannel.send(byteBuffer, new InetSocketAddress("localhost"5001));
     
                System.out.println("[보낸 바이트 수] " + byteCount + " bytes");
            }
     
            System.out.println("[발신 종료]");
     
            datagramChannel.close();
        }
     
    }
     
    cs










     8.2 수신자 만들기

      이번에는 DatagramChannel로 수신자 프로그램을 구현하는 방법에 대해 알아보기로 합시다.


      DatagramChannel을 이용해서 데이터를 받기 위해서는 bind() 메소드를 호출해서 포트와 먼저 바인딩을 해야 합니다. 


      파라미터로 SocketAddress 타입으로 InetSocketAddress 객체를 대입하면 됩니다.




    1
    2
    3
    4
    DatagramChannel datagramChannel = DatagramChannel.open(StandardProtocalFamily.INET);
     
    datagramChannel.bind(new InetSocketAddress(5001));
     
    cs




      포트와 바인딩이 되었다면 다음과 같이 receive() 메소드로 데이터를 받을 수 있습니다. 


      receive() 메소드의 파라미터는 받은 데이터를 저장할 ByteBuffer이고, 리턴 타입은 원격 클라이언트의 IP와 포트 정보를 가지고 있는 SocketAddress 입니다.


      실제로는 InetSocketAddress가 리턴됩니다.



    1
    2
    SocketAddress socketAddress = datagramChannel.receive(ByteBuffer dst);
     
    cs




      데이터를 받기 전까지 receive() 메소드는 블로킹되고, 데이터를 받으면 리턴됩니다.


      수신자는 항상 데이터를 받을 준비를 해야 하므로 작업 스레드를 생성해서 receive() 메소드를 반복적으로 호출해야 합니다.


      작업 스레드를 종료시키는 방법은 두 가지인데, receive() 메소드가 블로킹되어 있는 상태에서 작업 스레드의 interrupt()를 호출시켜 ClosedByInterruptException 예외를 발생시키거나, 다음과 같이 DatagramChannel의 close() 메소드를 호출시켜 AsynchronousCloseException 예외를 발생시키는 것입니다.


      그리고 예외 처리 코드에서 작업 스레드를 종료시키면 됩니다.










    1
    2
    datagramChannel.close();
     
    cs




      다음은 수신자 프로그램의 예제 코드입니다. 실행 후 10초가 지나면 수신자를 종료하도록 했습니다.



      * UdpReceiveExam.java



    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
     
    package pathexam;
     
    import java.net.InetSocketAddress;
    import java.net.SocketAddress;
    import java.net.StandardProtocolFamily;
    import java.nio.ByteBuffer;
    import java.nio.channels.DatagramChannel;
    import java.nio.charset.Charset;
     
    public class UdpReceiveExam extends Thread {
     
        public static void main(String[] args) throws Exception {
            DatagramChannel datagramChannel = DatagramChannel.open(StandardProtocolFamily.INET);
            datagramChannel.bind(new InetSocketAddress(5001));
     
            Thread thread = new Thread() {
                @Override
                public void run() {
                    System.out.println("[수신 시작]");
     
                    try {
                        while (true) {
                            ByteBuffer byteBuffer = ByteBuffer.allocateDirect(100);
                            SocketAddress socketAddress = datagramChannel.receive(byteBuffer);
                            byteBuffer.flip();
     
                            Charset charset = Charset.forName("UTF-8");
                            String data = charset.decode(byteBuffer).toString();
                            System.out.println("[받은 내용: " + socketAddress.toString() + "] " + data);
                        }
                    } catch (Exception e) {
                        System.out.println("[수신 종료]");
                    }
                }
            };
            thread.start();
     
            Thread.sleep(10000);
            datagramChannel.close();
        }
     
    }
     
    cs















    * 이 포스트은 서적 '이것이 자바다' 를 참고하여 작성한 포스트입니다.

    댓글

Designed by Tistory.