자바

자바 UDP 네트워킹 - 발신자와 수신자

알통몬_ 2017. 4. 13. 09:55
반응형


안녕하세요 알통몬입니다.

공감 및 댓글은 포스팅 하는데 아주아주 큰 힘이 됩니다!!

포스팅 내용이 찾아주신 분들께 도움이 되길 바라며

더 깔끔하고 좋은 포스팅을 만들어 나가겠습니다^^

 


지난 두 개의 포스팅에서 TCP 네트워킹을 통한 채팅 서버와 클라이언트를 만들어 보았는데요.

2017/04/12 - [자바] - 자바 TCP 채팅 서버 만들기 - 서버 클래스의 구조 및 startServer(), stopServer(),

2017/04/13 - [자바] - 자바 Socket 채팅 클라이언트 만들기


이번 포스팅에서는 UDP 네트워킹에 대해 공부하겠습니다.


UDP는 비연결 지향적 프로토콜입니다.

비연결 지향직이란 말은 데이터를 주고 받을 때 TCP 처럼 연결 절차를 거치는 것이 아니라

발신자가 일방적으로 데이터를 발신하는 방식을 말합니다.

연결 과정이 없기 때문에 TCP 보다 전송이 빠르지만

데이터 전달의 신뢰성이 떨어집니다.

UDP는 발신자가 데이터를 순차적으로 보내도 이 패킷들이 서로 다른 선로를 통해

전달이 될 수가 있어서 먼저 보낸 패킷이 나중에 도착하거나 잘못된 선로로

전송되어서 잃어버릴 수도 있습니다.


때문에 

데이터의 속도가 신뢰성보다 중요한 프로그램에서는 UDP를 사용하고

속도보단 신뢰성이 중요한 프로그램에서는 TCP를 사용하면 되겠습니다.


자바에서는 UDP 프로그래밍을 위해 java.net.DatagramSocket 과 java.netDatagramPacket 클래스를

제공합니다.

java.net.DatagramSocket : 발신점과 수신점에 해당하는 클래스입니다.

java.net.DatagramPacket : 주고 받는 패킷 클래스입니다.


발신자 구현하기

DatagramSocket datagramSocket = new DatagramSocket();

보내고자 하는 데이터를 바이트 배열로 생성하고, 문자열일 경우에는 UTF-8로 인코딩해서

바이트 배열을 얻으면 됩니다.

byte[] bArray = "문자열".getBytes("UTF-8");


데이터와 수신자 정보를 담고 있는 DatagramPacket 생성자의 첫 번째 매개 값은 보낼 데이터인

바이트 배열입니다. 두 번째 매개 값은 바이트 배열에서 보내고자 하는 항목 수입니다.

전체 항목을 보내려면 배열의 length 값을 대입하면 됩니다.

세 번째 매개 값은 IP 와 포트 정보를 가지고 있는 SocketAddress입니다.

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

byte[] byteArr = data.getBytes("UTF-8");

DatagramPacket datagramPacket = new DatagramPacket(

          byteArr, byteArr.length,

          new InetSocketAddress("localhost", 포트번호)

);


DatagramPacket 생성 후 이것을 매개값으로 해서 DatagramSocket의 send()메서드를 호출하면

수신자에게 데이터 전달

datagramSocket.send(datagramPacket);

보낼 데이터가 더 이상 없을 경우 DatagramPacket의 close() 메서드를 호출

datagramSocket.close();


발신자 예제)

import java.net.DatagramPacket;

import java.net.DatagramSocket;

import java.net.InetSocketAddress;


public class UdpSendExample {

 public static void main(String[] args) throws Exception {

  DatagramSocket datagramSocket = new DatagramSocket();

  

  System.out.println("[[발신 시작]]");

  

  for(int i=1; i<3; i++) {

   String data = "메시지" + i;

   byte[] byteArr = data.getBytes("UTF-8");

   DatagramPacket packet = new DatagramPacket(

    byteArr, byteArr.length, 

    new InetSocketAddress("localhost", 5001)

   );

   

   datagramSocket.send(packet);

   System.out.println("[[보낸 바이트 수]]: " + byteArr.length + " bytes");

  }

  

  System.out.println("[[발신 종료]]");

  

  datagramSocket.close();

 }

 

}



수신자 구현하기

DatagramSocket 객체를 생성하고, 매개 값으로는 바인딩할 포트 번호를 지정하면 됩니다.

DatagramSocket datagramSocket = new DatagramSocket(포트 번호);

DatagramSocket이 생성되었을 때 receive()를 호출하면 패킷을 읽을 준비를 합니다.

receive()는 패킷을 받는 시점까지 블로킹되었다가 패킷이 도착하면 매개 값으로 주어진

DatagramPacket에 패킷 내용을 저장합니다.

datagramSocket.receive(datagrampacket);


패킷에 저장할 DatagramPacket 생성 :

첫 번째 매개 값은 읽은 패킷 데이터를 저장할 바이트 배열입니다.

두 번째 매개 값은 읽을 수 있는 최대 바이트 수로써 첫 번째 바이트 배열의 크기랑 같거나 작아야 합니다.

일반적으로는 첫 번째 바이트 배열의 크기를 줍니다.

DatagramPacket datagramPacket = new DatagramPacket(new byte[10] , 10);


receive() 로 패킷을 읽은 후에 DatagramPacket의 getData()로 데이터가 저장된 바이트 배열을

얻을 수 있습니다. 그리고 getLength()를 호출하면 읽은 바이트 수를 얻을 수 있습니다.

받은 데이터가 인코딩된 문자열이라면 디코딩하여 문자열을 얻으면 됩니다.

String receiveData = new String(datagramPacket.getData(), 0, datagramPacket.getLength(), "UTF-8");


수신자가 패킷을 받고 나서 발신자에게 응답 패킷을 보내고 싶으면 발신자의 IP와 포트를 알아야 합니다.

DatagramPacket의 getSocketAddress()를 호출하면 발신자의 SocketAddress 객체를 얻어 낼 수 있어서

발신자에게 응답 패킷을 보낼 때 send()를 이용할 수 있습니다.

SocketAddress socketAddress = datagramPacket.getSocketAddress();


작업 스레드를 종료시키려면 receive()가 블로킹되어있는 상태에서 DatagramSocket의 close()를

호출하시면 됩니다. 이런 경우 receive()에서 SocketException 이 발생하게 되는데 

예외 처리 코드를 통해 작업 스레드를 종료시키면 됩니다.

datagramPacket.close();



수신자 예제)

import java.net.DatagramPacket;

import java.net.DatagramSocket;


public class UdpReceiveExample extends Thread {

 public static void main(String[] args) throws Exception {

  DatagramSocket datagramSocket = new DatagramSocket(5001);

  

  Thread thread = new Thread() {

   @Override

   public void run() {

    System.out.println("[[수신 시작]]");

    try {

     while(true) {

      DatagramPacket packet = new DatagramPacket(new byte[100], 100);

      datagramSocket.receive(packet);

      

      String data = new String(packet.getData(), 0, packet.getLength(), "UTF-8");

      System.out.println("[[받은 내용 : "  + packet.getSocketAddress() + "] " + data]);

     }

    } catch (Exception e) {

     System.out.println("[[수신 종료]]");

    }

   }   

  };

  thread.start();

  

  Thread.sleep(10000);

  datagramSocket.close();

 }

}


이상입니다.

다음 포스팅에서는 IO 와 NIO의 차이에 대해 공부하겠습니다.

반응형