Network / / 2024. 9. 26. 16:20

UDP 소켓 프로그래밍

UDP

UDP (User Datagram Protocol)는 인터넷 프로토콜 스위트의 일부로, 메시지를 데이터그램으로 교환하는 데 사용되는 간단한 전송 계층 프로토콜입니다. TCP (Transmission Control Protocol)와 비교하여, UDP는 비연결 지향적이고 신뢰성이 낮은 통신 방식을 제공합니다. 이러한 특징 때문에 UDP는 다음과 같은 주요 특성을 가집니다: 

1. 비연결성: UDP는 연결을 설정하거나 유지하지 않습니다. 각 데이터그램은 독립적으로 전송되며, 서로에게 영향을 주지 않습니다.
2. 신속성: 연결 설정에 필요한 핸드셰이크가 없기 때문에, 데이터 전송이 더 빠릅니다.
3. 경량 프로토콜: UDP 헤더는 TCP 헤더보다 훨씬 간단하며, 처리 과정이 간단합니다.
4. 비신뢰성: UDP는 데이터의 도착을 보장하지 않으며, 순서대로 도착하거나 중복되지 않는 것도 보장하지 않습니다.
5. 오류 검사: UDP는 기본적인 오류 검사 기능을 제공하지만, 오류 발생 시 재전송을 하지 않습니다. 

UDP는 실시간 통신, 스트리밍, 멀티캐스팅 등의 응용 프로그램에 적합합니다. 이러한 응용 프로그램은 데이터의 신속한 전송을 요구하며, 일부 패킷 손실이 허용될 수 있습니다.

 

UDP Server Socket Programming

다음은 윈도우즈 OS에서 UDP 기반 소켓 프로그래밍-서버측 코드입니다.

#include <iostream>
#include <winsock2.h>

#pragma comment(lib, "ws2_32.lib")

