[Java] NIO 기반 입출력 및 네트워킹 - UDP 채널 : 발신자 / 수신자 / 통신
by coco3oUDP 채널
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장
'📚Computer Science > Network' 카테고리의 다른 글
프록시(Proxy)란? (0) | 2021.07.08 |
---|---|
쿠키(Cookie)와 세션(Session)의 차이 (+캐시(Cache)) (16) | 2021.06.25 |
GET과 POST의 차이점 (0) | 2021.06.24 |
[Java] NIO 기반 입출력 및 네트워킹 - TCP 넌블로킹 채널 (0) | 2021.05.21 |
[Java] NIO 기반 입출력 및 네트워킹 - TCP 블로킹 채널 (0) | 2021.05.21 |
블로그의 정보
슬기로운 개발생활
coco3o