포스트

CS — ipc followup

IPC 모의면접 직후 나온 후속 질문 — 소켓이란 무엇인가 — 를 결론 → 흐름 → 표 → 코드 → 면접 답변 순으로 정리한 노트.

CS — ipc followup

IPC 모의면접 꼬리질문 정리

IPC 모의면접 직후 나온 후속 질문 1개를 깊이 있게 정리한 노트. 본문은 22번 원본의 해당 섹션을 가리키고, 여기서는 결론 → 흐름 → 표 → 코드/예 → 면접 답변 한 줄 순으로 압축한다.


목차

  1. 소켓이란 무엇일까

1. 소켓이란 무엇일까

한 줄 결론 — 소켓(socket)은 네트워크 또는 같은 머신 안에서 두 endpoint를 잇는 양방향 통신 채널의 추상화이고, OS가 파일 디스크립터(file descriptor) 로 노출하는 IPC/네트워크 통합 인터페이스다. 22번 IPC §10의 카탈로그를 한 항목씩 더 풀어보면, 소켓은 같은 머신 IPC(UNIX domain·loopback)부터 글로벌 네트워크 통신(TCP/UDP)까지 같은 API(BSD socket)로 다루는 가장 범용적인 메커니즘이다.

1-1. 정의 — endpoint + fd 추상화

1
2
3
4
프로세스 A ←──[양방향 채널]──→ 프로세스 B
   ↑                                ↑
 소켓 1                           소켓 2
 (fd: 4)                          (fd: 5)

소켓의 두 가지 본질:

측면의미
endpoint (종점)통신의 한쪽 끝. 각 통신마다 보내는 소켓 + 받는 소켓 = 2개
file descriptor 추상화OS는 소켓을 정수 fd로 노출 — read/write/close가 그대로 동작 (POSIX의 “everything is a file” 철학)

SOCKET 타입은 Windows에선 32/64비트 정수 핸들, POSIX에선 int fd. 그래서 표준 입출력(stdin/stdout)·파일·파이프·소켓이 모두 같은 API로 다뤄진다.

1
2
3
4
5
6
7
// POSIX — 파일과 소켓 모두 같은 read/write
int fd = open("file.txt", O_RDONLY);
read(fd, buf, sizeof(buf));

int sock = socket(AF_INET, SOCK_STREAM, 0);
// ... connect 후
read(sock, buf, sizeof(buf));    // 같은 read()!

통신을 파일 I/O와 똑같이 다루게 만든 것 — 이게 BSD socket API 설계의 핵심이다.

1-2. 흐름 한눈에 — BSD socket API

1
2
3
4
5
6
7
8
9
10
11
12
서버 측                          클라이언트 측
────                             ────
socket()    → fd 얻기            socket()
bind()      → 주소 부여
listen()    → 들어오는 연결 대기
                                 connect()  → 연결 시도
accept()    → 새 연결 수락        ─────────────
            (새 소켓 fd 반환)
   ↓                                 ↓
send/recv ←──────양방향─────→ send/recv
   ↓                                 ↓
close()                           close()

각 단계 의미:

함수의미
socket(family, type, proto)새 소켓 fd 생성. 가족·타입·프로토콜 지정
bind(fd, addr, addrlen)소켓에 주소(IP+포트 또는 경로) 부여. 서버 필수
listen(fd, backlog)TCP 서버 — 연결 대기 상태로. backlog는 미수락 큐 깊이
accept(fd, ...)큐에서 연결 하나 꺼내 새 fd 반환. 차단형
connect(fd, addr, addrlen)클라이언트 — 서버에 연결 시도
send/recv 또는 write/read데이터 송수신
close(fd)소켓 닫음. TCP는 4-way handshake

1-3. 종류 — Stream vs Datagram

