PromleeBlog
sitemap
aboutMe

posting thumbnail
TCP와 UDP 신뢰성과 속도의 균형 - 코딩과 함께 배우는 네트워크 5일차
TCP and UDP Balancing Reliability and Speed - Learning Network with Coding Day 5

📅

🚀

들어가기 전에 🔗

지난 시간에는 우리 동네 안에서 통신할 때 필요한 MAC 주소와 ARP 프로토콜에 대해 배웠습니다.
오늘은 한 단계 더 나아가, 실제로 데이터가 목적지 프로그램까지 잘 도착하도록 돕는
TCP
UDP
프로토콜에 대해 알아보겠습니다.
이들은 전송 계층(OSI 7계층 중 4계층, TCP/IP 모델 중 3계층)에서 작동하며, 데이터 전송의
신뢰성
속도
라는 두 마리 토끼를 어떻게 잡을지 고민하는 친구들입니다.

🚀

UDP (User Datagram Protocol) 🔗

UDP는 'User Datagram Protocol'의 줄임말로, TCP와는 성격이 많이 다른 프로토콜입니다.
UDP는 데이터를 보내기 전에 상대방과 연결을 설정하지 않고(비연결 지향), 데이터가 순서대로 잘 도착했는지, 빠진 부분은 없는지 거의 확인하지 않습니다.
마치 일반 우편처럼, 일단 보내고 나면 도착 여부를 크게 신경 쓰지 않는 것과 비슷합니다.

👨‍💻
UDP는 TCP보다 구조가 간단하고 헤더 정보가 적어서 데이터 전송 속도가 매우 빠르다는 장점이 있습니다.

UDP의 주요 특징 🔗

UDP는 언제 사용할까요? 🔗

약간의 데이터 손실이 있더라도, 실시간성과 빠른 속도가 더 중요할 때 주로 사용됩니다.

실습 1: Python으로 간단한 UDP 메시지 송수신기 만들기 🔗

UDP의 비연결 지향적 특징과 빠른 데이터 전송 방식을 코드로 확인해 봅시다.
UDP 메시지 수신기 (udp_receiver.py)
import socket # 소켓 모듈 임포트
 
  # 수신기 설정
HOST = '127.0.0.1'  # 수신할 IP 주소 (모든 인터페이스에서 수신하려면 '0.0.0.0')
PORT = 12345        # 사용할 포트 번호
 
  # 1. 소켓 생성 (AF_INET: IPv4, SOCK_DGRAM: UDP 프로토콜)
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as receiver_socket:
    # 2. 소켓에 주소와 포트 번호 할당
    receiver_socket.bind((HOST, PORT))
    print(f"UDP 수신기가 {HOST}:{PORT} 에서 메시지를 기다리고 있습니다...")
 
    while True:
        # 3. 데이터 수신 (최대 1024바이트), 보낸 측의 주소 정보도 함께 받음
        # recvfrom() 함수는 데이터가 도착할 때까지 여기서 실행을 멈추고 기다립니다 (블로킹).
        data, sender_address = receiver_socket.recvfrom(1024)
        
        print(f"{sender_address} 로부터 메시지 수신: {data.decode()}") # 바이트 데이터를 문자열로 디코딩
        
        # UDP는 비연결형이므로, 응답을 보내고 싶다면 sender_address를 사용합니다.
        # 여기서는 간단히 수신만 합니다.
        # 만약 "종료" 메시지를 받으면 수신기를 멈추고 싶다면 아래 주석 해제
        # if data.decode().lower() == "종료":
        #     print("종료 메시지를 수신하여 수신기를 중단합니다.")
        #     break
UDP 메시지 송신기 (udp_sender.py)
import socket # 소켓 모듈 임포트
 
  # 수신기(서버) 정보
