# 인터넷의 핵심 동력, TCP

> Clean Markdown view of GeekNews topic #24389. Use the original source for factual precision when an external source URL is present.

## Metadata

- GeekNews HTML: [https://news.hada.io/topic?id=24389](https://news.hada.io/topic?id=24389)
- GeekNews Markdown: [https://news.hada.io/topic/24389.md](https://news.hada.io/topic/24389.md)
- Type: GN+
- Author: [neo](https://news.hada.io/@neo)
- Published: 2025-11-16T09:38:58+09:00
- Updated: 2025-11-16T09:38:58+09:00
- Original source: [cefboud.com](https://cefboud.com/posts/tcp-deep-dive-internals/)
- Points: 19
- Comments: 1

## Summary

인터넷의 안정성을 떠받치는 **TCP**는 단순한 데이터 전달을 넘어, **신뢰성·순서·흐름 제어**를 정교하게 조율하는 전송 계층의 핵심입니다. IP가 단지 ‘주소’를 다룬다면, TCP는 그 위에서 **패킷 손실 복구·혼잡 제어·3-way 핸드셰이크**를 통해 실제 애플리케이션 간 통신을 가능하게 합니다. C 언어로 구현한 **에코 서버와 간단한 HTTP 서버 예제**는 소켓 API의 동작 원리를 구체적으로 보여주며, 커널 버퍼와 윈도우 필드가 어떻게 데이터 흐름을 관리하는지도 드러냅니다. 네트워크가 아무리 복잡해져도, 결국 이 **보이지 않는 신뢰의 프로토콜**이 모든 연결의 밑단을 지탱하고 있다는 사실이 새삼 인상적입니다.

## Topic Body

- **TCP(Transmission Control Protocol)** 은 불안정한 네트워크 환경에서도 **신뢰성 있고 순서가 보장된 데이터 전송**을 가능하게 하는 인터넷의 핵심 프로토콜  
- IP가 호스트 간 데이터 전달만 담당하는 반면, TCP는 **포트 기반의 프로세스 간 통신**과 **오류 복구·재전송·순서 제어**를 수행  
- **흐름 제어(flow control)** 와 **혼잡 제어(congestion control)** 를 통해 수신 버퍼나 네트워크 대역폭의 한계를 초과하지 않도록 조절  
- C 언어로 구현한 **단순 TCP 서버와 HTTP 서버 예제**를 통해 소켓 생성, 바인드, 리슨, 연결 수락, 송수신 과정이 구체적으로 설명됨  
- TCP의 **시퀀스·ACK 번호, 윈도우, 체크섬, 플래그(SYN/ACK/FIN/RST)** 등 내부 구조는 오늘날 인터넷의 안정적 동작을 가능하게 하는 핵심 기반임  

---

### TCP의 필요성과 역할
- IP는 호스트 간 패킷 전달만 담당하며, **프로세스 간 통신**을 위해서는 TCP/UDP 같은 **전송 계층**이 필요  
  - IP 주소는 ‘건물’, 포트는 ‘아파트’에 비유되어, 각 애플리케이션이 포트에 바인드되어 통신함  
- TCP는 **패킷 손실·중복·순서 뒤바뀜** 등 네트워크 불안정성을 감추고, **재전송·체크섬** 등으로 신뢰성을 보장  
- 라우터는 단순하게 유지되고, **신뢰성은 통신 양 끝단에서 처리**되어 네트워크 인프라의 복잡성을 줄임  
- 이러한 구조 덕분에 HTTP, SMTP, SSH 등 주요 인터넷 서비스가 안정적으로 동작  

### 흐름 제어와 혼잡 제어
- 수신 측은 **커널의 수신 버퍼(receive buffer)** 를 통해 데이터를 임시 저장하며, 버퍼 크기는 `net.ipv4.tcp_rmem`으로 설정  
- 송신 측은 수신 측이 허용 가능한 데이터 양을 **윈도우(window)** 필드로 전달받아 전송량을 조절  
- 네트워크 전체의 대역폭 차이로 인한 **혼잡(congestion)** 을 방지하기 위해 TCP는 **혼잡 제어 알고리듬**을 도입  
  - 1986년 발생한 **혼잡 붕괴(congestion collapse)** 사건을 계기로 ‘백오프(back-off)’ 메커니즘이 추가됨  

### TCP 서버 및 HTTP 서버 예제
- C 언어로 작성된 **기본 TCP 에코 서버**는 클라이언트의 입력을 받아 “you sent:”를 붙여 다시 전송  
  - `socket()`, `bind()`, `listen()`, `accept()`, `send()`, `recv()` 등 **Berkeley 소켓 API** 사용  
  - 서버가 `sleep()` 중일 때 클라이언트의 데이터는 **수신 버퍼에 대기**하며, 이후 순차적으로 처리됨  
- 간단한 **HTTP/1.1 서버 예제**에서는 TCP 연결을 통해 요청을 수락하고, `HTTP/1.1 200 OK` 헤더와 본문을 전송  
  - 요청 횟수를 `i`로 카운트하며, `curl localhost:8080` 요청 시 “[1] Yo, I am a legit web server” 형태의 응답 출력  

### TCP 세그먼트 구조와 핵심 필드
- TCP 세그먼트는 **출발지·목적지 포트, 시퀀스 번호, ACK 번호, 윈도우 크기, 체크섬, 플래그** 등으로 구성  
  - 포트는 16비트씩 할당되어 **최대 64K 포트** 사용 가능  
  - 연결은 `(프로토콜, 출발지 IP, 출발지 포트, 목적지 IP, 목적지 포트)`의 **5-튜플**로 식별  
- **시퀀스 번호**는 전송된 바이트 범위를, **ACK 번호**는 수신 완료된 바이트를 나타냄  
  - 누락된 데이터가 있을 경우 ACK는 해당 지점에서 멈추며, 재전송 후 누적 ACK로 갱신  
- **플래그 비트**는 연결 상태를 제어  
  - `SYN`/`ACK`는 **3-way 핸드셰이크**를 통해 연결 수립  
  - `FIN`은 **4-way 핸드셰이크**로 연결 종료  
  - `RST`는 비정상 종료나 오류 시 즉시 연결 해제  
- **윈도우 필드**는 수신 가능한 데이터 양을 표시하며, `ss` 명령으로 버퍼 상태(`rb131072`, `tb16384`) 확인 가능  
- **체크섬(checksum)** 은 세그먼트 내 16비트 단위 합산으로 오류 검출 수행  

### 결론
- TCP는 **신뢰성·순서·무결성**을 보장하며, 불안정한 인터넷 환경에서도 애플리케이션이 정상 동작하도록 지원  
- 수십 년 전에는 수 KB 전송도 어려웠지만, 오늘날 **4K 스트리밍**이 일상화될 만큼 발전  
- 이러한 안정적 통신을 가능하게 한 **TCP 설계와 구현의 정교함**이 인터넷의 지속적 성장 기반임

## Comments



### Comment 46365

- Author: neo
- Created: 2025-11-16T09:38:58+09:00
- Points: 1

###### [Hacker News 의견](https://news.ycombinator.com/item?id=45935503) 
- 신뢰성 없는 **datagram 계층** 위에 신뢰성 있는 데이터 스트림을 만들려 하면, 결과적으로 TCP와 거의 동일한 형태가 나옴  
  TCP의 초기 한계는 작은 **윈도 크기**, 손실 패킷 처리 미흡, 그리고 단일 스트림만 관리한다는 점이었음  
  이런 문제를 해결하기 위해 **SCTP**와 **QUIC**이 등장했음  
  혼잡 제어 알고리즘은 프로토콜의 일부가 아니라 각 연결의 양쪽에서 동작하는 코드임  
  초기 알고리즘(Reno, Vegas 등)은 단순했지만 충분히 효과적이었고, 이후 대용량 버퍼·긴 RTT·공정성 등을 다루는 연구가 계속되고 있음
  - 웹 개발자들이 다중 스트림을 잘 활용하지 못한 책임도 있다고 생각함  
    예전에 JavaScript로 여러 다운로드를 하나의 스트림에서 **우선순위와 취소 기능**을 제어할 수 있는 라이브러리를 만든 적이 있음  
    GreaseMonkey 스크립트로 데이팅 사이트의 썸네일을 백그라운드에서 미리 받아오게 했고, 스크롤 위치에 따라 미리 로드되게 했음  
    결과적으로 서버 부하를 줄이면서 사용자 경험을 개선했음  
    재미있게도 그 스크립트를 한 매치에게 공유했는데, 그 사람과는 지금까지도 함께하고 있음 — 거의 **Tinder 이전의 Tinder**였던 셈임
  - TCP가 만들어질 당시에는 전화망 중심의 **회선 교환(circuit switching)** 사고방식이 지배적이었음  
    TCP는 패킷 교환망 위에서 가상의 회선을 제공하는 구조로, 신뢰성을 **재전송**으로 구현한다는 개념은 프랑스의 **Cylades** 네트워크에서 비롯되었음
  - TCP의 근본적인 결함 중 하나는 **보안 불가능성**임  
    공격자가 네트워크 어디서든 데이터를 **주입(inject)** 하거나 **RST 패킷**으로 연결을 끊을 수 있음  
    방화벽으로 RST를 차단하면 안정성은 높아지지만, 위조된 시퀀스 번호로 인한 **비동기화 공격**은 여전히 가능함  
    따라서 모든 애플리케이션은 별도의 연결로 **resume 기능**을 구현해야 하고, TCP의 느린 시작(slow start) 문제도 함께 안게 됨  
    또한 주소와 포트를 분리한 개념 자체도 비효율적이라 생각함
  - 어떤 애플리케이션은 단일 TCP 연결로도 충분히 잘 동작함  
    예를 들어 **DNS over TLS(DoT)** 에서는 하나의 TCP 연결로 여러 쿼리를 동시에 보내고 응답을 **순서 무관하게** 받을 수 있음  
    이는 여러 연결을 여는 것보다 효율적이고 예의 바른 방식임  
    QUIC이 더 빠를지는 모르겠지만, 서버 지원은 아직 제한적임  
    **HTTP/1.1 파이프라이닝**으로도 비슷한 일을 하지만 응답은 순차적으로 옴
  - TCP의 **혼잡 제어 알고리즘**이 프로토콜 외부에 있다는 점은 당시로서는 매우 혁신적이었음  
    하지만 많은 대학 강의에서는 이 점을 강조하지 않아, TCP에 단일 알고리즘만 있다고 오해하는 경우가 많음

- **SCTP**에 대한 애정이 있는지 묻고 싶음  
  SCTP는 UDP의 메시지 지향성과 TCP의 신뢰성을 결합한 프로토콜로, **멀티스트리밍**과 **멀티호밍**을 지원함  
  여러 독립 스트림을 병렬로 전송할 수 있어 웹페이지의 텍스트와 이미지를 동시에 보낼 수 있음  
  자세한 내용은 [Wikipedia: Stream Control Transmission Protocol](https://en.wikipedia.org/wiki/Stream_Control_Transmission_Protocol) 참고
  - SCTP는 문제의 절반만 해결하고, 새로운 결함을 여러 개 도입함  
    결국 가장 좋은 해답은 **UDP 위의 신뢰성 계층**, 즉 **QUIC**임  
  - BSD를 즐겨 쓰고 **Erlang**으로 일하는 입장에서 SCTP를 매우 사랑함

- IP만으로 직접 패킷을 보낼 수 있는지 궁금했음  
  중간 라우터들이 TCP나 UDP가 아닌 패킷을 거부할 것 같았음
  - 직접 **IP 패킷을 조작해 전송**할 수 있음  
    IPv4라면 [IANA 프로토콜 번호 목록](https://www.iana.org/assignments/protocol-numbers/protocol-numbers.txt)에서 0~255 중 하나를 지정하면 됨  
    코어 라우터는 이 필드를 검사하지 않지만, NAT나 ISP 장비는 검사할 수 있음  
    두 대의 리눅스 서버 간에는 실험용 번호(253, 254)로도 통신 가능함  
  - **ICMP**도 잊지 말아야 함. IPv6에서는 더 중요함  
    IPsec, GRE, L2TP 같은 프로토콜도 TCP/UDP가 아님  
    기업망의 방화벽이나 NAT 환경에서는 임의 프로토콜이 차단될 수 있음  
    NAT는 **end-to-end 원칙**을 깨뜨렸고, 결국 사람들은 TCP나 UDP 위에, 아니면 **HTTP 위에** 모든 걸 얹기 시작했음  
  - NAT나 로드밸런서가 없다면 문제없지만, 요즘은 흔하므로 IPv6가 더 나을 수 있음  
  - 라우터는 **3계층 장비**라 TCP/UDP 여부에는 신경 쓰지 않음  
    다만 ECMP 해시의 엔트로피가 줄어드는 정도의 영향만 있음  
    결국 상대방이 그 프로토콜을 이해하느냐가 관건임  
  - TCP와 UDP는 단지 IP 페이로드일 뿐이며, 라우터는 이를 해석하지 않음  
    포트 번호는 단지 **노드 내부 서비스 식별자**일 뿐임

- **RUDP(Plan9)** 는 TCP와 UDP 사이의 훌륭한 절충안이었음  
  [Reliable User Datagram Protocol](https://en.wikipedia.org/wiki/Reliable_User_Datagram_Protocol) 참고

- TCP가 기본값이 된 덕분에, 신뢰성이나 순서 보장이 필요 없는 경우에도 무조건 사용되었음  
  이제 **HTTP/3(QUIC 기반)** 이 확산되면서 상황이 나아질 가능성이 있음  
  다만 QUIC은 훨씬 복잡하고, 그 강력함이 일부에게만 유용함  
  **UDP + WireGuard 스타일의 단순한 암호화 계층**이 더 나은 대안이 될 수 있음

- TCP는 인류의 위대한 발명 중 하나지만, **반연결형 네트워크(NAT 기반)** 의 지배를 예상하지 못했음
  - “NAT를 말하는 거냐?”라는 질문이 나옴  
  - 1981년으로 돌아가 “전 세계 주소 대신 제한된 범위의 주소를 쓰고, 일부 노드는 접근 불가하게 만들자”고 하면  
    당시 엔지니어들은 왜 굳이 그렇게 복잡하게 만드냐고 물었을 것임  
    결국 지금의 **비대칭 링크 구조**와 클라이언트–서버 구분이 이런 발상에서 비롯됨

- TCP의 **혼잡 제어 알고리즘**은 개발자들이 잘 모르는 흥미로운 효과를 가짐  
  새 연결에서 데이터를 보내면 **초기 전송이 느리고**, 속도 상승은 **지연(latency)** 에 의해 결정됨  
  데이터센터에서는 RTT를 몇 마이크로초 줄이는 것만으로도 큰 속도 향상이 가능함  
  대부분의 TCP 스택은 바이트가 아니라 **세그먼트 단위**로 속도 상승을 계산하므로, **점보 프레임**을 쓰면 6배 빠르게 오를 수 있음  
  AWS는 이런 이유로 **낮은 스위칭 지연**과 **점보 프레임 지원**에 많은 노력을 들임  
  전문가들은 이런 튜닝을 하지만, 대부분은 왜 10Gbps 링크에서 10Gbps가 안 나오는지 의아해함

- IP 위에 자신만의 프로토콜을 만드는 건 **매우 간단한 일**이었음  
  15년 전만 해도 Python으로 **직접 패킷을 조립**해 실험할 수 있었음
