UDP의 상세한 설명 -------------------------------------------------------------------------------------------------

앞선 장에선 TCP의 데이터 전송에 있어서 흐름제어 즉 데이터를 손실하지 않는 방법에 대해서 배웠다.

이번에는 반대로 UDP의 데이터 전송방식과 UDP는 어째서 데이터 손실을 보장하지 않는지에 대해서 알아볼 차례이다.


UDP방식에서 소켓이란? ---------------------------------------------------------------------------------------------

 TCP방식에서는 하나의 클라이언트에 전담자를 두어서 두개의 소켓이 데이터 송수신시 서로간에 신호를 통해서 데이터 전송의 신뢰성을 획득했다.


 A와 B의 관계에서 서로간에 송신과 수신이 잘 이루어졌다는 신호를 보내는 쓰리 웨이 핸드쉐이킹이 존재하기 때문이다.


 하지만 이것이 가능하기 위해서 1개의 클라이언트 소켓과 1개의 담당 소켓은 항시 서로 신호를 보내며 연결 상태를 유지해야 한다. 

 즉 1개의 서버에 10개의 클라이언트가 접속한다면 서버에서는 10명의 담당 소켓을 만들어줘야 신뢰성 있는 데이터 송수신이 가능한 구조였다.


 그렇다면 UDP의 경우에는 어떨가?


 UDP는 구성에 따라서 1개의 서버 1개의 소켓이 10개의 클라이언트를 맡아서 처리할 수도 있다. 

 거기다가 기존의 연결을 위한 listen함수도 필요가 없다. 소켓을 생성하고 보내기만 하면 된다. 

말로하는 것보다는 TCP처럼 각 소켓간의 송수신이 잘 되었는지에 대한 신호는 필요가 없다. 말로 설명하는 것보다는 함수를 보면서 이해해보자.


UDP방식 소켓 생성 ---------------------------------------------------------------------------------------------

UDP방식이라고 소켓을 다른 방식으로 생성하지는 않는다. socket 함수를 통해서 생성한다.

다만 함수에 들어가는 인자값이 다를 뿐이다. socket함수에 대해서 다시한번 살펴보자.


SOCKET socket(

  _In_  int af, -> 소켓이 사용할 프로토콜 체계 

  _In_  int type, -> 소켓이 사용할 연결방식 (연결 지향형 비연결지향형)

  _In_  int protocol -> 소켓이 사용할 실제적인 프로토콜.

);


SOCKET socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);

IPv4 인터넷 프로토콜 체계에서 동작하는 비 연결지향형 UDP 소켓을 생성하여 반환하라.

식으로 UDP소켓을 생성해주기만 하면 이 소켓을 통해서 데이터를 보낼 준비가 된것이다. 


UDP방식 데이터 전송  ---------------------------------------------------------------------------------------------


int sendto(

  _In_  SOCKET s, -> 데이터 전송에 필요한 UDP소켓

  _In_  const char *buf, -> 전송할 데이터를 저장하고 있는 버퍼의 주소 값

  _In_  int len, -> 전송할 데이터의 크기를 바이트 단위로 전달.

  _In_  int flags, -> 옵션 지정에 사용되는 매개변수, 지정할 옵션이 없다면 0 전달.

  _In_  const struct sockaddr *to, -> 목적지의 주소정보를 담고 있는 sockaddr 구조체 변수의 주소 값 전달.

  _In_  int tolen 매개변수 to으로 전달될 주소에 해당하는 구조체 변수의 크기 정보를 담고 있는 변수의 주소 값 전달.

); -> 성공시 전송된 바이트 수, 실패시 -1을 반환한다.


TCP기반의 send함수와 비교되는 점은 당연히 주소값을 넣어주는 곳이다. sendto함수는 연결되어있지 않기 때문에 어디로 보내야할지를 함수 자체에서 결정한다. 그러므로 bind함수를 통한 주소전달 작업이 필요없다.

