UDP
는 연결 요청과 수락과정 없이 데이터를 일방적으로 보내는 통신 프로토콜입니다. 연결과정이 없기 때문에 TCP에 비해 속도가 빠르며, 고정회선이 아닌 여러 회선을 사용하기 때문에 데이터가 순서대로 도착하지 않아서 데이터가 일부 손실될 수 있다는 낮은 신뢰성을 특징으로 갖습니다.
따라서 속도보다 신뢰성이 중요하다면
TCP
, 신뢰성보다 속도가 중요하면UDP
를 사용합니다.
자바에서는 UDP
네트워킹을 위해서 java.net
패키지에서 DatagramSocket, DatagramPacket
클래스를 제공하고 있습니다. DatagramSocket
은 발신지와 수신지에서 사용하는 클래스이고, DatagramPacket
은 통신되는 데이터에 사용되는 클래스입니다.
UDP 서버는 DatagramSocket
객체를 생성하고 이용합니다. 마찬가지로 바인딩할 포트 번호를 지정해주어야합니다.
다음 코드는 데이터를 송수신하는 기본적인 기능만 갖춘 UDP 서버 코드입니다.
public class UDPServer {
private static DatagramSocket datagramSocket = null;
public static void main(String[] args) {
System.out.println("* 서버 종료는 q 입력 *");
//UDP 서버 시작
startServer();
Scanner scanner = new Scanner(System.in);
while (true) {
String key = scanner.nextLine();
if (key.equals("q")) {
break;
}
}
scanner.close();
//UDP 서버 종료
stopServer();
}
public static void startServer() { //서버 시작 메소드
//서버 작업 스레드
Thread thread = new Thread() {
@Override
public void run() {
try {
//DatagramSocket 객체 생성, Port binding
datagramSocket = new DatagramSocket(50001);
System.out.println(datagramSocket.getLocalPort() + "번 포트에서 서버 실행");
while (true) {
//클라이언트로 부터 데이터 받기
DatagramPacket receivePacket = new DatagramPacket(new byte[1024], 1024);
datagramSocket.receive(receivePacket);
String fromClientData =
new String(
receivePacket.getData(), 0,
receivePacket.getLength(), "UTF-8"
);
//클라이언트의 IP, Port number 취득
SocketAddress socketAddress = receivePacket.getSocketAddress();
//클라이언트로부터 받은 데이터를 가공해서 다시 클라이언트로 전송
String data = "서버가 보내는 데이터: " + fromClientData;
byte[] bytes = data.getBytes("UTF-8");
DatagramPacket sendPacket =
new DatagramPacket(bytes, 0, bytes.length, socketAddress);
datagramSocket.send(sendPacket);
}
}
catch (IOException e) {
System.err.println(e);
}
}
};
thread.start();
}
public static void stopServer() { //서버 종료 메소드
//서버 종료, Port unbinding
datagramSocket.close();
System.out.println("서버 종료");
}
}
여기서 유심히 볼 부분은 다음과 같은 부분들 입니다.
//클라이언트로 부터 데이터 받기 DatagramPacket receivePacket = new DatagramPacket(new byte[1024], 1024);
DatagramPacket
생성자의 첫 번째 매개변수는 수신한 데이터를 저장할 배열, 두 번째 매개변수는 수신 가능한 최대 바이트 수를 의미합니다.
datagramSocket.receive(receivePacket);
UDP 통신은 연결이 없기 때문에 서버가 열리면 클라이언트의
DatagramPacket
을 언제나 받을 수 있도록 대기해야합니다. 이런 역할을receive()
메소드가 수행하는데,receive()
는 데이터를 수신할 때 까지 블로킹되어있다가 데이터가 수신되면 매개변수로 전달된 DatagramPacket 객체에 저장합니다
//클라이언트의 IP, Port number 취득 SocketAddress socketAddress = receivePacket.getSocketAddress();
데이터를 다시 클라이언트에게 전송하기 위해서 클라이언트의 IP와 Port 번호가 필요한데요. 이는
receive()
를 통해 받은DatagramPacket
객체 내부에 정보가 들어있습니다. 이를 통해 정보가 담긴SocketAddress
를 전송에 활용합니다.
이번엔 데이터를 송수신하는 기본적인 기능만 갖춘 클라이언트를 만들어보겠습니다. 서버와 동일하게 DatagramSocket
클래스를 이용합니다.
public class UDPClient {
public static void main(String[] args) {
try {
//DatagramSocket 객체 생성
DatagramSocket datagramSocket = new DatagramSocket();
//서버로 데이터 송신
String data = "클라이언트가 보낸 데이터";
byte[] bytes = data.getBytes("UTF-8");
DatagramPacket sendPacket =
new DatagramPacket(
bytes, bytes.length, new InetSocketAddress("localhost", 50001)
);
datagramSocket.send(sendPacket);
//서버로부터 데이터 수신
DatagramPacket receivePacket = new DatagramPacket(new byte[1024], 1024);
datagramSocket.receive(receivePacket);
String str =
new String(
receivePacket.getData(), 0,
receivePacket.getLength(), "UTF-8"
);
System.out.println(str);
datagramSocket.close();
}
catch (IOException e) {
throw new RuntimeException(e);
}
}
}
datagramSocket.send(sendPacket);
send()
메소드를 이용하면 UDP 서버로DatagramPacket
을 전송합니다.
datagramSocket.receive(receivePacket);
마찬가지로 클라이언트 또한 언제든지 서버의 처리 결과를 받기 위해서
receive()
메소드를 사용합니다.