RECEIVER_HOST = '127.0.0.1' # 데이터를 보낼 대상의 IP 주소
RECEIVER_PORT = 12345       # 데이터를 보낼 대상의 포트 번호
 
  # 1. 소켓 생성 (AF_INET: IPv4, SOCK_DGRAM: UDP)
  # UDP는 연결 과정이 없으므로, 송신 측에서는 bind()가 필수는 아닙니다.
  # (운영체제가 알아서 사용 가능한 포트를 할당해줍니다.)
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as sender_socket:
    while True:
        message = input("수신기에 보낼 메시지를 입력하세요 (끝내려면 '종료' 입력): ")
        if message.lower() == "종료":
            # sender_socket.sendto(message.encode(), (RECEIVER_HOST, RECEIVER_PORT)) # 종료 메시지도 보낼 수 있음
            print("송신기를 종료합니다.")
            break
        
        # 2. 입력받은 메시지를 바이트 데이터로 인코딩하여 지정된 주소로 전송
        sender_socket.sendto(message.encode(), (RECEIVER_HOST, RECEIVER_PORT))
        print(f"'{message}' 메시지를 {RECEIVER_HOST}:{RECEIVER_PORT} 로 발송했습니다.")
실행 방법
  1. 먼저 udp_receiver.py 파일을 실행합니다. 터미널에 "UDP 수신기가 ... 메시지를 기다리고 있습니다..." 메시지가 뜹니다.
  2. 다른 터미널 창을 열고 udp_sender.py 파일을 실행합니다.
  3. 송신기 터미널에 "수신기에 보낼 메시지를 입력하세요..." 프롬프트가 뜨면, 메시지를 입력하고 엔터를 칩니다.
  4. 수신기 터미널에 송신기가 보낸 메시지와 송신자의 주소 정보가 출력됩니다. '종료'를 입력할 때까지 계속 메시지를 주고받을 수 있습니다.
실행 예시
실행 예시
뒤에 나올 TCP 예제와 비교해보면, UDP 코드는 listen(), accept() 같은 연결 관리 부분이 없는 것을 확인할 수 있습니다.
이것이 바로 비연결 지향 프로토콜의 특징입니다.

🚀

TCP (Transmission Control Protocol) 🔗

TCP는 'Transmission Control Protocol'의 줄임말로, 이름에서 알 수 있듯이
전송을 제어하는 프로토콜
입니다.
TCP는 데이터를 보내기 전에 상대방과 먼저 연결을 설정하고(연결 지향), 데이터가 순서대로 정확하게 도착했는지, 빠진 부분은 없는지 꼼꼼하게 확인합니다.
마치 우리가 중요한 서류를 등기 우편으로 보내면서, 상대방이 잘 받았는지, 중간에 분실되지는 않았는지 확인하는 것과 비슷합니다.

TCP의 주요 특징 🔗

3-Way Handshake: "데이터 보내도 될까요?" 🔗

TCP는 데이터를 보내기 전에 상대방과 "우리 이제부터 통신 시작하자!" 하고 약속하는 과정을 거치는데, 이것을
3-Way Handshake
라고 부릅니다. 마치 세 번의 악수와 같습니다.
3-Way Handshake 흐름도
3-Way Handshake 흐름도
  1. SYN (Synchronize Sequence Numbers)
    클라이언트가 서버에게 "안녕? 나 너랑 통신하고 싶은데, 준비됐니?" 하고 연결 요청 메시지(SYN)를 보냅니다.
    이때 클라이언트는 자신의 초기 순서 번호(일종의 데이터 시작 번호)를 함께 보냅니다.
  2. SYN + ACK (Acknowledgement)
    서버는 클라이언트의 요청을 받고, "응, 좋아! 나도 준비됐어. 네 요청 잘 받았고, 이건 내 시작 번호야." 하고 응답 메시지(SYN+ACK)를 보냅니다.
    이 응답에는 서버 자신의 초기 순서 번호와 함께, 클라이언트의 요청을 잘 받았다는 확인(ACK) 정보가 담겨 있습니다.
  3. ACK
    클라이언트는 서버의 응답을 받고, "알겠어! 너도 준비됐구나. 이제 진짜 통신 시작하자!" 하고 마지막 확인 메시지(ACK)를 서버에게 보냅니다.
    이로써 연결이 완전히 설정됩니다.