int main() {
    // Winsock 초기화
    WSADATA wsaData;
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
        std::cout << "Failed to initialize winsock." << std::endl;
        return 1;
    }

    // UDP 소켓 생성
    SOCKET serverSocket = socket(AF_INET, SOCK_DGRAM, 0);
    if (serverSocket == INVALID_SOCKET) {
        std::cout << "Failed to create socket." << std::endl;
        WSACleanup();
        return 1;
    }

    // 서버 정보 설정
    sockaddr_in serverAddr;
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_port = htons(12345);  // 서버 포트 번호
    serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);  // 모든 인터페이스에서 수신 대기

    // 소켓과 서버 정보를 바인딩
    if (bind(serverSocket, (sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) {
        std::cout << "Failed to bind socket." << std::endl;
        closesocket(serverSocket);
        WSACleanup();
        return 1;
    }

    // 데이터 수신
    char buffer[1024];
    sockaddr_in clientAddr;
    int clientAddrSize = sizeof(clientAddr);
    int recvBytes = recvfrom(serverSocket, buffer, sizeof(buffer), 0, (sockaddr*)&clientAddr, &clientAddrSize);
    if (recvBytes == SOCKET_ERROR) {
        std::cout << "Failed to receive data." << std::endl;
    }
    else {
        buffer[recvBytes] = '\0';
        std::cout << "Received data from client: " << buffer << std::endl;
    }

    // Winsock 정리
    closesocket(serverSocket);
    WSACleanup();

    return 0;
}

 

UDP 소켓 생성

    // UDP 소켓 생성
    SOCKET serverSocket = socket(AF_INET, SOCK_DGRAM, 0);
    if (serverSocket == INVALID_SOCKET) {
        std::cout << "Failed to create socket." << std::endl;
        WSACleanup();
        return 1;
    }

위 코드는 UDP 소켓을 생성하는 과정을 보여줍니다. 

 

UDP 소켓 생성:

SOCKET serverSocket = socket(AF_INET, SOCK_DGRAM, 0);
if (serverSocket == INVALID_SOCKET) {
    std::cout << "Failed to create socket." << std::endl;
    WSACleanup();
    return 1;
}

 

  • SOCKET serverSocket = socket(AF_INET, SOCK_DGRAM, 0): 이 줄은 UDP 소켓을 생성합니다.

       1. AF_INET`: 주소 체계를 지정하는데, 이 경우 IPv4 인터넷 프로토콜이 사용됩니다.

       2. SOCK_DGRAM: 데이터그램 기반의 소켓 유형을 지정합니다, UDP 통신에 적합합니다.

       3. 0: 프로토콜을 지정하는데, 여기서 0은 기본 프로토콜인 UDP를 의미합니다.

  • 에러 처리: INVALID_SOCKET은 소켓 생성이 실패했을 때 반환되는 값입니다. 소켓 생성에 실패하면 에러 메시지를 출력하고, WSACleanup() 함수를 호출하여 Winsock 리소스를 정리한 후 프로그램을 종료합니다.

데이터 수신

    // 데이터 수신
    char buffer[1024];
    sockaddr_in clientAddr;
    int clientAddrSize = sizeof(clientAddr);
    int recvBytes = recvfrom(serverSocket, buffer, sizeof(buffer), 0, (sockaddr*)&clientAddr, &clientAddrSize);
    if (recvBytes == SOCKET_ERROR) {
        std::cout << "Failed to receive data." << std::endl;
    }
    else {
        buffer[recvBytes] = '\0';
        std::cout << "Received data from client: " << buffer << std::endl;
    }

 

위 코드는 UDP 소켓을 통해 데이터를 수신하는 과정을 나타냅니다. 주요 부분을 하나씩 살펴보겠습니다: 

1. 데이터 수신을 위한 준비:

    char buffer[1024];
    sockaddr_in clientAddr;
    int clientAddrSize = sizeof(clientAddr);

 

  • char buffer[1024]: 이 배열은 수신된 데이터를 저장하기 위한 버퍼입니다. 여기서는 1024바이트 크기로 설정되어 있습니다.
  • sockaddr_in clientAddr: 이 구조체는 클라이언트의 주소 정보를 저장합니다. UDP는 비연결 프로토콜이므로, 각 데이터그램이 어디서 왔는지 알기 위해 사용됩니다.
  • int clientAddrSize = sizeof(clientAddr): 이 변수는 clientAddr 구조체의 크기를 저장합니다. recvfrom 함수에서 이 값을 사용합니다.

2. 데이터 수신:

int recvBytes = recvfrom(serverSocket, buffer, sizeof(buffer), 0, (sockaddr*)&clientAddr, &clientAddrSize);

 

  • recvfrom 함수는 UDP 소켓을 통해 데이터를 수신하는 데 사용됩니다. 여기서 중요한 파라미터들은 다음과 같습니다:

       1. serverSocket: 데이터를 수신할 소켓입니다.

       2. buffer: 수신된 데이터를 저장할 버퍼입니다.

       3. sizeof(buffer): 버퍼의 크기입니다.

       4. (sockaddr*)&clientAddr: 송신자의 주소 정보를 저장할 구조체의 포인터입니다.

       5. &clientAddrSize: 송신자의 주소 정보 구조체의 크기를 저장하는 변수의 포인터입니다.

  • recvBytes: 수신된 바이트 수를 저장합니다. 만약 소켓 오류가 발생하면 SOCKET_ERROR를 반환합니다.

3. 수신 결과 처리:

    if (recvBytes == SOCKET_ERROR) {
        std::cout << "Failed to receive data." << std::endl;
    }
    else {
        buffer[recvBytes] = '\0';
        std::cout << "Received data from client: " << buffer << std::endl;
    }
  • 오류 확인: recvBytes SOCKET_ERROR와 같으면 수신 중 오류가 발생한 것입니다. 이 경우 콘솔에 오류 메시지를 출력합니다.
  •  성공적인 수신: 오류가 없으면 수신된 데이터를 처리합니다. buffer[recvBytes] = '\0'는 문자열의 끝을 나타내기 위해 널 종료 문자(\0)를 추가합니다. 이후 수신된 데이터를 콘솔에 출력합니다.

이 코드는 UDP 네트워크 프로그래밍에서 데이터를 수신하는 기본적인 방법을 보여줍니다. 오류 처리, 데이터 수신, 그리고 수신된 데이터의 처리 과정이 포함되어 있습니다.

 

UDP Client Programming

#include <iostream>
#include <winsock2.h>
#include <ws2tcpip.h>

#pragma comment(lib, "ws2_32.lib")

int main() {
    // Winsock 초기화
    WSADATA wsaData;
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
        std::cout << "Failed to initialize winsock." << std::endl;
        return 1;
    }

    // UDP 소켓 생성
    SOCKET clientSocket = socket(AF_INET, SOCK_DGRAM, 0);
    if (clientSocket == INVALID_SOCKET) {
        std::cout << "Failed to create socket." << std::endl;
        WSACleanup();
        return 1;
    }

    // 서버 정보 설정
    sockaddr_in serverAddr;
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_port = htons(12345);  // 서버 포트 번호
    //serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");  // 서버 IP 주소

    if (inet_pton(AF_INET, "127.0.0.1", &serverAddr.sin_addr) <= 0) {
        printf("Invalid address/ Address not supported.\n");
        return -1;
    }

    // 데이터 전송
    std::string message = "Hello, server!";
    if (sendto(clientSocket, message.c_str(), (int)message.size(), 0, (sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) {
        std::cout << "Failed to send data." << std::endl;
    }

    // Winsock 정리
    closesocket(clientSocket);
    WSACleanup();

    return 0;
}

 

데이터 전송

    // 데이터 전송
    std::string message = "Hello, server!";
    if (sendto(clientSocket, message.c_str(), (int)message.size(), 0, (sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) {
        std::cout << "Failed to send data." << std::endl;
    }

이 코드는 UDP 소켓을 통해 데이터를 전송하는 과정을 나타냅니다. 주요 부분을 하나씩 살펴보겠습니다: 

1. 데이터 전송을 위한 준비:

std::string message = "Hello, server!";
  • std::string message: 전송할 메시지를 저장하는 문자열입니다. 이 경우 "Hello, server!"라는 메시지가 저장되어 있습니다.

2. 데이터 전송:

    if (sendto(clientSocket, message.c_str(), (int)message.size(), 0, (sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) {
        std::cout << "Failed to send data." << std::endl;
    }
  • sendto 함수는 UDP 소켓을 통해 데이터를 전송하는 데 사용됩니다. 여기서 중요한 파라미터들은 다음과 같습니다:
    • clientSocket: 데이터를 전송할 소켓입니다.
    • message.c_str(): 전송할 데이터, 즉 메시지의 C 스타일 문자열입니다.
    • (int)message.size(): 전송할 데이터의 크기입니다.
    • (sockaddr*)&serverAddr: 수신자, 즉 서버의 주소 정보를 저장하는 구조체의 포인터입니다.
    • sizeof(serverAddr): 서버의 주소 정보 구조체의 크기입니다.
  • 이 함수는 성공적으로 데이터를 전송하면 전송된 바이트 수를 반환하고, 실패하면 SOCKET_ERROR를 반환합니다.

3. 전송 결과 처리:

  • 오류 확인: sendto 함수의 결과가 SOCKET_ERROR와 같으면 데이터 전송 중 오류가 발생한 것입니다. 이 경우 콘솔에 오류 메시지를 출력합니다.

이 코드는 UDP 네트워크 프로그래밍에서 클라이언트가 서버로 데이터를 전송하는 기본적인 방법을 보여줍니다. 데이터 전송 준비, sendto 함수를 사용한 데이터 전송, 그리고 전송 결과의 오류 처리가 포함되어 있습니다.

'Network' 카테고리의 다른 글

CORS, WebSocket, 그리고 Base64  (6) 2024.09.27
Http 프로토콜  (5) 2024.09.27
TCP 소켓 프로그래밍  (0) 2024.09.26
네트워크 프로토콜  (4) 2024.09.26
네트워크 필수 개념: DHCP, ARP, NAT, 그리고 ZeroConf  (8) 2024.09.25
  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유