TIL

[AI와 함께하는 네트워크 공부] 2. TCP vs UDP

양밀루 2025. 7. 7. 20:04

🚚 TCP (Transmission Control Protocol)

장점 단점
1. 신뢰성 보장
  • 패킷 분실 시 자동 재전송
  • 중복 패킷 제거
  • 순서 보장
2. 흐름 제어
  • 받는 쪽 속도에 맞춰 전송
  • 버퍼 오버플로우 방지
3. 혼잡 제어
  • 네트워크 상황에 맞춰 전송 속도 조절
1. 속도 느림
  • 연결 설정 시간 필요
  • 확인 과정으로 인한 지연
2. 오버헤드
  • 헤더 크기 큼 (20바이트)
  • 제어 정보 많음
3. 실시간 통신에 부적합
  • 재전송으로 인한 지연
  • 순서 보장 때문에 블로킹

🤝 TCP 3-Way Handshake (연결 설정)

📱 클라이언트 → 🖥️ 서버: "연결하고 싶어요!" (SYN)
🖥️ 서버 → 📱 클라이언트: "좋아요! 연결해요!" (SYN-ACK)  
📱 클라이언트 → 🖥️ 서버: "확인했어요!" (ACK)

 

ex.

// URLSession 사용 시 내부적으로 TCP 3-Way Handshake 자동 수행
let url = URL(string: "https://api.example.com/data")!
URLSession.shared.dataTask(with: url) { data, response, error in
    // TCP 연결이 확실히 설정된 후 데이터 전송
}

🔄 TCP 데이터 전송 과정

1. 클라이언트: "데이터 1번 보냅니다"
2. 서버: "1번 잘 받았어요!"
3. 클라이언트: "데이터 2번 보냅니다"  
4. 서버: "2번 잘 받았어요!"
5. 클라이언트: "데이터 3번 보냅니다"
6. 서버: 응답 없음 (분실)
7. 클라이언트: "3번 다시 보냅니다!" (재전송)
8. 서버: "3번 잘 받았어요!"

 

🎯 TCP 사용 사례

// 1. 일반적인 API 호출
URLSession.shared.dataTask(with: url) // 내부적으로 TCP 사용

// 2. 파일 다운로드
URLSession.shared.downloadTask(with: url) { location, response, error in
    // 파일이 완전히 다운로드되는 것이 중요
}

// 3. 채팅 메시지 전송
func sendMessage(_ message: String) {
    let url = URL(string: "https://chat.example.com/send")!
    var request = URLRequest(url: url)
    request.httpMethod = "POST"
    request.httpBody = message.data(using: .utf8)
    
    URLSession.shared.dataTask(with: request) { data, response, error in
        // 메시지가 확실히 전송되는 것이 중요
    }.resume()
}

 


🚀 UDP (User Datagram Protocol) 

장점 단점
1. 빠른 속도
  • 연결 설정 과정 없음
  • 즉시 데이터 전송
2. 낮은 오버헤드
  • 헤더 크기 작음 (8바이트)
  • 간단한 구조
3. 실시간 통신에 적합
  • 지연 시간 최소화
  • 블로킹 없음
1. 신뢰성 없음
  • 패킷 분실 가능
  • 중복 패킷 가능
  • 순서 보장 안됨
2. 흐름 제어 없음
  • 받는 쪽 상황 고려 안함
  • 버퍼 오버플로우 가능

📦 UDP 데이터 전송 과정

1. 클라이언트: "데이터 1번 보냅니다" (바로 전송)
2. 클라이언트: "데이터 2번 보냅니다" (바로 전송)
3. 클라이언트: "데이터 3번 보냅니다" (바로 전송)
4. 서버: 2번, 1번, 3번 순서로 도착 (순서 바뀜)
5. 서버: 1번 분실되어도 재전송 요청 안함

🎯 UDP 사용 사례

import Network

// 1. 실시간 게임
class GameClient {
    let connection: NWConnection
    
    init() {
        let endpoint = NWEndpoint.hostPort(host: "game.server.com", port: 12345)
        connection = NWConnection(to: endpoint, using: .udp)
        connection.start(queue: .main)
    }
    
    func sendPlayerPosition(x: Float, y: Float) {
        let data = "\(x),\(y)".data(using: .utf8)!
        connection.send(content: data, completion: .idempotent)
        // 빠른 전송이 중요, 일부 패킷 분실은 괜찮음
    }
}

// 2. 화상통화 (WebRTC)
// WebRTC는 내부적으로 UDP 사용
import WebRTC

class VideoCallManager {
    let peerConnection: RTCPeerConnection
    
    func setupVideoCall() {
        // RTP (Real-time Transport Protocol) over UDP
        // 실시간 영상/음성 전송에는 UDP가 적합
    }
}

// 3. DNS 조회
// iOS가 내부적으로 UDP 사용
// 빠른 응답이 중요, 실패 시 재시도

⚖️ TCP vs UDP 비교

  TCP UDP
연결 설정 필요 (3-Way Handshake) 불필요
신뢰성 보장 (재전송, 순서보장) 보장 안됨
속도 느림 빠름
헤더 크기 20바이트 8바이트
흐름 제어 있음 없음
혼잡 제어 있음 없음
오버헤드 높음 낮음
실시간 통신 부적합 적합

 

 

📱 iOS에서의 실제 구현

TCP 사용 (URLSession)

class APIClient {
    func fetchUserData(completion: @escaping (User?) -> Void) {
        let url = URL(string: "https://api.example.com/user")!
        
        // TCP 연결 사용 (기본값)
        URLSession.shared.dataTask(with: url) { data, response, error in
            guard let data = data else {
                completion(nil)
                return
            }
            
            // 데이터 무결성이 중요한 경우
            let user = try? JSONDecoder().decode(User.self, from: data)
            completion(user)
        }.resume()
    }
}

UDP 사용 (Network Framework)

import Network

class UDPClient {
    private var connection: NWConnection?
    
    func connect(to host: String, port: UInt16) {
        let endpoint = NWEndpoint.hostPort(
            host: NWEndpoint.Host(host), 
            port: NWEndpoint.Port(integerLiteral: port)
        )
        
        connection = NWConnection(to: endpoint, using: .udp)
        
        connection?.stateUpdateHandler = { state in
            switch state {
            case .ready:
                print("UDP 연결 준비됨")
            case .failed(let error):
                print("UDP 연결 실패: \(error)")
            default:
                break
            }
        }
        
        connection?.start(queue: .main)
    }
    
    func sendData(_ data: Data) {
        connection?.send(content: data, completion: .contentProcessed { error in
            if let error = error {
                print("전송 실패: \(error)")
            }
        })
    }
    
    func receiveData() {
        connection?.receive(minimumIncompleteLength: 1, maximumLength: 1024) { data, _, _, error in
            if let data = data {
                print("데이터 수신: \(data)")
            }
            
            // 계속 수신
            self.receiveData()
        }
    }
}