슬기로운 개발생활

[Java] NIO 기반 입출력 및 네트워킹 - UDP 채널 : 발신자 / 수신자 / 통신

by coco3o
반응형

UDP 채널

NIO에서 UDP 채널은 DatagramChannel이다.

DatagramChannel도 TCP 채널과 마찬가지로 블로킹과 넌블로킹 방식으로 사용할 수 있지만, 여기선 블로킹 방식만 다룬다.


발신자 만들기

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

 

DatagramChannel을 생성하려면 open() 메소드를 호출해야 한다. open()은 ProtocolFamily 인터페이스 타입 매개값을 가진다.

 

이 객체의 역할은 IPv4와 IPv6을 구분하는 것이다. 구현 객체는 StandardProtocolFamily 열거 상수를 사용한다.

 

다음은 IPv4를 사용하는 DatagramChannel을 생성하는 코드이다.

DatagramChannel datagramChannel = DatagramChannel.open(StandardProtocolFamily.INET);

DatagramChannel을 이용해서 데이터를 보내려면 send() 메소드를 사용한다.

 

send() 메소드의 첫 번째 매개값은 보낼 데이터를 가지고 있는 ByteBuffer이고,

 

두 번째 매개값은 수신자IP와 포트 정보를 가지고 있는 SocketAddress이다.

 

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

 

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

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

더 이상 보낼 데이터가 없을 경우 close() 메소드 호출

datagramChannel.close();

 

* UdpSendExample.java 발신자

package javaStudy;
 
import java.net.InetSocketAddress;
import java.net.StandardProtocolFamily;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
import java.nio.charset.Charset;
 
public class UdpSendExample {
 
    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();
    }
}

 

수신자 만들기

이번엔 DatagramCHannel로 수신자 프로그램을 구현하는 방법에 대해 알아본다.

 

DatagramChannel을 이용해서 데이터를 받으려면 bind() 메소드를 호출해서 포트와 바인딩을 해야 한다.

 

매개값은 SocketAddress 타입으로 InetSocketAddress 객체를 대입하면 된다.

DatagramChannel datagramChannel = DatagramChannel.open(StandardProtocolFamily.INET);

datagramChannel.bind(new InetSocketAddress(5001));

 

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

 

receive() 메소드의 매개값은 받은 데이터를 저장할 ByteBuffer이고,

리턴 타입은 원격 클라이언트의 IP와 포트 정보를 가지고 있는 SocketAddress이다. 실제로는 InetSocketAddress가 리턴된다.

SocketAddress socketAddress = datagramChannel.receive(ByteBuffer dst);

 

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

 

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

 

작업 스레드를 종료시키는 방법은 두 가지이다.

1. receive() 메소드가 블로킹되어 있는 상태에서 작업 스레드의 interrupt()를 호출시켜 ClosedByInterruptException 예외를 발생시킨다.

 

2. 아래와 같이 DatagramChannel의 close()를 호출시켜 AsynchronousCloseException 예외를 발생시키는 것이다.

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

datagramChannel.close();

 

* UdpReceiveExample.java 수신자

package javaStudy;
 
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 UdpReceiveExample 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();
    }
}

 

Reference : '이것이 자바다' 19장

반응형

블로그의 정보

슬기로운 개발생활

coco3o

활동하기