티스토리 뷰

  앞선 시간에 말했듯이 UDP 클래스에는 총 2개의 주요 클래스가 존재한다. 이번 시간에는 각 클래스가 가지는 의미와 특징에 대해알아보자. 그리고 추가적으로 UDP에서 Java 7의 NIO를 적용하게 되면 어떠한 특징을 갖게 되는지 역시 알아보도록 하자.

  • DatagramPacket : 데이터를 전송하기 위해서 Packet으로 감싸야 함
  • DatagramSocket : 보내야 되는 패킷을 전송하거나 받은 패킷을 확인하는 역할 수행

1. DatagramPacket 클래스

  UDP 데이터그램은 IP 헤더와 UDP 해더로 구성된다. UDP 헤더에는 출발지와 목적지의 포트에 대한 정보가 들어있고, IP 헤더에는 출발지와 목적지의 IP 주소를 포함하여 데이터 무결성을 보장하기 위해 필요한 다른 정보들이 들어가게된다. UDP의 경우 체크섬이 존재하지만, 만약 체크섬에서 오류가 나온다 하더라도 송,수신자에게 오류 결과를 알리지 않고 데이터를 버리게 된다.


  이론적으로 UDP의 데이터의 최대량은 65507바이트이다. 하지만 대부분의 플랫폼에서는 최대 용량을 사용하지 않고, 각 플랫폼에 맞는 제한된 용량만을 사용한다. UDP 데이터 그램의 구조는 다음과 같다.




  Datagram Packet 클래스는 사용 목적에 따라 생성자가 달라진다. Datagram 생성자의 경우 공통적으로 데이터그램 데이터를 보관할 바이트 배열과 바이트 배열에 저장된 길이를 나타내는 정수를 인자로 받게된다. 특히 송신과 수신에 따라 완전 다른 생성자를 가지게 된다.


송신 데이터그램 생성자


  • public DatagramPacket(byte[] data, int length, InetAddress destination, int port)
  • public DatagramPacket(byte[] data, int offset, int length, InetAddress destination, int port)
  • public DatagramPacket(byte[] data, int length, SocketAddress destination)
  • public DatagramPacket(byte[] data, int offset, int length, SocketAddress destination)


  offset은 데이터의 출발 지점을 의미한다. 만약 offset 값을 인자로 받지 않는 다면 시작 점은 0이된다. 그리고 데이터의 총 길이는 length로 입력하게 된다. 따라서, length보다 offset에 더 큰 값을 입력하게 되면 IllegalArgumentException이 된다.


  이를 이용해 송신을 위한 DatagramPacket은 다음과 같이 만들 수 있다.


1
2
3
4
5
6
7
8
9
10
11
String s = "UDP Data Example";
byte[] data = s.getBytes();
 