1
2
3
4
5
6
7
8
9
10
11
SOCK_STREAM (TCP):
   바이트 스트림 / 연결 지향 / 신뢰성 / 순서 보장
   → 같은 채널로 4바이트 + 8바이트 쓰면 12바이트로 한 번에 받힐 수 있음 (메시지 경계 없음)

SOCK_DGRAM (UDP):
   메시지 단위 / 비연결 / 비신뢰 / 순서 보장 없음
   → 4바이트 패킷 + 8바이트 패킷 → 4바이트, 8바이트로 따로 받음 (경계 보존)

SOCK_RAW:
   IP 헤더부터 직접 — 보통 관리자 권한 필요
   → ping, traceroute, 패킷 캡처
타입연결신뢰성순서메시지 경계비용사용처
SOCK_STREAM (TCP)OO (재전송)OX핸드셰이크 + ACKHTTP·SSH·DB
SOCK_DGRAM (UDP)XXXO단순 송수신DNS·게임·스트리밍
SOCK_SEQPACKETOOOO중간특수 (잘 안 씀)
SOCK_RAW-----패킷 조작·진단

1-4. 주소 패밀리 (family)

1
2
3
4
5
AF_INET    — IPv4 (32비트 IP + 16비트 port)
AF_INET6   — IPv6 (128비트 IP + 16비트 port)
AF_UNIX    — UNIX domain (파일 시스템 경로, 같은 머신)
AF_BLUETOOTH — Bluetooth
AF_PACKET  — 링크 계층 (Linux, 패킷 캡처)
1
2
3
4
5
6
7
8
9
10
11
12
// IPv4 TCP
struct sockaddr_in addr = {
    .sin_family = AF_INET,
    .sin_port = htons(8080),
    .sin_addr.s_addr = inet_addr("127.0.0.1")
};

// UNIX domain
struct sockaddr_un addr = {
    .sun_family = AF_UNIX
};
strcpy(addr.sun_path, "/tmp/mysock");
패밀리주소범위
AF_INETIPv4 + port같은 머신 (loopback) + 네트워크
AF_INET6IPv6 + port같은 머신 + 네트워크
AF_UNIX파일 경로같은 머신만 (가장 빠름)

1-5. 5-tuple — 연결의 식별자

TCP 연결 하나는 5-tuple로 유일하게 식별된다:

1
2
3
(protocol, src_ip, src_port, dst_ip, dst_port)

예: (TCP, 192.168.1.10:54321, 93.184.216.34:80)

같은 서버 IP·포트에 여러 클라이언트가 동시 접속해도, 클라이언트 측 포트(ephemeral port, OS가 자동 할당)가 다르므로 5-tuple이 모두 다르다 → 커널이 각 연결을 분리 관리 가능.

1
2
3
4
서버 192.168.1.10:80
  ├─ (TCP, 1.2.3.4:54321, 192.168.1.10:80)   ← 연결 1
  ├─ (TCP, 1.2.3.4:54322, 192.168.1.10:80)   ← 같은 IP의 다른 ephemeral port
  └─ (TCP, 5.6.7.8:39101, 192.168.1.10:80)   ← 다른 클라이언트

이게 한 서버가 수십만 동시 연결을 받을 수 있는 원리.

1-6. UNIX domain socket — 같은 머신 IPC

22번 §10.2의 핵심 재정리. 파일 시스템 경로로 식별되고, TCP loopback보다 30~50% 빠르다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 서버
int sfd = socket(AF_UNIX, SOCK_STREAM, 0);
struct sockaddr_un addr = { .sun_family = AF_UNIX };
strcpy(addr.sun_path, "/tmp/mysock");
unlink("/tmp/mysock");   // 이전 잔존 파일 제거
bind(sfd, (struct sockaddr*)&addr, sizeof(addr));
listen(sfd, 5);
int cfd = accept(sfd, NULL, NULL);

