본문 바로가기

Protocol

UDP를 알아보자

오늘은 UDP에 대해서 정리해보려고 한다

TCP를 좋아하는 나에게 UDP를 새로 만들어야하는 일이 생겨서

UDP는 너무 옛날에 봐서 가물가물해짐

 

가볍게 리마인드하고 가보자잇

 

 

대학교 때 이걸로 네트워크 수업 들었었는데

교수님 말씀이 다 옳으셨다...

 


1. UDP 란?

 

TCP ( Transmission Control Protocol )

- OSI 7계층에서 전송층 (4번째 계층)에 해당

- 전송을 제어하는 프로토콜

- 각 peer 간 신뢰적인 연결 지향성 서비스 제공

- 신뢰성 (연속X)

 

 

UDP (User Datagram Protocol)

- 얘도 OSI 7계층에서 전송층에 해당됨

- 사용자의 Datagram을 전송하는 프로토콜

- 신뢰성이 낮은 프로토콜

- 연속성

 

 

TCP와 UDP 모두 상대방에게 자신의 메시지를 전달할 때 사용되는 프로토콜이다

하는 짓이 비슷하지만, call 해서 사용하는 함수나 흐름이 조금 다르다

 

TCP와 UDP의 가장 큰 차이점은 신뢰성 연속성이라고 생각한다

TCP 통신은 상대방이 잘 받았는지 못 받았는지 확인하는 과정을 거치지만

UDP 통신은 일단 자기가 보낼거를 보내고

그 뒤는 생각하지 않는다

 

자세한 얘기는 흐름을 통해 보도록 하겠다

 

 

2. UDP 흐름

 

UDP 플로우는 네트워크 공부를 조금이라도 했다면

한번쯤은 봤을 그림이다

 

UDP 패킷 교환 방식

 

송신 호스트와 수신 호스트 간의 패킷 교환을 보여주는 그림이다

송신 호스트→ 수신 호스트로 패킷 123을 보내고 있는데,

주목해야할 부분은 마지막 (d)단계에서 패킷 손실이 일어난 부분이다

 

나는 다 보냈다고 생각해도 받는 쪽은 일부만 받았을 수도 있다

받는 쪽에서는 자신이 일부만 받았을 수 있기 때문에,

이를 고려해서 프로그램을 만들어야한다

(난 그래서 TCP가 좋다)

 

근데 패킷 손실이 있을 수 있는걸 왜 써???;;; 라고 생각할 수도 있지만

대신 속도가 빠르다

동영상이나 크기가 큰 데이터의 경우에는

그래서 UDP를 사용하는 경우가 많다

RTP와 RTCP도 기본은 UDP로 전송했던 걸로 기억한다...

 


** 참고) 알아두면 좋은 Datagram과 Packet 차이 **
Datagram: 순수한 사용자의 메시지를 나타냄
Packet: 사용자의 Datagram을 어떤 단위(MTU)에 맞춰서 토막낸 상태


 

3. UDP 통신 함수

 

UDP 흐름

 

 /*그림에는 Server와 Client가 정해져 있는데,

명칭상이고 UDP는 서버 클라이언트 개념보다

호스트 대 호스트로 생각하는게  더 좋다 */

 

UDP 통신시에 사용하는 함수는 위에 그림에서 보는 것처럼

socket(), bind, recvfrom(), sendto(), close()로 5개가 있다

하나하나 살펴보장

 

(1) socket()

TCP나 UDP 통신시, endpoint를 만들어주는 함수

 

#include <sys/types.h>          
#include <sys/socket.h>

int socket(int domain, int type, int protocol);

 

Return

- 만들어진 소켓 descriptor 번호

- 실패시 -1 반환, errno(EACCES, EIO 등)를 설정함 

 

Parameters

1) domain

- 요청된 주소 도메인 (AF_INET, AF_INET6, AF_UNIX 등)

- IPv4나 IPv6 같은 프로토콜 집합(protocol family)를 의미

 

2) type

- 생성된 소켓 유형 (SOCK_STREAM, SOCK_DGRAM, SOCK_RAW 등)

- TCP일 경우 SOCK_STREAM을 매개변수로, UDP일 경우 SOCK_DGRAM을 사용하면 됨

 

3) protocol

- UDP나 TCP 같은 특정 프로토콜을 명시함 (0, IPPROTO_UDP, IPPROTO_TCP)

- 0을 사용하면 알아서 해줘서 보통 0을 많이 씀

 

 

(2) bind()

socket()으로 만들어진 소켓(fd)에 로컬 이름을 붙여줌(binding)

 

#include <sys/types.h>         
#include <sys/socket.h>

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

 

Return

- binding 성공/실패 여부 (성공시에는 0을 반환)

- 실패시 -1 반환, errno(EACCES, EIO 등)를 설정함 

 

Parameters

1) sockfd

- socket() 호출에서 반환된 소켓 descriptor

 

2) const struct sockaddr

- socket에 붙여줄 이름을 포함하는 socketaddr 구조체의 주소

 

3) addrlen

- 두번째 인자인 sockaddr 로 생성한 주소 크기

- ex) sizeof(addr)

 


** bind는 꼭 필요할까? **

bind는 서버나 클라이언트에서 꼭 사용하지 않아도 된다

왜냐면, bind의 목적은 socket에 address와 port를 연결하는거다

