1. UDP란?
UDP(User Datagram Protocol)는 비연결 지향적이고 신뢰성이 없는 프로토콜이다.
특징 :
- 비연결성: 데이터 전송 전 연결 설정 불필요
- 비신뢰성: 데이터 손실, 중복, 순서 오류 가능성 있음
- 빠른 전송: 연결 설정, 오류 검출, 재전송 등의 오버헤드가 없음
- 메시지 경계 유지: 송신한 메시지 단위 그대로 수신
- 브로드캐스트와 멀티캐스트 지원
UDP는 TCP와 달리 서버와 클라이언트가 연결되지 않는 비연결 지향 통신이다.
2. UDP 구현 순서
UDP 서버 구현 순서 :
1) 소켓 생성
2) 소켓 바인딩
3) 데이터 수신 및 송신
UDP 클라이언트 구현 순서 :
1) 소켓 생성
2) 데이터 송신 및 수신
소켓을 연결하는 과정이 없어 TCP에 비해 단순하다.
3. 장단점
장점:
1) 빠른 속도
- 연결 설정 과정이 없음
- 패킷 손실 시 재전송하지 않음
- 오버헤드가 적음
2) 단순한 구조
- 최소한의 프로토콜 메커니즘
- 헤더 크기가 작음 (8바이트)
- 구현이 간단함
3) 리소스 사용량이 적음
- 연결 상태를 유지하지 않음
- 버퍼링이 필요 없음
4) 실시간성이 좋음
- 지연이 적어 실시간 스트리밍에 적합
- 브로드캐스트/멀티캐스트 가능
단점:
1) 신뢰성이 낮음
- 데이터 전송 순서가 보장되지 않음
- 패킷 손실 여부를 확인할 수 없음
- 중복 패킷 발생 가능
2) 데이터 무결성 보장 안됨
- 패킷 손실이나 오류를 검출하지 않음
- 애플리케이션 레벨에서 직접 처리 필요
3) 흐름 제어 없음
- 수신자의 처리 능력을 고려하지 않음
- 네트워크 혼잡 제어 없음
UDP는 빠른 속도와 낮은 지연시간이 필요한 경우, 또는 일부 데이터 손실이 허용되는 애플리케이션에 적합하다.
실시간 스트리밍이나 게임의 캐릭터 위치 업데이트 등에 UDP 통신을 사용한다.
4. 간단한 송수신 서버
UDP로 통신을 하는 송수신 서버이다.
서버
#include <iostream>
#include <string>
#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib")
#define PORT 7777
using namespace std;
void HandleError(const char* cause) {
int errCode = ::WSAGetLastError();
cout << cause << " ErrorCode : " << errCode << endl;
}
int main() {
WSADATA wsaData;
if (::WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
return 1;
// UDP 소켓 생성
SOCKET serverSocket = ::socket(AF_INET, SOCK_DGRAM, 0);
if (serverSocket == INVALID_SOCKET) {
HandleError("Socket");
return 1;
}
SOCKADDR_IN serverAddr;
serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.s_addr = INADDR_ANY;
serverAddr.sin_port = htons(PORT);
// 바인드
if (::bind(serverSocket, (SOCKADDR*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) {
HandleError("Bind");
return 1;
}
cout << "UDP Server Started" << endl;
// 데이터 송수신
while (true) {
char recvBuffer[100] = { 0 };
SOCKADDR_IN clientAddr;
int addrLen = sizeof(clientAddr);
// recvfrom으로 클라이언트의 주소 정보도 같이 받음
int recvLen = ::recvfrom(serverSocket, recvBuffer, sizeof(recvBuffer), 0,
(SOCKADDR*)&clientAddr, &addrLen);
if (recvLen <= 0) {
HandleError("Recvfrom");
break;
}
cout << "Received Data : " << recvBuffer << endl;
// 받은 클라이언트 주소로 데이터 전송
char sendBuffer[100] = "Hello Client!";
::sendto(serverSocket, sendBuffer, strlen(sendBuffer), 0,
(SOCKADDR*)&clientAddr, sizeof(clientAddr));
}
::closesocket(serverSocket);
::WSACleanup();
system("pause");
}
클라이언트
#include <iostream>
#include <winsock2.h>
#include <WS2tcpip.h>
#pragma comment(lib, "ws2_32.lib")
#define PORT 7777
#define SERVERIP "127.0.0.1"
using namespace std;
void HandleError(const char* cause) {
int errCode = ::WSAGetLastError();
cout << cause << " ErrorCode : " << errCode << endl;
}
int main() {
WSADATA wsaData;
if (::WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
return 1;
// UDP 소켓 생성
SOCKET clientSocket = ::socket(AF_INET, SOCK_DGRAM, 0);
if (clientSocket == INVALID_SOCKET) {
HandleError("Socket");
return 1;
}
// 서버 주소 설정
SOCKADDR_IN serverAddr;
serverAddr.sin_family = AF_INET;
::inet_pton(AF_INET, SERVERIP, &serverAddr.sin_addr);
serverAddr.sin_port = htons(PORT);
// 송신 (connect 없이 바로 전송)
char sendBuffer[100] = "Hello Server!";
int sendLen = ::sendto(clientSocket, sendBuffer, strlen(sendBuffer), 0,
(SOCKADDR*)&serverAddr, sizeof(serverAddr));
if (sendLen <= 0) {
HandleError("Sendto");
return 1;
}
cout << "Data Sent" << endl;
// 수신
char recvBuffer[100] = { 0 };
SOCKADDR_IN peerAddr;
int addrLen = sizeof(peerAddr);
int recvLen = ::recvfrom(clientSocket, recvBuffer, sizeof(recvBuffer), 0,
(SOCKADDR*)&peerAddr, &addrLen);
if (recvLen <= 0) {
HandleError("Recvfrom");
return 1;
}
cout << "Received Data : " << recvBuffer << endl;
::closesocket(clientSocket);
::WSACleanup();
system("pause");
}
서버와 클라이언트가 연결되면 메시지를 한번 주고받는다.
'학습 > C++ 소켓 프로그래밍' 카테고리의 다른 글
Non-Blocking 소켓 (0) | 2024.11.09 |
---|---|
소켓 옵션 (0) | 2024.11.08 |
TCP 소켓 프로그래밍 (0) | 2024.10.31 |
소켓 프로그래밍 (0) | 2024.10.29 |
소켓 프로그래밍 기초 (0) | 2024.10.29 |