본문 바로가기

진리는어디에

sendto & recvfrom

 TCP는 연결지향의 통신에 신뢰성을 보장하고 UDP는 비연결 지향의 통신에 신뢰성을 보장하지 못한다는 것 같은 지루하고 이론적인 이야기는 저기 나 보다 더 많이 공부하신 분들이 써 놓은 책이나 웹페이지에게 떠넘겨 버리고(http://en.wikipedia.org/wiki/User_Datagram_Protocol), 여기서는 유닉스계열 쪽의 UDP소켓 API의 사용법에 대해 간략히 알아보고, 그에 발생 할 수 있는 예외 상황들을 다뤄 보고자 한다.

 UDP 통신은 위에서 언급했던것 처럼 말 그대로 비연결이다. 한글로 또박또박 풀어 쓰자면 연결하는 과정이 따로요 없다는 소리다. 어떻게 연결을 하지 않고 통신을 할 수 있는가라고 반문한다면 TCP four-way handshake처럼 쌍방간에 연결을 맺었다는 합의가 없다는 소리다. UDP는 단순히 데이터그램에 가야 할 주소를 적어 보내 줄 뿐그 이상 그 이하도 아니다. 하나의 데이터그램이 전송되기 위해서는 반드시 주소와 포트가 따라 붙어야 하고, 그러므로써 하나, 하나 라는 구분이 생기게 된다. 시작 할 때 부터 쌍방간에 연결을 맺고 시작하는 TCP는 데이터의 시작과 끝만 있을 뿐 그 사이에는 모두 하나의 커다란 흐름, 즉 Stream으로 보고, UDP는 그 '구분'이 되는 특성상 Datagram이라고 한다. 이 Datagram의 특성 때문에 TCP는 read와 write의 함수 호출 횟수가 일치 하지 않는 반면, UDP는 read/write의 수가 항상 일치한다. 즉 두번 쓰면 두번에 걸쳐 읽어야지 결코 한번에 읽을 수는 없다는 이야기다.

 

 UDP가 데이터그램을 주고 받는 함수는 아래와 같다. TCP read/write와 비슷하긴 하지만 데이터그램을 보낼 때 마다 주소를 넣어 주어야 하는 부분이 다르다. 받는 부분 역시 어디서 보내 왔는지 알기 위해 주소를 받아 오는 out parameter가 있다.

#include <sys/socket.h>

ssize_t recvfrom(int sockfd, void *buff, size_t nbytes, int flags,
                                                      
struct sockaddr *from, socklen_t *addrlen);

ssize_t sendto(int sockfd, const void *buff, size_t nbytes, int flags,
                                                       const struct sockaddr *to, socklen_t addrlen
);

Both return: number of bytes read or written if OK, –1 on error
man page : http://www.die.net/doc/linux/man/man2/recvfrom.2.html
http://www.die.net/doc/linux/man/man2/sendto.2.html


 여기서 리턴 값에 잠시 주목 해보자. 리턴이 -1이 아닌 것이 나왔다는 것은 send/receive 작업이 성공했다는 뜻이다. 하지만 send에서 성공 했다는 것은 내가 보낸 데이터그램이 peer쪽으로 성공적으로 도착했다는 소리가 아니다. 다만 보내는 것 까지가 성공했다는 소리다. peer쪽의 소켓이 유효한지 안한지를 알 수는 없다는 소리다. 일단 커널 버퍼에 write 작업을 할 수 있는 공간만 있다면 무조건 OK.!!

 

 아래는 늘상 그렇듯이 간단한 UDP기반의 에코 서버/클라이언트다. 서버에 TCP 처럼 accept함수가 없다는 사실과, 클라이언트에 connect함수가 없다는 사실에 주목 하기 바란다.


 

유익한 글이었다면 공감(❤) 버튼 꾹!! 추가 문의 사항은 댓글로!!