TCP/IP 개념 정리

TCP/IP의 계층 구조, TCP와 IP의 역할 차이, 3-way handshake, 포트와 소켓, Python TCP 서버/클라이언트 예제로 네트워크 흐름을 정리했습니다.

TCP/IP는 인터넷과 대부분의 사내 네트워크에서 사용하는 기본 통신 구조입니다. TCP는 데이터를 신뢰성 있게 전달하기 위한 전송 계층 프로토콜이고, IP는 목적지까지 패킷을 라우팅하기 위한 인터넷 계층 프로토콜입니다. 둘을 함께 부를 때 TCP/IP라고 하지만, 실제 통신은 응용 계층, 전송 계층, 인터넷 계층, 네트워크 액세스 계층이 나뉘어 동작합니다.

TCP/IP 계층 구조

계층 역할 대표 예시
응용 계층 사용자 프로그램이 이해하는 데이터 형식과 요청/응답 규칙을 정의합니다. HTTP, HTTPS, DNS, SMTP, FTP, SSH
전송 계층 프로세스 간 통신을 담당하며 포트 번호를 사용합니다. TCP, UDP
인터넷 계층 출발지 IP에서 목적지 IP까지 패킷을 전달합니다. IPv4, IPv6, ICMP
네트워크 액세스 계층 같은 링크 안에서 프레임을 전달합니다. Ethernet, Wi-Fi, VLAN, ARP

예를 들어 브라우저가 웹 서버에 접속하면 응용 계층에서는 HTTP 요청이 만들어지고, 전송 계층에서는 TCP 세그먼트가 되고, 인터넷 계층에서는 IP 패킷이 되며, 마지막으로 이더넷 프레임에 실려 네트워크로 나갑니다.

TCP와 IP의 역할 차이

IP는 목적지까지 패킷을 보내는 역할에 집중합니다. 목적지에 도착했는지, 순서가 맞는지, 중간에 유실됐는지는 IP 단독으로 보장하지 않습니다. TCP는 그 위에서 연결을 만들고, 순서 번호와 ACK를 사용해 손실된 데이터를 재전송하고, 수신 측이 처리 가능한 속도에 맞춰 흐름을 조절합니다.

  • IP 주소는 어느 장비로 보낼지 결정합니다.
  • 포트 번호는 그 장비 안의 어느 프로세스로 보낼지 결정합니다.
  • TCP 연결은 출발지 IP, 출발지 포트, 목적지 IP, 목적지 포트의 조합으로 구분됩니다.
  • TCP는 신뢰성을 제공하지만 UDP보다 연결 관리 비용이 큽니다.

TCP 3-way handshake

TCP 연결은 일반적으로 다음 순서로 시작합니다.

  1. 클라이언트가 서버로 SYN을 보냅니다.
  2. 서버가 SYN, ACK로 응답합니다.
  3. 클라이언트가 ACK를 보내면 연결이 성립됩니다.

이 단계에서 막히면 애플리케이션 문제가 아니라 방화벽, 라우팅, 서버 listen 상태, 보안 장비, 서버 backlog 같은 네트워크/OS 계층 문제일 수 있습니다. 연결이 성립된 뒤 느리다면 패킷 손실, 윈도우 크기, 서버 처리 지연, 애플리케이션 응답 지연을 따로 봐야 합니다.

Python으로 보는 TCP 소켓 예제

실습에는 TCP 클라이언트 예제가 있었지만, 클라이언트만 있으면 실제로 테스트할 서버가 없습니다. 아래처럼 서버와 클라이언트를 나누면 TCP 연결 흐름을 직접 확인할 수 있습니다.

서버 코드

# tcp_server.py
import socket

HOST = "127.0.0.1"
PORT = 12345

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as server:
 server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
 server.bind((HOST, PORT))
 server.listen(5)
 print(f"listening on {HOST}:{PORT}")

 conn, addr = server.accept()
 with conn:
 print(f"connected by {addr}")
 data = conn.recv(1024)
 print("received:", data.decode())
 conn.sendall(b"Hello from server")

클라이언트 코드

# tcp_client.py
import socket

HOST = "127.0.0.1"
PORT = 12345

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as client:
 client.connect((HOST, PORT))
 client.sendall(b"Hello, world")
 data = client.recv(1024)

print("Received:", data.decode())

실행 순서

python3 tcp_server.py

# 다른 터미널에서 실행
python3 tcp_client.py

서버가 먼저 listen 상태로 대기하고 있어야 클라이언트가 연결할 수 있습니다. 서버가 떠 있지 않으면 클라이언트는 보통 Connection refused를 받습니다. 네트워크 경로가 막혀 있거나 패킷이 돌아오지 않으면 timeout 형태로 보이는 경우가 많습니다.

연결 상태 확인

개념을 확인하기 위한 최소한의 관찰 명령만 정리합니다. TCP/IP 개념 글이므로 명령어를 많이 넣기보다 연결 상태를 이해하는 데 필요한 정도만 사용합니다.

ss -tnlp | grep ':12345'
ss -tnp | grep ':12345'

LISTEN은 서버가 연결을 받을 준비가 된 상태이고, ESTAB는 TCP 연결이 성립된 상태입니다. SYN-SENTSYN-RECV가 오래 남아 있으면 handshake 단계에서 문제가 있는 것입니다.

MTU와 캡슐화

응용 데이터는 TCP 세그먼트, IP 패킷, Ethernet 프레임으로 감싸져 전송됩니다. 각 계층은 자기 헤더를 붙이므로 실제 네트워크에서 한 번에 보낼 수 있는 크기에는 제한이 있습니다. 일반 Ethernet MTU는 1500바이트이며, VPN, 터널링, VXLAN 같은 캡슐화가 들어가면 유효 payload가 줄어들 수 있습니다.

MTU 문제는 작은 요청은 성공하지만 큰 응답이나 파일 전송만 실패하는 형태로 나타날 수 있습니다. 이 경우 단순히 포트가 열렸는지만 볼 것이 아니라 경로 MTU, 방화벽의 ICMP 차단 여부, 터널 인터페이스 MTU까지 확인해야 합니다.

정리

  • IP는 목적지 장비까지 패킷을 보내는 계층입니다.
  • TCP는 포트 기반 연결, 순서 보장, 재전송, 흐름 제어를 담당합니다.
  • 서버 프로그램은 먼저 bind/listen 상태가 되어야 클라이언트 연결을 받을 수 있습니다.
  • TCP 장애는 연결 전 handshake 문제와 연결 후 데이터 전송 문제를 분리해서 봐야 합니다.
  • 실무에서는 IP, port, listen, route, firewall, packet loss, MTU를 순서대로 확인합니다.
BGM EVER