try {
    InetAddress address = InetAddress.getByName("www.google.co.kr");
    int Port = 7;
    DatagramPacket dp = new DatagramPacket(data, data.length, address, Port);
    
catch (IOException ex) {
}
 
cs


수신 데이터그램 생성자


  반대로 수신을 위한 데이터 그램의 경우 Port 정보를 입력하지 않아도 된다. 앞서 살펴보았던 offset과 length에 대한 정보를 입력하면 된다.


public DatagramPacket(byte[] buffer, int length)

public DatagramPacket(byte[] buffer, int offset, int length)


  그리고 위와 같이 생성된 DatagramPacket에 정보를 입력하거나 출력해서 우리의 프로그램에서 사용할 수 있어야 한다. 이를 위해서 Java에서는 Datagram에 관련된 여러 메소드를 제공하고 있다.


<DatagramPacket 메소드>


  get 메소드는 DatagramPacket의 데이터를 가져오기 위해 사용된다.


  • public InetAddress getAddress() : 데이터 그램의 InetAddress 객체 반환
  • public int getPort() : 포트 번호 반환 (정수형)
  • public SocketAddress getSocketAddress() : SocketAddress 객체 반환 (호스트의 IP주소, 포트....)
  • public byte[] getData() : 데이터그램의 데이터 반환
  • public int getLength() : 데이터그램에 저장된 데이터의 바이트 수 반환 (정수형)
  • public int getOffset() : 메소드 호출 시 반환된 배열에서 데이터가 시작되는 위치 반환 (정수형)


  set 메소드는 DatagramPakcet 클래스에 정보를 입력하는 데 사용된다.


  • public void setData(byte[] data) : 데이터그램의 데이터 변경
  • public void setData(byte[] data, int offset, int length) : 데이터그램의 데이터 변경 (offset, length 포함)
  • public void setAddress(InetAddress remote) : 데이터그램의 목적지 주소 변경 (IP)
  • public void setPort(int Port) : 데이터그램의 목적지 포트번호 변경
  • public void setAddress(SocketAddress remote) : 데이터그램의 목적지 주소 변경 (주소)
  • public void setLength(int length) : 데이터그램의 길이 변경



2. DatagramSocket 클래스

  위의 DatagramPacket이 데이터를 보내기 위한 박스라고 하면 DatagramSocket은 이 박스를 보내기 위해 존재하는 우체국의 역할을 한다. 우체국 없이 직접 상자를 전달할 수도 있지만 부산에 있는 친구한테 직접 상자를 전달하기가 너무 힘들기에, 우체국의 택배 시스템을 대신 이용한다. 네트워크도 마찬가지이다. 서로간의 합의된 Socket으로 데이터를 감싸주어야 네트워크 환경에서 우리의 상자를 목적지에 안전하게 보낼 수 있는 것이다. 


생성자


  DatagramSocket도 DatagramPacket과 마찬가지로 여러개의 생성자가 존재한다.


public DatagramSocket() throws SocketException


  별도의 인자가 없는 경우 임의의 포트에 소켓이 바인딩 된다.


public DatagramSocket(int port) throws SocketException


  지정된 포트 번호로 임의의 소켓을 바인딩 한다.


public DatagramSocket(int port, InetAddress address) throws SocketException


  특정 포트와 특정 인터넷 주소에서 대기중인 소켓을 생성한다.


<DatagramSocket 메소드>


  DatagramSocket 메소드는 주로 연결된 소켓과 데이터를 주고 받기 위해 사용된다.


  • public void send(DatagramPacket packet) throws IOException : Datagram Packet 전송
  • public void receive(DatagramPacket packet) throws IOException : Datagram Packet 수신
  • public void close() : DatagramSocket 객체 종료
  • public int getLocalPort() : 대기 중인 로컬 포트를 나타내는 정수 반환
  • public InetAddress getLocalAddress() : 소켓이 바인드된 로컬 주소 반환
  • public SocketAddress getLocalSocketAddress() : 소켓 객체 반환


<소켓 옵션>


DatagramSocket은 UDP 소켓을 위한 총 6개의 옵션을 제공한다.


1) SO_TIMEOUT


  SO_TIMEOUT은 receive() 메소드가 패킷을 대기하는 millisecond 단위의 시간이다. SO_TIMEOUT을 설정하게 되면 수신을 위해 해당 시간이 초과된 경우 InterruptedIOException이 발생한다.


public void setSoTimeout(int timeout) throws SocketException

public int getSoTimeout() throws IOException


2) SO_RCVBUF


  네트워크 I/O에서 사용되는 버퍼의 크기를 결정한다.


public void setReceiveBufferSize(int size) throws SocketException

public int getReceiveBufferSize() throws SocketException


3) SO_SNDBUF


  송신 버퍼의 크기를 설정한다.


public void setSendBufferSize(int size) throws SocketException

public int getSendBufferSize() throws SocketException


4) SO_REUSEADDR


  다수의 데이터그램 소켓이 같은 포트와 주소로 바인드 할 수 있는 지 제어한다. boolean 값으로 입력된다.


public void setReuseAddress(boolean on) throws SocketException

public boolean getReuseAddress() throws SocketException


5) SO_BROADCAST


  브로드캐스트 주소로 패킷을 보내거나 받을 수 있는 지 제어한다. 


