소켓 통신 구조와 TCP·UDP·WebSocket 선택 기준

소켓 통신의 엔드포인트 구조와 TCP, UDP, RAW 소켓, WebSocket, Unix Domain 소켓의 특성 및 운영 고려사항을 정리한다.

2026-08-14 · 최초 발행 2025-08-10

프로세스 사이에 통신 경로를 만드는 소켓

소켓은 네트워크를 통해 데이터를 교환하기 위한 소프트웨어 엔드포인트다. 서버와 클라이언트가 연결을 관리하고 양방향으로 데이터를 주고받을 수 있게 하며, 네트워크 계층의 복잡성을 애플리케이션에서 다룰 수 있는 인터페이스로 감춘다.

통신 상대는 일반적으로 IP 주소와 포트 번호 조합으로 식별된다. 이 조합을 통해 네트워크상의 특정 프로세스에 도달하며, 운영체제는 소켓 인터페이스로 네트워크 하드웨어와 애플리케이션 사이를 연결한다.

전송 요구사항에 따라 달라지는 소켓 유형

순서와 신뢰성이 필요한 TCP Stream 소켓

Stream 소켓은 TCP를 기반으로 연결을 수립한 뒤 데이터를 전송한다. 전송 순서를 유지하고 오류 검출과 복구 메커니즘을 제공하므로, 신뢰성 있는 전달이 필요한 경우에 적합하다. HTTP 웹 브라우징, SMTP 이메일 전송, FTP 파일 전송, SSH 원격 로그인에 사용된다.

ServerClientServerClient연결 수립 완료(TCP 3-way handshake)연결 요청(SYN)연결 확인(SYN+ACK)확인 응답(ACK)데이터 전송데이터 수신 확인연결 종료 요청(FIN)연결 종료 확인

연결 없이 패킷을 보내는 UDP Datagram 소켓

Datagram 소켓은 UDP 기반의 비연결형 통신 방식이다. 연결 설정이나 수신 확인을 전제로 하지 않으며, 낮은 오버헤드로 빠르게 데이터를 전송한다. DNS 조회, 스트리밍 미디어, 온라인 게임, VoIP 통신처럼 전송 지연을 줄이는 일이 중요한 경우에 쓰인다.

ServerClientServerClient연결 설정/해제 없음수신 확인 없음데이터 패킷 1 전송데이터 패킷 2 전송응답 데이터 전송

하위 프로토콜을 다루는 RAW 소켓

RAW 소켓은 IP나 ICMP 같은 하위 레벨 네트워크 프로토콜에 직접 접근할 때 사용한다. 일반적으로 특수 권한이 필요하며, 네트워크 모니터링 도구, 패킷 스니퍼, 네트워크 보안 도구, 커스텀 프로토콜 구현에 활용된다.

웹 연결을 유지하는 WebSocket

WebSocket은 HTTP 기반으로 연결을 시작한 뒤 양방향 실시간 통신을 제공한다. 단일 TCP 연결을 유지하므로 실시간 채팅, 라이브 스코어보드, 협업 도구, 실시간 알림 시스템처럼 웹 환경에서 지속적인 데이터 교환이 필요한 서비스에 맞는다.

ServerClientServerClientWebSocket 연결 수립양방향 실시간 통신HTTP 연결 요청(Upgrade: websocket)101 Switching Protocols데이터 프레임 전송데이터 프레임 전송

같은 시스템 안에서 쓰는 Unix Domain 소켓

Unix Domain 소켓은 동일 시스템의 프로세스 간 통신에 사용하며 파일 시스템 경로로 식별된다. 네트워크 스택을 거치지 않으면서도 표준 소켓 API를 사용할 수 있다. MySQL·PostgreSQL 연결, X Window System, 시스템 서비스 간 통신, 컨테이너 통신이 적용 대상이다.

서버와 클라이언트가 연결을 다루는 흐름

서버는 통신에 맞는 소켓을 생성하고 IP 주소와 포트를 바인딩한 다음, listen 상태에서 연결을 기다린다. 요청이 들어오면 accept로 연결을 수락하고 데이터를 송수신한 뒤 소켓을 닫는다.

소켓 생성주소 바인딩연결 대기 상태클라이언트 연결 수락데이터 송수신연결 종료