즉, 패킷을 수신할 때 이걸 어디로 보낼래? 하는걸 정하는거다

 

OS가 패킷을 어디로 전달해야하는지 모를 때 사용하면 되고,

(recvfrom을 먼저 호출하는 경우에 해당)

sendto를 먼저 호출할 때는 OS가 알아서 bind를 해줘서 안써도 됨


 

(3) recvfrom()

 

소켓 discriptor(이제 그냥 소켓 FD로 부르겠다)로부터 메시지를 수신해서 버퍼에 저장

 

 #include <sys/types.h>
 #include <sys/socket.h>

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
                        struct sockaddr *src_addr, socklen_t *addrlen);

 

Return

-  메시지 또는 데이터 그램의 길이를 Byte 단위로 반환

- 0 반환시에는 연결이 close 됐음을 의미

- 실패시 -1 반환, errno(EACCES, EIO 등)를 설정함 

 

Parameters (너무 많긔)

1) int sockfd

- socket() 호출에서 반환된 소켓 fd

 

2) void *buf

- 데이터를 받을 버퍼에 대한 주소

 

3) size_t len

- 두번째 인자인 buf의 길이 (Byte 길이)

- 4번째 인자인 flag에 MSG_CONNTERM 플래그 설정이 된 경우에는, len이 0이어야 함

 

4) int flags

- 데이터를 읽을 때에 주는 옵션

- 0, MSG_CONNTERM, MSG_PEEK, MSG_OOB 또는 MSG_WAITALL로 설정할 수있는 매개 변수

- 잘 모르겠다면 0을 쓰자

 

5) struct sockaddr *src_addr

- 데이터가 수신되는 소켓 주소 struct에 대한 주소

- 참고) source address 의미: 송신하는 쪽 IP 주소

 

6) socklen_t *addlen

- 5번째 인자인 src_addr의 크기

- src_addr이 NULL이라면 길이는 무시됨

 

 

(4) sendto()

 

소켓 fd를 통해 수신자에게 데이터를 보내는 함수

 

#include <sys/types.h>
#include <sys/socket.h>

ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
                      const struct sockaddr *dest_addr, socklen_t addrlen);

 

Return

-  보낸 문자 수를 반환

- 0 이상의 값은 전송된 Byte 수를 나타냄 ( 완벽하게 다 보냈다고는 안했다. 보장 X )

- 실패시 -1 반환, errno(EACCES, EIO 등)를 설정함 

 

Parameters (너무 많긔)

1) int sockfd

- socket() 호출에서 반환된 소켓 fd

 

2) const void *buf

- 전송할 메시지가 포함된 버퍼에 대한 주소

- 보낼 메시지는 변경되면 안되기 때문에 const가 붙는 듯

 

3) size_t len

- 두번째 인자인 buf의 길이 (Byte 길이)

 

4) int flags

- 데이터를 보낼 때 주는 옵션

- 0, MSG_OOB, MSG_DONTROUTE 등

 

5) struct sockaddr *dest_addr

- 데이터를 받을(수신측) 호스트의 주소 (상대방이겠쥬?)

- 참고) destination address 의미: 수신하는 쪽 IP 주소

 

6) socklen_t *addlen

- 5번째 인자인 dest_addr의 크기

 

(5) close()

 

소켓 fd를 close 하는 함수

 

#include <unistd.h>

int close(int fd);

 

Return

-  close 성공시 0을 반환

- 실패시 -1 반환, errno 값 설정해줌 

 

Parameters

1) int fd

- 닫을 socket fd

- 대기중인 입력 데이터가 있을 때는 완전히 닫히지 않고 재설정 될 수도 있음

 


** 참고)

TCP인 경우, socket() 으로 만들고 setsocktopt() 옵션으로 SO_LINGER 설정시,

close를 호출하여 닫기를 원할 경우 데이터가 존재시 즉시 종료되지 않게 할 수 있음


 

4. UDP Packet 구조

 

UDP 패킷 구조

 

UDP 패킷 구조는 IP 헤더와 UDP 헤더와 데이터로 구성된다

데이터는 실제 우리가 보낼 데이터이고

그 앞에 UDP 헤더를 붙여서 UDP 사용을 명시한다

(TCP면 TCP 헤더가 붙겠쥬)

 

그리고 위 그림에서 UDP 헤더 부분은 아래로 구성된다

 

UDP 헤더의 구조

 

보내고 받을 Port의 정보와

뒤에 올 데이터의 크기 등을 담고 있다

 

 

TCP 헤더와 UDP를 참고자료로 첨부하고 마치겠다

 

 

 


[ 참고 자료 ]

 

** 읽어보는거 강추!! **

https://www.ibm.com/docs/en/zos/2.3.0?topic=functions-socket-create-socket 

https://kldp.org/node/136920

 

UDP Client Socket에서 bind를 안해도 되는 이유가 무엇일까요? | KLDP

UDP C/S socket 을 만들다 문득 궁금해서 여쭈어봅니다. 서버에서는 bind를 하는데 클라이언트에서는 bind를 하지 않는데 이러한 일련의 프로세스들이 어떻게 돌아가는 것인가요?

kldp.org

 

'Protocol' 카테고리의 다른 글

SOAP을 알아보자  (0) 2021.06.10
HTTP Chunked Message를 알아보자  (0) 2021.03.09
SMTP를 알아보자  (0) 2020.12.16