public void setBroadcast(boolean on) throws SocketException

public boolean getBroadcast() throws SocketException


6) IP_TOS


  IP 패킷 헤더에 있는 IP_TOS 필드의 값으로 결정된다.


public int getTrafficClass() throws SocketException

public void getTrafficClass(int trafficClass) throws SocketException



3. DatagramChannel 클래스

  UDP 역시 TCP와 마찬가지로 Java NIO에서 작동이 가능하다. 논블록 UDP 애플리캐이션을 만들기 위해서 우린 DatagramChannel 클래스를 사용하면 된다. 


DatagramChannel channel = DatagramChannel.open();


  DatagramChannel은 생성자를 제공하지 않으므로, open() 메소드를 사용하여 객체를 생성한다. 아직 포트에 바인드 되지 않은 상태이다.


SocketAddress address = new InetSocketAddress(port);

channel.bind(address);


  소켓에 채널을 연결하면 port에 있는 포트번호로 소켓이 바인딩 된다.


  데이터 수신은 receive() 메소드로 진행할 수 있다.


public SocketAddress receive(ByteBuffer buffer) throws IOException


  채널을 이용한 데이터 수신은 아래와 같이 만들 수 있다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import java.io.IOException;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.SocketException;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
 
public class Solution {
 
    private final static int PORT = 13;
    
    public static void main(String[] args) throws SocketException, IOException {
        // TODO Auto-generated method stub
        
        try {
            DatagramChannel channel = DatagramChannel.open();
            DatagramSocket socket = channel.socket();
            SocketAddress address = new InetSocketAddress(PORT);
            
            socket.bind(address);
            ByteBuffer buffer = ByteBuffer.allocateDirect(8000);
            
            while(true) {
                SocketAddress client = channel.receive(buffer);
                buffer.flip();
                
                // 데이터 처리 부분
                /***********************
                
                ***********************/
                buffer.clear();
            }
            
        } catch (IOException ex) {
            System.err.println(ex);
        }
        
    }
 
}
cs



  데이터를 보내는 방법은 send() 메소드를 이용하면 된다. 


public int send(ByteBuffer buffer, SocketAddress address) throws IOException


  send() 메소드는 쓰인 바이트 수를 반환하게 된다. 앞서 만든 수신 소켓에 송신 메소드를 추가하면 아래와 같다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import java.io.IOException;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.SocketException;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
 
public class Solution {
 
    private final static int PORT = 13;
    
    public static void main(String[] args) throws SocketException, IOException {
        // TODO Auto-generated method stub
        
        try {
            DatagramChannel channel = DatagramChannel.open();
            DatagramSocket socket = channel.socket();
            SocketAddress address = new InetSocketAddress(PORT);
            
            socket.bind(address);
            ByteBuffer buffer = ByteBuffer.allocateDirect(8000);
            
            while(true) {
                SocketAddress client = channel.receive(buffer);
                buffer.flip();
                
                // 데이터 송신
                buffer.flip();
                channel.send(buffer, client);
                
                buffer.clear();
            }
            
        } catch (IOException ex) {
            System.err.println(ex);
        }
        
    }
 
}
 
cs



<DatagramChannel 메소드>


  • connect() : 데이터그램 채널을 열고 난 후 특정 원격 호스트에 연결하는 경우 사용한다.
  • read() : connect로 연결된 채널에 대해서 데이터를 읽어 들이는 경우 사용한다.
  • write() : 연결된 채널에 대해서 데이터를 입력하는 경우 사용한다.
  • close() : 사용이 끝나 채널을 종료해야 하는 경우 사용한다. 


'Skill > Programming - Network' 카테고리의 다른 글

25. OSI계층과 프로토콜  (0) 2019.10.26
24. IP 멀티캐스트  (0) 2019.10.20
22. UDP (User Datagram Protocol)  (0) 2019.10.04
21. 채널 (Channel)  (0) 2019.09.27
20. 버퍼 (Buffer)  (0) 2019.09.20
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함