4-Way Handshake: "이제 그만 안녕!" 🔗

데이터 전송이 모두 끝나면, 연결을 안전하게 종료하기 위해
4-Way Handshake
과정을 거칩니다.
4-Way Handshake 흐름도
4-Way Handshake 흐름도
  1. FIN (Finish)
    클라이언트가 서버에게 "나 이제 보낼 데이터 다 보냈어. 연결 끊어도 될까?" 하고 연결 종료 요청(FIN)을 보냅니다.
  2. ACK
    서버는 "알겠어. 네 요청 잘 받았어. 잠시만 기다려줘, 나도 마무리할 게 있어." 하고 확인(ACK) 메시지를 보냅니다.
    이때 서버는 아직 클라이언트에게 보낼 데이터가 남아있을 수 있습니다.
  3. FIN
    서버가 모든 데이터를 다 보내고 연결을 종료할 준비가 되면, 클라이언트에게 "나도 이제 다 끝났어. 연결 끊자." 하고 종료 요청(FIN)을 보냅니다.
  4. ACK
    클라이언트는 서버의 종료 요청을 받고, "알겠어. 잘 가!" 하고 마지막 확인(ACK) 메시지를 보낸 후 잠시 대기했다가 연결을 완전히 닫습니다.
    서버도 이 ACK를 받으면 연결을 닫습니다.

TCP는 언제 사용할까요? 🔗

신뢰성이 매우 중요한 데이터 전송에 주로 사용됩니다.

실습 2: Python으로 간단한 TCP 에코 서버와 클라이언트 만들기 🔗

TCP의 연결 지향적 특징과 데이터 교환을 코드로 확인해 봅시다. 에코 서버는 클라이언트가 보낸 메시지를 그대로 다시 보내주는 서버입니다.
TCP 에코 서버 (tcp_echo_server.py)
import socket # 소켓 프로그래밍을 위한 모듈 임포트
 
  # 서버 설정
HOST = '127.0.0.1'  # 로컬호스트 IP 주소 (자기 자신 컴퓨터)
PORT = 12345        # 사용할 포트 번호 (다른 프로그램과 겹치지 않게)
 
  # 1. 소켓 생성 (AF_INET: IPv4 주소 체계, SOCK_STREAM: TCP 프로토콜)
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as server_socket:
    # 2. 소켓에 주소와 포트 번호 할당
    server_socket.bind((HOST, PORT))
    
    # 3. 클라이언트의 연결 요청을 기다리도록 설정 (최대 1개 연결 동시 처리)
    server_socket.listen(1)
    print(f"서버가 {HOST}:{PORT} 에서 연결을 기다리고 있습니다...")
 
    # 4. 클라이언트의 연결 수락 (연결되면 새로운 소켓과 주소 정보 반환)
    # accept() 함수는 클라이언트가 연결할 때까지 여기서 실행을 멈추고 기다립니다 (블로킹).
    client_socket, client_address = server_socket.accept()
    
    with client_socket:
        print(f"{client_address} 에서 클라이언트가 연결되었습니다.")
        
        while True:
            # 5. 클라이언트로부터 데이터 수신 (최대 1024바이트)
            data = client_socket.recv(1024)
            if not data: # 클라이언트가 연결을 끊으면 빈 데이터 수신
                print(f"{client_address} 에서 연결이 종료되었습니다.")
                break # 반복문 종료
            
            print(f"수신된 메시지: {data.decode()}") # 수신된 바이트 데이터를 문자열로 디코딩하여 출력
            
            # 6. 수신한 데이터를 클라이언트에게 그대로 다시 전송 (에코)
            client_socket.sendall(data) 
            print(f"에코 메시지 발송: {data.decode()}")