이는 좀더 자세히 말하면 *to에 입력될 주소값만 바꿔주면 어떤 데이터든 전송이 가능하다는 말이다.


UDP방식 데이터 수신 ---------------------------------------------------------------------------------------------


int recvfrom(

  _In_         SOCKET s, -> 데이터를 수신할 UDP소켓

  _Out_        char *buf, -> 데이터 수신에 사용될 버퍼의 주소 값 전달.

  _In_         int len, -> 수신할 최대 바이트 수 전달, 당연히 2번째 인자값으로 전달되 버퍼의 크기를 넘을 수 없다.

  _In_         int flags, -> 옵션 지정에 사용되는 매개변수 지정할 옵션이 없다면 0 전달.

  _Out_        struct sockaddr *from, 발신지 정보를 채워넣을 sockaddr구조체 변수의 주소 값 전달.

  _Inout_opt_  int *fromlen -> 매개변수 from으로 전달될 주소에 해당하는 구조체 변수의 크기 정보를 담고 있는 변수의 주소 값.

); -> 성공시 받아온 데이터의 바이트 수 반환, 실패시 -1을 반환한다.


TCP기반의 recv함수와 비교되는 점은 당연히 주소값을 넣어주는 곳이다. 이는 좀더 자세히 말하면 *fom에 입력될 주소값만 바꿔주면 어떤 데이터든 수신이 가능하다는 말이다.


여기서 잠깐 데이터 경계가 뭐지? ---------------------------------------------------------------------------------



2번째 항목이 보면 데이터 경계를 구분하지 않는 다는 말이 확 와닫지 않는다.

자세히 설명한다면 이렇다. TCP같은 경우 100바이트의 데이터를 보낸다고 하면

20 20 20 20 20 바이트로 5번을 끊어서 보내건 100바이트를 한번에 보내건

recv함수 한번으로 현재까지 보낸 데이터를 수신이(한번에 혹은 여러번에) 가능하다. 

입출력 버퍼와 내부의 쓰리 웨이 핸드 쉐이킹이라는 과정이 존재하기 때문에 내부의 남아있는 데이터와 수신된 데이터를 파악이 가능하기 때문이다.

하지만 UDP방식은 다르다.

상대가 3번의 함수를 호출했다면 UDP방식의 소켓은 recv함수를 3번 호출해야만 한다.

이 차이는 굉장히 크다. UDP방식으로 통신했을 때는 상대가 보낸 함수의 호출횟수만큼 그리고 그 길이만큼 정확히 호출해줘야 한다.


CONNECTED UDP방식 데이터 전송  ----------------------------------------------------------------------------------

connect udp 소켓에 대해서 먼저 설명해야겠다.

대부분의 소켓이 일반적으로 하나의 소켓과 연결해서 사용하는 경우가 많다. 즉 sendto recvfrom함수를 사용한다고 하더라도 일반적으로 같은 주소로 계속 보내는 경우가 많다는 것이다. 하지만 위의 두 함수는 항시 주소값을 내부에서 체크하여 데이터를 전송한다. 그리고 이러한 과정은 지속적인 데이터 처리를 한다는 것이고 당연히 함수의 연산이 많아진다는 것을 의미한다.


그렇다면 애초에 UDP소켓을 하나의 주소로 연결시켜 놓으면 그 과정을 단축시킬 수 있게 된다.

소켓에 주소를 연결시키는 방식은 의외로 간단하다.

UDP소켓으로 커넥트함수를 호출하여 주소값으로 커넥트 시켜주면 이는 connected UDP소켓이 된다.

반대의 의미로 연결되지 않은 소켓은 unconnected UDP 소켓으로 불린다.


이렇게 연결된 소켓은 이미 연결이 완료되었기 때문에

send함수와 recv함수만 사용해서 데이터 송수신이 가능하다.




Posted by JJOREG