인터넷의 핵심 동력, TCP
(cefboud.com)- 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 설계와 구현의 정교함이 인터넷의 지속적 성장 기반임
Hacker News 의견
-
신뢰성 없는 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 참고- SCTP는 문제의 절반만 해결하고, 새로운 결함을 여러 개 도입함
결국 가장 좋은 해답은 UDP 위의 신뢰성 계층, 즉 QUIC임 - BSD를 즐겨 쓰고 Erlang으로 일하는 입장에서 SCTP를 매우 사랑함
- SCTP는 문제의 절반만 해결하고, 새로운 결함을 여러 개 도입함
-
IP만으로 직접 패킷을 보낼 수 있는지 궁금했음
중간 라우터들이 TCP나 UDP가 아닌 패킷을 거부할 것 같았음- 직접 IP 패킷을 조작해 전송할 수 있음
IPv4라면 IANA 프로토콜 번호 목록에서 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 페이로드일 뿐이며, 라우터는 이를 해석하지 않음
포트 번호는 단지 노드 내부 서비스 식별자일 뿐임
- 직접 IP 패킷을 조작해 전송할 수 있음
-
RUDP(Plan9) 는 TCP와 UDP 사이의 훌륭한 절충안이었음
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으로 직접 패킷을 조립해 실험할 수 있었음