클라이언트는 서버와 같은 유형의 소켓을 만들고, 서버 IP 주소와 포트에 connect를 시도한다. 연결된 후 데이터를 교환하고 통신이 끝나면 소켓을 닫는다.

소켓 생성서버 연결 요청데이터 송수신연결 종료

TCP 서버와 클라이언트 구현

다음 코드는 TCP 서버가 연결을 수락해 클라이언트 메시지를 읽고 응답하는 흐름, 그리고 클라이언트가 서버에 접속해 메시지를 주고받는 흐름을 보여준다.

서버 코드:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>

#define PORT 8080

int main() {
    int server_fd, new_socket;
    struct sockaddr_in address;
    int addrlen = sizeof(address);
    char buffer[1024] = {0};
    char *hello = "Hello from server";

    // 소켓 생성
    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
        perror("socket failed");
        exit(EXIT_FAILURE);
    }

    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(PORT);

    // 주소 바인딩
    if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
        perror("bind failed");
        exit(EXIT_FAILURE);
    }

    // 연결 대기
    if (listen(server_fd, 3) < 0) {
        perror("listen");
        exit(EXIT_FAILURE);
    }

    // 연결 수락
    if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {
        perror("accept");
        exit(EXIT_FAILURE);
    }

    // 데이터 수신
    read(new_socket, buffer, 1024);
    printf("Message from client: %s\n", buffer);

    // 데이터 전송
    send(new_socket, hello, strlen(hello), 0);
    printf("Hello message sent\n");

    // 소켓 닫기
    close(new_socket);
    close(server_fd);
    return 0;
}

클라이언트 코드:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/socket.h>

#define PORT 8080

int main() {
    int sock = 0;
    struct sockaddr_in serv_addr;
    char *hello = "Hello from client";
    char buffer[1024] = {0};

    // 소켓 생성
    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        printf("\n Socket creation error \n");
        return -1;
    }

    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(PORT);

    // IP 주소 변환
    if(inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0) {
        printf("\nInvalid address/ Address not supported \n");
        return -1;
    }

    // 서버 연결
    if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
        printf("\nConnection Failed \n");
        return -1;
    }

    // 데이터 전송
    send(sock, hello, strlen(hello), 0);
    printf("Hello message sent\n");

    // 데이터 수신
    read(sock, buffer, 1024);
    printf("Message from server: %s\n", buffer);

    // 소켓 닫기
    close(sock);
    return 0;
}

운영에서 함께 설계할 조건

블로킹 I/O는 구현이 단순하지만 I/O 작업 동안 스레드가 차단된다. 논블로킹 I/O는 호출이 즉시 반환되므로 다른 작업을 수행할 수 있다. 다수 연결을 단일 스레드에서 관리해야 할 때는 select(), poll(), epoll() 같은 I/O 멀티플렉싱 방식을 활용할 수 있다.

연결 수립 시에는 클라이언트 인증과 권한 부여를 검토해야 한다. TLS/SSL로 통신 내용을 보호하고, 입력 검증으로 악의적인 데이터 주입을 막으며, 연결 수 제한과 타임아웃 설정으로 DoS에 대응한다.

예기치 않은 연결 종료도 통신 설계의 일부다. 연결 끊김 처리, 서버 다운 시 재연결 메커니즘, 무한 대기를 막는 타임아웃, 버퍼 오버플로우를 피하기 위한 적절한 버퍼 크기 관리가 필요하다.

소켓 기반 통신의 확장

WebRTC는 P2P 실시간 통신을 위한 웹 표준 기술이고, gRPC는 고성능 RPC(Remote Procedure Call) 프레임워크다. QUIC 프로토콜은 UDP 기반의 신뢰성 있는 전송 프로토콜이며, Serverless WebSockets는 클라우드 환경에서 확장 가능한 소켓 통신을 다룬다.

소켓은 기본적인 클라이언트-서버 모델부터 복잡한 분산 시스템까지 네트워크 애플리케이션의 통신 기반으로 사용된다. 애플리케이션이 요구하는 전달 신뢰성, 연결 방식, 실행 환경을 기준으로 소켓 유형을 선택하고 성능·보안·장애 회복 조건을 함께 반영해야 한다.

소켓 통신네트워크 프로그래밍TCPUDPWebSocket