// 클라이언트
int sfd = socket(AF_UNIX, SOCK_STREAM, 0);
struct sockaddr_un addr = { .sun_family = AF_UNIX };
strcpy(addr.sun_path, "/tmp/mysock");
connect(sfd, (struct sockaddr*)&addr, sizeof(addr));
write(sfd, "hello", 5);

특징:

  • 파일 시스템 경로 식별/tmp/mysock, /var/run/docker.sock
  • 권한 관리chmod/chown 그대로 적용. ACL이 OS 제공
  • TCP보다 빠름 — 프로토콜 스택(헤더·체크섬·시퀀스) 우회
  • fd 전송 가능SCM_RIGHTS로 파일 디스크립터 자체를 다른 프로세스에 전달 (고급)
  • abstract socket (Linux)\0로 시작하는 이름으로 파일 시스템 노드 없이 사용

대표 사용처:

  • Docker daemon (/var/run/docker.sock)
  • X11/Wayland 디스플레이 서버
  • systemd activation·journald
  • DBus 데스크톱 IPC

1-7. loopback (127.0.0.1) vs UNIX domain

같은 머신 안에서 TCP를 쓸 때 127.0.0.1(IPv4 loopback) 또는 ::1(IPv6 loopback)을 쓴다. 커널이 NIC를 우회해 직접 처리.

항목loopback (127.0.0.1 + TCP)UNIX domain (AF_UNIX)
주소127.0.0.1:port/tmp/mysock (경로)
프로토콜 스택TCP/IP 통과 (헤더·체크섬)우회 (커널 직접 큐)
속도UNIX domain의 70%100% (기준)
버퍼링큼 (TCP 윈도우)작음 (커널 내 버퍼)
fd 전송XO (SCM_RIGHTS)
접근 제어방화벽 규칙파일 시스템 권한
네트워크 확장같은 코드로 외부 가능같은 머신만
포트 충돌65535개 한정무관 (경로)

결론 — 같은 머신 안에서 IPC만 필요하면 UNIX domain이 빠르고 권한 관리 쉬움. 네트워크 확장 가능성이 있으면 처음부터 TCP. Docker가 UNIX domain socket을 쓰는 이유 — 네트워크 노출 위험 없이 파일 권한으로 접근 제어.

1-8. Windows Winsock — BSD socket의 Windows 변형

1
2
3
4
5
6
7
8
9
10
11
12
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment(lib, "ws2_32.lib")

WSADATA wsa;
WSAStartup(MAKEWORD(2, 2), &wsa);   // ← Windows만 필수

SOCKET s = socket(AF_INET, SOCK_STREAM, 0);
// ... bind/connect/send/recv 동일
closesocket(s);                       // ← close()가 아니라 closesocket()

WSACleanup();

POSIX와의 차이:

항목POSIXWindows Winsock
초기화없음WSAStartup 필수
종료없음WSACleanup
fd 타입intSOCKET (정수 핸들)
닫기close(fd)closesocket(s)
에러errnoWSAGetLastError()
non-blockingfcntl(F_SETFL, O_NONBLOCK)ioctlsocket(FIONBIO)
고성능 비동기epoll / kqueueIOCP (I/O Completion Port)
AF_UNIX표준Windows 10 1803+ 지원 (이전엔 named pipe로 대체)

Winsock의 IOCP는 고성능 서버의 사실상 표준 — WSARecv/WSASend + completion port로 수십만 동시 연결 처리. Linux의 epoll·BSD의 kqueue와 같은 역할.

1-9. IPC 관점에서 본 22번과의 위치

22번 §3 분류 표를 다시 적용:

메커니즘데이터/신호커널 경유네트워크
UNIX domain socket데이터매번같은 머신
TCP socket데이터매번O
UDP socket데이터매번O

소켓은 IPC 카탈로그 중 “매번 커널 경유 + 네트워크 가능” 자리에 위치한다. 공유 메모리(가장 빠름)와 RPC(가장 느림) 사이의 중간 비용·중간 추상화·최고 범용성.