TCP 에코 클라이언트 (tcp_echo_client.py)
import socket  # 소켓 프로그래밍을 위한 모듈 임포트
 
  # 서버 정보
HOST = '127.0.0.1'  # 접속할 서버의 IP 주소
PORT = 12345        # 접속할 서버의 포트 번호
 
  # 1. 소켓 생성 (AF_INET: IPv4, SOCK_STREAM: TCP)
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as client_socket:
    # 2. 서버에 연결 시도
    client_socket.connect((HOST, PORT))
    print(f"{HOST}:{PORT} 서버에 연결되었습니다.")
 
    while True:
        # 사용자로부터 메시지 입력
        message = input("서버에 보낼 메시지를 입력하세요 (종료하려면 '종료' 입력): ")
 
        if message.lower() == '종료':
            print("클라이언트 연결을 종료합니다.")
            break  # 반복문 종료 → with문 빠져나가며 소켓 자동 종료
 
        # 3. 입력받은 메시지를 바이트 데이터로 인코딩하여 서버에 전송
        client_socket.sendall(message.encode())
        print(f"메시지 발송: {message}")
 
        # 4. 서버로부터 에코된 데이터 수신 (최대 1024바이트)
        echo_data = client_socket.recv(1024)
        print(f"서버로부터 에코된 메시지: {echo_data.decode()}")  # 수신된 바이트 데이터를 문자열로 디코딩
 
  # 소켓은 with 문을 빠져나가면서 자동으로 닫힙니다.
실행 방법:
  1. 먼저 tcp_echo_server.py 파일을 실행합니다. 터미널에 "서버가 ... 연결을 기다리고 있습니다..." 메시지가 뜨면 정상입니다.
  2. 다른 터미널 창을 열고 tcp_echo_client.py 파일을 실행합니다.
  3. 클라이언트 터미널에 "서버에 보낼 메시지를 입력하세요:" 라는 프롬프트가 뜨면, 아무 메시지나 입력하고 엔터를 칩니다.
  4. 서버는 클라이언트가 보낸 메시지를 받고, 클라이언트는 서버가 다시 보내준 에코 메시지를 받게 됩니다.
TCP 실습을
TCP 실습을
이 실습을 통해 TCP가 연결을 맺고 데이터를 주고받는 과정을 간접적으로 체험할 수 있습니다.

🚀

TCP vs UDP 🔗

특징TCP (Transmission Control Protocol)UDP (User Datagram Protocol)
연결 방식
연결 지향 (Connection-oriented)비연결 지향 (Connectionless)
신뢰성
높음 (순서 보장, 오류 제어, 재전송)낮음 (순서 보장 X, 최소한의 오류 검사)
속도
상대적으로 느림빠름
헤더 크기
상대적으로 큼 (최소 20바이트)작음 (8바이트)
흐름 제어
지원함지원 안 함
혼잡 제어
지원함지원 안 함
데이터 단위
세그먼트 (Segment)데이터그램 (Datagram)
주요 사용처
웹, 이메일, 파일 전송 등 신뢰성 중요실시간 스트리밍, 온라인 게임, DNS 등 속도 중요

🚀

결론 🔗

오늘은 데이터를 안전하고 확실하게 전달하는 우편배달부 TCP와, 빠르고 간편하게 소식을 전하는 특급 배송원 UDP에 대해 알아보았습니다.
TCP는 3-Way Handshake와 4-Way Handshake를 통해 연결을 관리하며 높은 신뢰성을 제공하지만 다소 느릴 수 있고, UDP는 이러한 과정 없이 빠르게 데이터를 전송하지만 신뢰성은 낮습니다.

Python 코드를 이용한 간단한 실습을 통해 이 두 프로토콜의 기본적인 동작 방식도 체험해 보았습니다.
다음 시간에는 우리가 웹사이트에 접속할 때 사용하는 핵심 언어, HTTP와 보안이 강화된 HTTPS 프로토콜에 대해 자세히 알아보겠습니다.

참고 🔗