[네트워크] UDP 소켓 프로그래밍 (C++)

kimyb·2022년 11월 27일

소켓에 대해 공부를 하게되면 항상 TCP만 사용하다 보니,
UDP를 한번씩 깜빠깜빡해 생각난 김에 정리해 보자.

UDP(User Datagram Protocol)란?

P로 끝난걸로 프로토콜임을 짐작할 수 있다.
전송 계층 프로토콜 중 하나로, IP 스택의 일부로 사용되며, 데이터를 빠르게 보내고 받는 데 사용.

  1. 비연결 지향 (Connectionless):

UDP는 연결 설정 과정을 거치지 않고 데이터를 전송하여, 이는 TCP와 대조적으로 연결 지향적인 프로토콜이 아님을 알 수 있다. 따라서 연결 설정 및 해제와 관련된 오버헤드가 없으며, 데이터 패킷을 빠르게 보내고 받을 수 있다.

  1. 데이터그램(데이터 패킷):

UDP에서 데이터는 데이터그램으로 나뉘어 전송됩니다. 각 데이터그램은 독립적으로 처리되며, 순서가 보장되지 않아, 이러한 특성으로 인해 데이터가 일부 손실될 수 있으며, 순서가 바뀔 수 있다.

  1. 신뢰성 부족:

UDP는 오류 검출 기능은 제공하지만, 오류 복구를 지원하지 않는다. 따라서 데이터 전송의 신뢰성이 보장되지 않음. 이런 특성은 실시간 응용 프로그램에 유용할 수 있다.

  1. 소스 포트 및 대상 포트:

UDP 패킷은 송신자와 수신자를 식별하기 위한 포트 번호를 사용하며, 각 UDP 패킷에는 송신자 포트와 수신자 포트가 포함되어 있다. 이를 사용하여 데이터를 올바른 프로세스로 라우팅 할 수 있다.

  1. 빠른 전송:

연결 설정 및 유지에 소요되는 오버헤드가 없기 때문에, 실시간 응용 프로그램에서 사용되며, 지연 시간이 중요한 상황에서 유용.



UDP 소켓 사용

C++을 사용하여 단순한 UDP 클라이언트와 서버를 만들어보자.

클라 : [메시지] -> 서버
서버 : [메시지] 출력.

UDP 서버 코드:

#include <iostream>
#include <Winsock2.h>

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

int main() {
    WSADATA wsaData;
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
        std::cerr << "Failed to initialize winsock" << std::endl;
        return 1;
    }

    // 소켓 생성
    SOCKET serverSocket = socket(AF_INET, SOCK_DGRAM, 0);
    if (serverSocket == INVALID_SOCKET) {
        std::cerr << "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 = INADDR_ANY;

    // 소켓 바인딩
    if (bind(serverSocket, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) {
        std::cerr << "Failed to bind socket" << std::endl;
        closesocket(serverSocket);
        WSACleanup();
        return 1;
    }

    std::cout << "UDP server is running..." << std::endl;

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

        // 메시지 수신
        int bytesReceived = recvfrom(serverSocket, buffer, sizeof(buffer), 0, (struct sockaddr*)&clientAddr, &clientAddrSize);
        if (bytesReceived == SOCKET_ERROR) {
            std::cerr << "Error in recvfrom" << std::endl;
            continue;
        }

        buffer[bytesReceived] = '\0';
        std::cout << "Received message from " << inet_ntoa(clientAddr.sin_addr) << ": " << buffer << std::endl;
    }

    closesocket(serverSocket);
    WSACleanup();

    return 0;
}

UDP 클라이언트 코드:

#include <iostream>
#include <Winsock2.h>
#include <string>

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

int main() {
    WSADATA wsaData;
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
        std::cerr << "Failed to initialize winsock" << std::endl;
        return 1;
    }

    // 소켓 생성
    SOCKET clientSocket = socket(AF_INET, SOCK_DGRAM, 0);
    if (clientSocket == INVALID_SOCKET) {
        std::cerr << "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("서버의_IP_주소"); // 서버의 IP 주소로 변경

    while (true) {
        std::string message;
        std::cout << "Enter a message (or 'exit' to quit): ";
        std::getline(std::cin, message);

        if (message == "exit") {
            break;
        }

        // 메시지 전송
        sendto(clientSocket, message.c_str(), message.length(), 0, (struct sockaddr*)&serverAddr, sizeof(serverAddr));
    }

    closesocket(clientSocket);
    WSACleanup();

    return 0;
}

UDP 통신은 연결 지향이 아니므로 오류 처리와 예외 상황을 고려해야 한다ㅣ.

profile
공부했던것을 정리.

0개의 댓글