22번 §12 비용 표 재인용:

1
2
3
UNIX domain socket send/recv   수 μs
TCP loopback send/recv         5~30 μs
TCP 네트워크                    수 ms (지연 + 대역폭)

같은 머신 안에선 파이프와 비슷한 비용, 네트워크로 가면 한 자릿수 ms로 증가. 그래서 “같은 머신 + 네트워크 가능성 모두 가진다” 가 소켓의 진짜 가치.

1-10. 흔한 패턴 코드

1-10-1. TCP echo server (POSIX)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <string.h>

int main() {
    int sfd = socket(AF_INET, SOCK_STREAM, 0);
    struct sockaddr_in addr = {
        .sin_family = AF_INET,
        .sin_port = htons(8080),
        .sin_addr.s_addr = INADDR_ANY
    };

    int opt = 1;
    setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    bind(sfd, (struct sockaddr*)&addr, sizeof(addr));
    listen(sfd, 5);

    while (1) {
        int cfd = accept(sfd, NULL, NULL);
        char buf[1024];
        ssize_t n = read(cfd, buf, sizeof(buf));
        if (n > 0) write(cfd, buf, n);   // echo
        close(cfd);
    }
    close(sfd);
}

1-10-2. UDP receiver

1
2
3
4
5
6
7
8
9
10
11
12
13
int sfd = socket(AF_INET, SOCK_DGRAM, 0);   // ← SOCK_DGRAM
struct sockaddr_in addr = {
    .sin_family = AF_INET,
    .sin_port = htons(9000),
    .sin_addr.s_addr = INADDR_ANY
};
bind(sfd, (struct sockaddr*)&addr, sizeof(addr));

char buf[1024];
struct sockaddr_in from;
socklen_t fromlen = sizeof(from);
recvfrom(sfd, buf, sizeof(buf), 0, (struct sockaddr*)&from, &fromlen);
// UDP는 connect/accept 없이 recvfrom으로 누가 보냈는지 받음

1-11. 면접 답변 한 줄

“소켓은 네트워크 또는 같은 머신 안에서 두 endpoint를 잇는 양방향 통신 채널의 추상화입니다. OS가 파일 디스크립터로 노출해 파일 I/O와 같은 API(read/write)로 다룰 수 있게 합니다. 종류는 stream(TCP)·datagram(UDP)·raw 등이 있고, 주소 패밀리로 IPv4(AF_INET)·IPv6·UNIX domain(AF_UNIX)을 선택합니다. 같은 머신 IPC만 필요하면 UNIX domain이 TCP loopback보다 30~50% 빠르고 파일 권한으로 접근 제어할 수 있어 Docker 데몬·X11·systemd 같은 시스템 서비스에서 표준입니다. 22번에서 본 IPC 카탈로그 중 ‘매번 커널 경유 + 네트워크 가능’ 자리에 있고, 공유 메모리(수 ns)와 RPC(수십~수백 μs) 사이의 중간 비용·최고 범용성이 소켓의 자리입니다.”


회귀 다리

  • IPC — 본 문서의 원본 §10(소켓), §3(분류축), §12(비용 스펙트럼)이 답변 근거.
  • 컨텍스트 스위칭 — 매 송수신마다 발생하는 모드 스위치 비용이 소켓 비용의 출발점.
  • 프로세스 vs 스레드 — 주소 공간 격리가 IPC가 필요한 이유의 토대.
  • 포인터·레퍼런스 — fd는 결국 커널 자료구조에 대한 핸들(추상 포인터)이라는 점에서 같은 추상화 패턴.

핵심 요약 — 소켓은 OS가 파일 디스크립터로 노출하는 양방향 통신 채널의 추상화라서 파일과 같은 read/write API로 다룰 수 있고, 같은 머신 IPC만 필요하면 UNIX domain socket이 TCP loopback보다 30~50% 빠르며 파일 권한으로 접근을 제어할 수 있다.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.