오늘은 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 플로우는 네트워크 공부를 조금이라도 했다면
한번쯤은 봤을 그림이다
↓

송신 호스트와 수신 호스트 간의 패킷 교환을 보여주는 그림이다
송신 호스트→ 수신 호스트로 패킷 123을 보내고 있는데,
주목해야할 부분은 마지막 (d)단계에서 패킷 손실이 일어난 부분이다
나는 다 보냈다고 생각해도 받는 쪽은 일부만 받았을 수도 있다
받는 쪽에서는 자신이 일부만 받았을 수 있기 때문에,
이를 고려해서 프로그램을 만들어야한다
(난 그래서 TCP가 좋다)
근데 패킷 손실이 있을 수 있는걸 왜 써???;;; 라고 생각할 수도 있지만
대신 속도가 빠르다
동영상이나 크기가 큰 데이터의 경우에는
그래서 UDP를 사용하는 경우가 많다
RTP와 RTCP도 기본은 UDP로 전송했던 걸로 기억한다...
** 참고) 알아두면 좋은 Datagram과 Packet 차이 **
Datagram: 순수한 사용자의 메시지를 나타냄
Packet: 사용자의 Datagram을 어떤 단위(MTU)에 맞춰서 토막낸 상태
3. 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 패킷 구조는 IP 헤더와 UDP 헤더와 데이터로 구성된다
데이터는 실제 우리가 보낼 데이터이고
그 앞에 UDP 헤더를 붙여서 UDP 사용을 명시한다
(TCP면 TCP 헤더가 붙겠쥬)
그리고 위 그림에서 UDP 헤더 부분은 아래로 구성된다

보내고 받을 Port의 정보와
뒤에 올 데이터의 크기 등을 담고 있다
TCP 헤더와 UDP를 참고자료로 첨부하고 마치겠다

[ 참고 자료 ]
** 읽어보는거 강추!! **
https://www.ibm.com/docs/en/zos/2.3.0?topic=functions-socket-create-socket
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 |