|
| 1 | +# 소켓과 네트워크 데이터 단위 정리 |
| 2 | + |
| 3 | +## 1. 소켓(Socket) 기본 개념 |
| 4 | +- **정의**: 네트워크로 통신하는 두 프로세스가 데이터를 주고받기 위해 사용하는 통신의 끝점(endpoint) 추상화. IP 주소와 포트 번호, 전송 계층 프로토콜(TCP/UDP 등) 조합으로 식별된다. |
| 5 | +- **역할**: 애플리케이션이 전송 계층 기능(연결 제어, 오류 복구 등)을 직접 구현하지 않고도 네트워크 I/O를 수행하게 해주는 운영체제 커널의 API. |
| 6 | +- **종류** |
| 7 | + - **스트림 소켓(Stream Socket)**: TCP 기반, 연결 지향(3-way handshake), 순서/신뢰성/흐름 제어 제공. |
| 8 | + - **데이터그램 소켓(Datagram Socket)**: UDP 기반, 비연결 지향, 전송 보장을 하지 않지만 지연이 짧고 오버헤드가 작다. |
| 9 | + - **로우 소켓(Raw Socket)**: IP 헤더를 포함한 패킷을 직접 구성·분석할 때 사용, 주로 네트워크 진단/보안 도구. |
| 10 | +- **소켓 식별자**: 프로세스는 `socket()` 호출로 파일 디스크립터를 받고, 커널은 `(local IP, local port, remote IP, remote port, protocol)` 튜플로 소켓을 추적한다. |
| 11 | + |
| 12 | +## 2. 소켓 프로그래밍 흐름과 상태 전이 |
| 13 | +### 서버 측 |
| 14 | +1. `socket()` – 소켓 생성. 주소 패밀리(AF_INET 등), 타입(SOCK_STREAM 등), 프로토콜 지정. |
| 15 | +2. `bind()` – 로컬 IP/포트를 소켓에 묶는다. |
| 16 | +3. `listen()` – 대기열(backlog)을 설정하고 연결 요청을 받을 준비를 한다. |
| 17 | +4. `accept()` – 대기열에서 연결을 하나 꺼내 새로운 커넥션 소켓을 반환한다. |
| 18 | +5. `read()/write()` 또는 `recv()/send()` – 양쪽으로 데이터 송수신. |
| 19 | +6. `close()` – 소켓 종료. TCP는 FIN/ACK 교환으로 연결 해제. |
| 20 | + |
| 21 | +### 클라이언트 측 |
| 22 | +1. `socket()` – 소켓 생성. |
| 23 | +2. `connect()` – 서버 IP:Port로 연결 요청(SYN) 전송. TCP는 3-way handshake 완료 시 사용 가능 상태. |
| 24 | +3. `read()/write()` – 연결 확립 후 데이터 송수신. |
| 25 | +4. `close()` – 연결 종료. |
| 26 | + |
| 27 | +### TCP 상태 전이 요약 |
| 28 | +| 상태 | 설명 | |
| 29 | +| --- | --- | |
| 30 | +| **LISTEN** | `bind + listen` 후 클라이언트의 SYN을 기다리는 서버 소켓 상태. | |
| 31 | +| **SYN-SENT** | 클라이언트가 `connect()`로 SYN을 보낸 직후, 서버의 SYN+ACK을 기다리는 상태. | |
| 32 | +| **SYN-RECEIVED** | 서버가 SYN을 받고 SYN+ACK을 보낸 뒤, 최종 ACK을 기다리는 상태. | |
| 33 | +| **ESTABLISHED** | 3-way handshake가 완료되어 양쪽이 데이터를 교환하는 정상 상태. | |
| 34 | +| **FIN-WAIT-1 / FIN-WAIT-2** | 능동 종료자가 FIN을 보낸 뒤 ACK을 기다리고, 이후 상대의 FIN을 기다리는 단계. | |
| 35 | +| **CLOSE-WAIT / LAST-ACK** | 수동 종료자가 FIN을 받고 응용이 `close()`를 호출하기 전/후 상태. | |
| 36 | +| **TIME-WAIT** | 능동 종료자가 최종 ACK을 보낸 뒤 2MSL 동안 지연 패킷 소멸을 기다리는 상태. | |
| 37 | +| **CLOSED** | 연결이 존재하지 않는 초기/최종 상태. | |
| 38 | + |
| 39 | +- **4-way 종료 흐름**: FIN(능동 종료자) → ACK(수동 종료자) → FIN(수동 종료자) → ACK(능동 종료자). 이 과정에서 능동 종료자는 FIN-WAIT-1/2 → TIME-WAIT으로, 수동 종료자는 CLOSE-WAIT → LAST-ACK → CLOSED로 이동한다. Netstat/ss 출력에 보이는 상태를 이해하면 누가 연결을 닫았는지, 왜 TIME_WAIT이 많은지 빠르게 파악할 수 있다. |
| 40 | + |
| 41 | +### 블로킹 vs 논블로킹 |
| 42 | +- **블로킹 모드**: `accept`, `read` 호출 시 데이터가 준비될 때까지 호출이 반환되지 않는다. |
| 43 | +- **논블로킹 모드**: `fcntl` 또는 `ioctl`로 설정. 데이터가 없으면 즉시 `EWOULDBLOCK` 반환. `select`, `poll`, `epoll`, `kqueue` 등의 멀티플렉싱 API와 함께 사용하여 성능 향상. |
| 44 | + |
| 45 | +### 소켓 옵션 예시 |
| 46 | +- `SO_REUSEADDR`: 같은 로컬 IP/포트를 가진 소켓이 TIME_WAIT에 있더라도 바인드할 수 있게 한다(단, 이미 ESTABLISHED 상태의 소켓이 존재하면 불가). |
| 47 | +- `SO_REUSEPORT`: 동일한 IP/포트를 여러 프로세스/스레드가 공유해 부하를 분산할 수 있도록 한다(Linux 3.9+, BSD). |
| 48 | +- `SO_KEEPALIVE`: 비활성 연결을 주기적으로 프로브해 끊어진 세션을 감지. |
| 49 | +- `TCP_NODELAY`: Nagle 알고리즘을 끄고 즉시 패킷을 전송해 지연(latency)을 줄인다. |
| 50 | +- `SO_LINGER`: `close()` 호출 시 커널이 남은 데이터를 보내고 기다릴지(정상 종료) 즉시 RST를 보낼지 제어. |
| 51 | + |
| 52 | +## 3. Socket.io와 WebSocket 비교 |
| 53 | +| 구분 | WebSocket | Socket.io | |
| 54 | +| --- | --- | --- | |
| 55 | +| **정체성** | RFC 6455에서 정의된 표준 전이중 통신 프로토콜 | WebSocket을 포함한 여러 전송 방식을 추상화한 라이브러리(클라이언트+서버) | |
| 56 | +| **핸드셰이크** | HTTP(S) 업그레이드 요청으로 프로토콜 변경, 이후 WebSocket 프레임 전송 | 동일한 업그레이드를 사용하지만, 서버가 WebSocket 미지원 시 폴링(Long Polling 등)으로 자동 대체 | |
| 57 | +| **전송 기능** | 텍스트/바이너리 프레임만 제공, 연결 유지·복구 로직은 직접 구현 필요 | 재연결, 지수 백오프, Heartbeat, 폴백 전송 등을 기본 제공 | |
| 58 | +| **메시지 모델** | 애플리케이션이 메시지 포맷을 직접 정의 | 이벤트 기반(`socket.emit('event', data)`) 네임스페이스, 룸(Room) 브로드캐스트 지원 | |
| 59 | +| **스케일링** | 클러스터링·부하분산·인증을 직접 설계 | Redis 어댑터 등으로 수평 확장, 미들웨어 체인, 인증 Hook 제공 | |
| 60 | +| **사용 시점** | 인프라 제약이 없고 브라우저/프록시가 WebSocket을 지원할 때 가볍게 사용 | 폴백 필요, 복잡한 연결 관리, 서버 간 브로드캐스트 등 고수준 기능이 필요할 때 | |
| 61 | + |
| 62 | +> Socket.io는 WebSocket 기능을 "포함"하지만 자체 프로토콜 계층을 추가하여 이벤트 메타데이터를 전송한다. 따라서 Socket.io 서버는 표준 WebSocket 클라이언트와 직접 호환되지 않는다. |
| 63 | +
|
| 64 | +## 4. 네트워크 데이터 단위: Frame / Packet / Segment / Datagram |
| 65 | +| 구분 | 계층(OSI) | 주 사용 장비 | 주요 헤더 필드 | 특징 | |
| 66 | +| --- | --- | --- | --- | --- | |
| 67 | +| **Frame** | 데이터 링크 계층 (Layer 2) | 스위치, NIC | MAC 주소, EtherType, FCS/CRC | 물리 네트워크에서 인접 노드 간 전달. MTU(예: 1500바이트) 제한 포함. | |
| 68 | +| **Packet** | 네트워크 계층 (Layer 3) | 라우터 | 출발지/목적지 IP, TTL, 프로토콜 번호 | 라우팅을 통해 여러 네트워크를 거쳐 최종 목적지에 도달. 단편화(Fragmentation) 가능. | |
| 69 | +| **Segment** | 전송 계층 (Layer 4, TCP) | 호스트 OS 커널 | 출발지/목적지 포트, SEQ/ACK 번호, 플래그, 윈도우 크기 | 신뢰성, 흐름 제어, 혼잡 제어 기능. 재전송 및 순서 보장. | |
| 70 | +| **Datagram** | 전송 계층 (Layer 4, UDP) 또는 일반적으로 비연결 메시지 | 호스트 OS 커널 | 출발지/목적지 포트, 길이, 체크섬 | 독립적 메시지 단위, 재전송 없음. 실시간 스트리밍, DNS 등에 사용. | |
| 71 | + |
| 72 | +### 캡슐화/역캡슐화 흐름 |
| 73 | +1. 응용 계층 데이터가 전송 계층으로 내려오며 **Segment/Datagram**이 된다. |
| 74 | +2. 네트워크 계층에서는 IP 헤더가 추가되어 **Packet**으로 변환. |
| 75 | +3. 데이터 링크 계층에서 MAC 헤더·트레일러가 붙어 **Frame**으로 전송. |
| 76 | +4. 수신 측은 반대로 헤더를 제거하며 상위 계층으로 전달한다. |
| 77 | + |
| 78 | +### 예시: TCP 기반 HTTP 요청 |
| 79 | +1. 브라우저가 HTTP 요청 메시지를 작성한다. |
| 80 | +2. TCP는 이를 Segment로 캡슐화하고 순서 번호와 ACK 번호를 관리한다. |
| 81 | +3. IP 계층이 Segment를 Packet에 담아 목적지 IP로 전달한다. |
| 82 | +4. 이더넷 링크 계층이 Packet을 Frame에 담아 스위치를 거쳐 서버로 전송한다. |
| 83 | +5. 서버는 역캡슐화를 거쳐 HTTP 요청을 애플리케이션에 전달한다. |
| 84 | + |
| 85 | +## 5. 추가로 알아두면 좋은 개념 |
| 86 | +- **포트(Port)**: 운영체제가 프로세스를 구분하기 위해 사용하는 16비트 번호. 0~1023는 well-known, 1024~49151는 등록된(registered), 49152~65535는 동적/사설 포트. |
| 87 | +- **소켓 페어(Socket Pair)**: TCP 커넥션을 고유하게 구별하는 `(Local IP:Port, Remote IP:Port)` 튜플. |
| 88 | +- **TIME_WAIT**: 능동 종료자가 마지막 ACK을 보낸 뒤 2MSL 동안 지연된 패킷이 사라지길 기다리는 상태. `SO_REUSEADDR`을 사용하면 동일한 로컬 IP/포트를 다시 바인드할 수 있지만, 여전히 기존 커넥션의 잔여 패킷이 도착할 수 있으므로 애플리케이션 레벨에서 중복 수신을 감안해야 한다. |
| 89 | +- **Half-close**: TCP에서는 한쪽 방향 스트림을 먼저 닫을 수 있다(`shutdown(SHUT_WR)`). 스트림 기반 특성을 활용하여 프로토콜 구현 가능. |
| 90 | + |
| 91 | +> 위 개념을 이해하면 네트워크 프로그램 디버깅 시 `netstat`, `ss`, `tcpdump`, `wireshark` 같은 도구에서 관찰되는 데이터 단위와 소켓 상태를 정확히 해석할 수 있다. |
0 commit comments