출처: TCP/IP 윈도우 소켓 프로그래밍(https://product.kyobobook.co.kr/detail/S000001636201)
일반적으로 UDP 서버와 클라이언트는 다음과 같은 순서로 소켓 함수를 호출한다.
UDP 서버
socket
함수를 호출해 소켓을 생성한다. 프로토콜을 결정한다.bind
함수를 호출해 생성한 소켓에 지역 IP, 포트 번호를 바인딩한다.recvfrom
함수를 호출하면 클라이언트가 보낸 데이터를 받을 수 있다. 이때 클라이언트의 원격 IP 주소와 원격 포트 번호를 알 수 있다.sendto
함수를 호출하면 데이터를 전송할 수 있다.closesocket
함수를 호출하여 소켓을 닫는다.
UDP 클라이언트
socket
함수를 호출해 소켓을 생성한다. 프로토콜을 결정한다.sendto
함수를 호출해 서버에 데이터를 보낸다. 이때 서버의 원격 IP 주소와 원격 포트 번호가 결정되고, 클라이언트 자신의 지역 IP 주소와 지역 포트 번호도 결정된다.recvfrom
함수를 호출하면 데이터를 받을 수 있다.closesocket
함수를 호출하여 소켓을 닫는다.
이 과정에서 주의해야 되는 부분은 다음과 같다.
- 블로킹 소켓을 사용할 경우 데드락에 주의해야 한다.
- 클라이언트는 데이터를 받은 후 송신자의 주소를 확인해야 한다.
recvfrom
함수는 UDP 서버가 보낸 데이터는 물론, 전혀 다른 UDP 응용 프로그램이 보낸 데이터도 수신할 수 있기 때문이다.
위에서 설명한 UDP 클라이언트는 connect
함수를 호출하지 않아 특정 대상을 지칭하지 않은 상태에서 통신하는 클라이언트가 된다. 그렇기 때문에 recvfrom
, sendto
라는 함수 자체가 특정한 대상과 통신을 하는 용도로 사용될 수 없음을 내포함과 동시에 connect
함수를 호출하면 TCP 응용 프로그램에서의 통신처럼 recv
, send
를 사용할 수 있다는 것을 의미한다.
그래서 connect
를 호출해 서버를 결정하고 나서 recv
, send
함수를 사용하는 UDP 클라이언트는 다음과 같은 특징을 가진다.
sendto
함수를 사용한 경우보다 효율적이다.connect
함수로 서버 주소를 한 번만 설정하고send
함수에서 이 정보를 재사용하기 때문이다.- 데이터를 받은 후 송신자의 주소 정보를 확인하지 않아도 된다.
connect
함수를 사용하여 통신 대상을 지정했기 때문이다.
1. UDP 데이터 전송 함수
UDP 서버 클라이언트는 데이터 전송 함수를 제외하면 TCP 서버 클라이언트와 공유하는 코드가 많다.
1.1 sendto
sendto
함수는 응용 프로그램 데이터를 운영체제의 송신 버퍼(보내고 바로 버림)에 복사하여 데이터를 전송한다. UDP를 사용하고 있다면 sendto
함수를 호출할 때 소켓의 지역 IP 주소와 포트 번호가 결정되지 않은 상태라면 운영체제가 자동으로 결정해준다. TCP의 bind
함수 역할을 포함한다고 볼 수 있다.
int sendto(
SOCKET s,
const char* buf,
int len,
int flags,
const struct socketaddr* to,
int tolen
);
// 성공: 보낸 바이트 수
// 실패: SOCKET_ERROR
s
: 통신에 사용할 소켓이다.buf
: 보낼 데이터를 담고 있는 응용 프로그램 버퍼의 주소len
: 보낼 데이터의 크기(버퍼의 크기 아님)flags
:sendto
함수의 동작을 바꾸는 옵션이다. 대부분 0으로 사용한다.to
: 목적지 주소를 담고 있는 소켓 주소 구조체다.tolen
: 목적지 주소를 담고 있는 소켓 주소 구조체의 크기다.
실제 사용 방법은 다음과 같다.
SOCKADDR_IN serveraddr;
char buf[BUFSIZE];
retval = sendto(
sock,
buf,
retval,
0,
(SOCKADDR *)&serveraddr,
sizeof(serveraddr));
sendto
함수를 사용할 때 주의 사항은 다음과 같다.
sendto
함수는 UDP 소켓은 물론 TCP 소켓에도 사용할 수 있다. 이 경우에는to
와tolen
인자는 무시된다. TCP 소켓에 사용할 때만flags
에MSG_OOB
를 사용할 수 있다.sendto
함수로 보낸 데이터는 독립적인 UDP 데이터그램(패킷)으로 만들어 전송된다. 수신 측에서는recvfrom
함수 호출 한 번으로 이 데이터를 읽을 수 있다. 덕분에 데이터의 경계를 구분하는 작업을 할 필요가 없다.- UDP 소켓에 대해
sendto
함수를 호출할 경우 한 번에 보낼 수 있는 데이터의 크기에 제한이 있다.(0 ~ 65507) 실제 사용할 때에는 최대값보다 훨씬 작은 크기를 사용하는 것이 바람직하며 브로드캐스트로 패킷을 보낼 경우 512 바이트보다 작은 크기를 사용할 것을 권고한다. sendto
함수로 보낸 데이터는 운영체제에 복사되어 전송된 후 곧바로 버려진다.sendto
함수가 리턴했다고 실제 데이터 전송이 완료된 것은 아니며, 데이터 전송이 끝나도 상대방이 받았는지 확인할 수 없다.- 블로킹 소켓을 사용할 경우, 커널 영역에 데이터를 복사할 공간이 부족하면 블록된다.
1.2 recvfrom
recvfrom
함수는 운영체제의 수신 버퍼에 도착한 데이터를 응용 프로그램 버퍼에 복사한다. recv
함수와 다른 점은 UDP 패킷 데이터를 한 번에 하나만 읽을 수 있다는 점이다. 따라서 응용 프로그램 버퍼를 크게 잡는다고 많은 데이터를 한 번에 읽을 수는 없다.
int recvfrom(
SOCKET s,
char* buf,
int len,
int flags,
struct sockaddr* from,
int* fromlen
);
// 성공: 받은 바이트 수
// 실패: SOCKET_ERROR
s
: 통신에 사용할 소켓이다.sendto
와는 달리 반드시 지역 IP 주소와 포트 번호가 미리 결정되어 있어야 한다.buf
: 받은 데이터를 저장할 응용 프로그램 버퍼 주소다.len
: 응용 프로그램 버퍼의 크기다. 도착한 UDP 패킷 데이터가len
보다 크면 나머지는 버린다. 이때recvfrom
함수는SOCKET_ERROR
를 리턴한다. 따라서 예상되는 UDP 패킷 데이터의 최대 크기를 감안해 응용 프로그램 버퍼를 준비해야 한다.flags
:recvfrom
함수의 동작을 바꾸는 옵션으로 대부분 0을 사용한다.from
: 송신자의 주소 정보다.fromlen
: 정수형 변수를from
이 가리키는 소켓 주소 구조체의 크기로 초기화 한 후 전달한다.recvfrom
함수가 리턴하면 원격 주소 정보의 크기를 갖게 된다.
recvfrom
은 다음과 같이 사용한다.
// 통신 상대의 주소를 저장할 변수 선언
SOCKADDR_IN peeraddr;
int addrlen;
// 수신용 버퍼
char buf[BUFSIZE];
addrlen = sizeof(peeraddr);
retval = recvfrom(
sock, // 수신용 소켓
buf,
BUFSIZE,
0,
(SOCKADDR*)&peeraddr,
&addrlen
);
주의 사항은 다음과 같다.
recvfrom
함수는 UDP 소켓은 물론 TCP 소켓에도 사용할 수 있다. 이 경우from
과fromlen
은 무시된다. TCP 소켓을 사용할 때만flags
에MSG_OOB
옵션을 사용할 수 있다.sendto
함수로 보낸 데이터는 독립적인 UDP 데이터그램으로 전송되며, 수신 측에서는recvfrom
함수를 호출하여 이 데이터를 읽을 수 있다. 따라서 응용 프로그램 수준에서 데이터 경계를 구분할 필요가 없다.- UDP 소켓에 대해
recvfrom
함수를 호출한 경우 리턴 값이 0이 될 수 있다. 이는 상대방이sendto
함수 호출 시 데이터 크기를 최솟값인 0으로 설정했다는 뜻이다. UDP에는 연결 개념이 없기 때문에 종료 개념도 없어 0이 특별한 의미가 있는 것은 아니다. 반면 TCP 소켓에 대해recvfrom
함수를 호출하면 리턴 값이 0이면 정상 종료를 의미한다. - 블로킹 소켓을 사용할 경우, 소켓 수신 버퍼에 도착한 데이터가 없으면
recvfrom
함수는 블록된다.
'네트워크 > 윈도우 소켓 프로그래밍' 카테고리의 다른 글
UDP 서버 클라이언트 개념 (0) | 2024.04.18 |
---|---|
스레드 동기화 - 임계 영역, 이벤트 (0) | 2024.04.08 |
멀티스레드 TCP 서버 (0) | 2024.04.08 |
멀티 스레드 서버를 위한 스레드 기초 (0) | 2024.04.08 |
응용 프로그램 데이터 전송 (0) | 2024.04.08 |