JDK 1.8 환경에서 동작하는 UDP 멀티캐스트 수신 프로그램 예제입니다.
자바 8에서는 java.net.MulticastSocket 클래스를 사용하여 멀티캐스트 데이터를 손쉽게 수신할 수 있습니다.
UDP 멀티캐스트 수신 예제 코드
Java
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
public class MulticastReceiver {
public static void main(String[] args) {
// 1. 멀티캐스트 IP와 포트 설정
// 멀티캐스트 IP 범위: 224.0.0.0 ~ 239.255.255.255
String multicastIp = "230.0.0.1";
int port = 10000;
MulticastSocket socket = null;
InetAddress group = null;
try {
// 2. MulticastSocket 생성 (포트 지정)
socket = new MulticastSocket(port);
// 3. 조인할 멀티캐스트 그룹 주소 설정
group = InetAddress.getByName(multicastIp);
// 4. 멀티캐스트 그룹에 가입 (Join)
socket.joinGroup(group);
System.out.println("UDP 멀티캐스트 수신 대기 중... (" + multicastIp + ":" + port + ")");
byte[] buffer = new byte[1024]; // 데이터를 담을 버퍼
while (true) {
// 5. 패킷 수신 대기
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
socket.receive(packet);
// 6. 받은 데이터 처리
String received = new String(packet.getData(), 0, packet.getLength(), "UTF-8");
String senderIp = packet.getAddress().getHostAddress();
System.out.println("[" + senderIp + "] 로부터 수신: " + received);
// "exit" 메시지를 받으면 종료 (선택 사항)
if ("exit".equalsIgnoreCase(received.trim())) {
System.out.println("수신 프로그램을 종료합니다.");
break;
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (socket != null) {
try {
// 7. 그룹 탈퇴 및 소켓 닫기
socket.leaveGroup(group);
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
코드 설명
-
MulticastSocket(port): 지정된 포트로 데이터를 받기 위해 소켓을 엽니다.
-
InetAddress.getByName(multicastIp): 멀티캐스트 주소(D Class IP: 224.0.0.0 ~ 239.255.255.255)를 객체화합니다.
-
socket.joinGroup(group): 이 명령을 실행해야 해당 IP로 들어오는 멀티캐스트 패킷을 커널에서 받아들입니다.
-
socket.receive(packet): 패킷이 도착할 때까지 프로그램이 블로킹(대기) 상태가 됩니다.
-
new String(packet.getData(), 0, packet.getLength(), “UTF-8”): 바이트 데이터를 문자열로 변환합니다. 인코딩은 보내는 쪽과 맞춰야 합니다.
실행 시 주의사항

-
방화벽: UDP 포트(예: 10000)가 방화벽에서 허용되어 있어야 합니다.
-
네트워크 인터페이스: PC에 가상 네트워크(VMware, VirtualBox 등)가 많을 경우, 특정 네트워크 인터페이스를 지정해줘야 할 수도 있습니다. 만약 수신이 안 된다면 아래 코드를 참고하여 인터페이스를 명시하세요.
Java
// 특정 인터페이스를 지정해야 하는 경우 (Java 1.4 이상)
// NetworkInterface netIf = NetworkInterface.getByName("eth0");
// socket.setNetworkInterface(netIf);
(참고) 테스트용 송신 코드 (Sender)
수신이 잘 되는지 확인하려면 아래 코드를 별도의 파일로 만들어 실행해 보세요.
Java
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class MulticastSender {
public static void main(String[] args) {
String multicastIp = "230.0.0.1";
int port = 10000;
String message = "Hello, Multicast! (JDK 1.8)";
try (DatagramSocket socket = new DatagramSocket()) {
InetAddress group = InetAddress.getByName(multicastIp);
byte[] msgBytes = message.getBytes("UTF-8");
DatagramPacket packet = new DatagramPacket(msgBytes, msgBytes.length, group, port);
socket.send(packet);
System.out.println("메시지 전송 완료: " + message);
} catch (Exception e) {
e.printStackTrace();
}
}
}
JDK 17 버전에서는 기존 JDK 1.8에서 사용하던 multicastSocket.joinGroup(InetAddress) 메서드가 Deprecated(더 이상 권장되지 않음) 되었습니다.
JDK 17을 포함한 현대적인 자바에서는 SocketAddress와 NetworkInterface를 명시적으로 사용하는 방식을 권장합니다. 이는 멀티캐스트 패킷을 받을 네트워크 카드(NIC)를 정확히 지정하기 위함입니다.
JDK 17용 UDP 멀티캐스트 수신 예제
Java
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.MulticastSocket;
import java.net.NetworkInterface;
import java.net.StandardSocketOptions;
import java.util.Enumeration;
public class MulticastReceiverJDK17{
public static void main(String[] args){
String multicastIp = "230.0.0.1";
int port = 10000;
// 1. 수신할 멀티캐스트 주소 및 포트 설정
InetSocketAddress group = new InetSocketAddress(multicastIp, port);
// 2. MulticastSocket 생성 (try-with-resources 사용으로 자동 close)
try (MulticastSocket socket = new MulticastSocket(port)) {
// 3. 네트워크 인터페이스 설정
// 멀티캐스트는 어떤 랜카드(인터페이스)를 사용할지 지정하는 것이 중요합니다.
// 아래는 기본 네트워크 인터페이스를 찾는 로직입니다.
NetworkInterface netIf = NetworkInterface.getByInetAddress(InetAddress.getLocalHost());
// 만약 위 코드가 null을 반환한다면, 첫 번째 활성 인터페이스를 선택합니다.
if (netIf == null) {
netIf = NetworkInterface.getNetworkInterfaces().nextElement();
}
// 4. 멀티캐스트 그룹 조인 (JDK 17 방식)
socket.joinGroup(group, netIf);
System.out.println("JDK 17 멀티캐스트 수신 시작 (" + multicastIp + ":" + port + ")");
System.out.println("사용 중인 인터페이스: " + netIf.getDisplayName());
byte[] buffer = new byte[1024];
while (!Thread.currentThread().isInterrupted()) {
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
// 5. 데이터 수신
socket.receive(packet);
String received = new String(packet.getData(), 0, packet.getLength(), "UTF-8");
System.out.printf("[%s] 수신 데이터: %s%n",
packet.getAddress().getHostAddress(), received);
if ("exit".equalsIgnoreCase(received.trim())) {
break;
}
}
// 6. 그룹 나가기
socket.leaveGroup(group, netIf);
System.out.println("수신 종료.");
} catch (IOException e) {
System.err.println("에러 발생: " + e.getMessage());
e.printStackTrace();
}
}
}
java UdpRcv 239.0.0.1 5000 0.0.0.0 5000
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
import java.net.SocketTimeoutException;
public class UdpRcv {
// ====== 상수 설정 ======
private static final int BUF_SIZE = 4096;
private static final int TIMEOUT_MS = 70000; // C 코드의 TIMEOUT 70초에 대응 (밀리초 단위)
public static void main(String[] args) {
// 1. 인자 개수 확인 (C 코드의 Usage 메시지에 맞춰 4개의 인자를 받도록 설정)
if (args.length != 4) {
System.out.println(“Usage: java UdpRcv <multicast IP> <multicast PORT> <listen IP> <listen PORT>”);
System.exit(1);
}
String multicastIp = args[0];
int multicastPort = Integer.parseInt(args[1]);
String listenIp = args[2];
int listenPort = Integer.parseInt(args[3]);
// Java 7 이상부터 지원하는 try-with-resources 문법 (JDK 1.8 호환)
// 소켓을 자동으로 닫아줍니다.
try (MulticastSocket socket = new MulticastSocket(listenPort)) {
// 2. 소켓 옵션 설정 (C의 SO_REUSEADDR은 MulticastSocket 생성 시 기본 적용됩니다)
socket.setSoTimeout(TIMEOUT_MS);
// 3. 멀티캐스트 멤버십 설정 (C의 IP_ADD_MEMBERSHIP)
InetAddress group = InetAddress.getByName(multicastIp);
socket.joinGroup(group);
// 특정 listen IP(인터페이스)를 지정하고 싶다면 아래 주석을 해제하세요.
// socket.setInterface(InetAddress.getByName(listenIp));
System.out.println(“UDP Multicast Receiver started…”);
System.out.println(“Joined Group: ” + multicastIp + ” on Port: ” + listenPort);
byte[] buffer = new byte[BUF_SIZE];
// 4. 루프 시작: 데이터 수신
while (true) {
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
try {
// C의 select() + recvfrom() 역할을 동시에 수행 (블로킹 방식이며 타임아웃 적용됨)
socket.receive(packet);
// 데이터 수신 성공 시 출력
recvData(packet);
} catch (SocketTimeoutException e) {
// C 코드의 poll timeout 메시지 대응
System.out.println(“poll timeout”);
}
}
} catch (IOException e) {
System.err.println(“Socket error: ” + e.getMessage());
e.printStackTrace();
}
}
// =============================================
// recvData: 데이터 수신 및 출력 함수
// =============================================
private static void recvData(DatagramPacket packet) {
String senderIp = packet.getAddress().getHostAddress();
int senderPort = packet.getPort();
int length = packet.getLength();
System.out.printf(“Received %d bytes from %s:%d\n”, length, senderIp, senderPort);
// 필요하다면 수신된 데이터를 문자열로 변환하여 출력할 수 있습니다.
// String data = new String(packet.getData(), 0, length);
// System.out.println(“Data: ” + data);
}
}
java UdpReceiver 239.0.0.1 5000 192.168.1.100 5000
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.MulticastSocket;
import java.net.NetworkInterface;
import java.net.SocketTimeoutException;
public class UdpReceiver {
// ====== 상수 설정 ======
private static final int BUF_SIZE = 4096;
private static final int TIMEOUT_SEC = 70; // C 코드의 #define TIMEOUT 70 반영
public static void main(String[] args) {
// 1. 인자 개수 확인
if (args.length != 4) {
System.out.println(“Usage: java UdpReceiver <multicast IP> <multicast PORT> <listen IP> <listen PORT>”);
System.exit(1);
}
String multicastIp = args[0];
int multicastPort = Integer.parseInt(args[1]);
String listenIp = args[2];
int listenPort = Integer.parseInt(args[3]);
MulticastSocket socket = null;
InetAddress group = null;
try {
// 2. 멀티캐스트 소켓 생성 및 바인딩
// listenPort로 소켓을 바인딩합니다.
socket = new MulticastSocket(listenPort);
// SO_REUSEADDR 설정 (JDK 1.8 MulticastSocket은 기본적으로 true로 설정되는 경우가 많으나 명시적 설정)
socket.setReuseAddress(true);
// 타임아웃 설정 (밀리초 단위)
socket.setSoTimeout(TIMEOUT_SEC * 1000);
// 3. 특정 인터페이스(Listen IP)로 멀티캐스트 그룹 조인
group = InetAddress.getByName(multicastIp);
InetAddress localInterface = InetAddress.getByName(listenIp);
NetworkInterface netIf = NetworkInterface.getByInetAddress(localInterface);
if (netIf != null) {
// 특정 네트워크 인터페이스를 지정하여 그룹에 가입 (JDK 1.8 방식)
socket.joinGroup(new InetSocketAddress(group, multicastPort), netIf);
System.out.println(“Joined multicast group on interface: ” + listenIp);
} else {
// 인터페이스를 찾지 못한 경우 기본 인터페이스로 가입
socket.joinGroup(group);
System.out.println(“Joined multicast group on default interface.”);
}
System.out.println(“UDP Multicast Receiver started. Listening on port ” + listenPort + “…”);
byte[] buffer = new byte[BUF_SIZE];
// 4. 데이터 수신 루프
while (true) {
try {
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
// 데이터 수신 (select와 recvfrom의 역할을 동시에 수행)
socket.receive(packet);
// 수신 데이터 처리 (recv_data 함수 역할)
handleReceivedData(packet);
} catch (SocketTimeoutException e) {
// C 코드의 poll timeout 출력 부분
System.out.println(“poll timeout”);
}
}
} catch (IOException e) {
System.err.println(“Error occurred: ” + e.getMessage());
e.printStackTrace();
} finally {
// 5. 소켓 닫기 및 그룹 탈퇴
if (socket != null && !socket.isClosed()) {
try {
if (group != null) {
socket.leaveGroup(group);
}
socket.close();
System.out.println(“Socket closed.”);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* C의 recv_data 내부의 recvfrom 이후 로직에 해당합니다.
*/
private static void handleReceivedData(DatagramPacket packet) {
String senderIp = packet.getAddress().getHostAddress();
int senderPort = packet.getPort();
int length = packet.getLength();
// 수신된 데이터를 문자열로 변환 (필요시)
String receivedMessage = new String(packet.getData(), 0, length);
System.out.printf(“Received %d bytes from %s:%d\n”, length, senderIp, senderPort);
// 원본 바이너리 데이터를 처리해야 한다면 packet.getData()를 직접 사용하세요.
}
}