3.5 연결지향형 트랜스포트: TCP
3.5.1 TCP 연결
💡 TCP는 애플리케이션 프로세스가 데이터를 다른 프로세스에게 보내기 전에,두 프로세스가 서로 ’핸드셰이크’를 먼저 해야 하므로연결지향형(connection-oriented)
이다.
즉, 데이터 전송을 보장하는 파라미터들을 각자 설정하기 위한 어떤 사전 세그먼트들을 보내야 한다.
TCP
연결
은 두 통신 종단 시스템의 TCP에 존재하는 상태를 공유하는 논리적인 것이다.TCP 연결은
전이중 서비스(full-duplex service)
를 제공한다.만약 호스트 A의 프로세스와 호스트 B의 프로세스 사이에 TCP 연결이 있다면,애플리케이션 계층 데이터는 B에서 A로 흐르는 동시에 A에서 B로 흐를 수 있다.
TCP 연결은 항상 단일 송신자와 단일 수신자 사이의
점대점(point-to-point)
이다.따라서 단일 송신 동작으로 한 송신자가 여러 수신자에게 데이터를 전송하는 ‘멀티캐스팅(multicasting)’은 TCP에서는 불가능하다.
TCP 연결 과정
클라이언트 프로세스(client process)
: 연결을 초기화하는 프로세스
서버 프로세스(server process)
세 방향 핸드셰이크(three-way handshake)
(3.5.6에서 더 자세히 다룰 예정)
- 클라이언트 애플리케이션 프로세스는 서버의 프로세스와 연결을 설정하기를 원한다고 TCP 클라이언트에게 먼저 알린다.
- 클라이언트의 트랜스포트 계층은 서버의 TCP와의 TCP 연결 설정을 진행한다.
즉, 클라이언트가 먼저 특별한 TCP 세그먼트를 보낸다.
- 서버는
두 번째 특별한 TCP 세그먼트
로 응답한다.
- 마지막으로, 클라이언트가
세 번째 특별한 세그먼트
로 다시 응답한다.
- 처음 2개의 세그먼트에는
페이로드(payload, 애플리케이션 계층 데이터)
가 없다.
- 세 번째 세그먼트는 페이로드를 포함할 수 있다.
TCP 연결이 설정된 이후
일단 TCP 연결이 설정되면, 두 애플리케이션 프로세스는 서로 데이터를 보낼 수 있다.
- 클라이언트 프로세스는
소켓(프로세스의 관문)
을 통해 데이터의 스트림을 전달한다.
- 데이터가 관문을 통해 전달되면, 이제 데이터는 클라이언트에서 동작하고 있는 TCP에 맡겨진다.
- TCP는 초기 세 방향 핸드셰이크 동안 준비된 버퍼 중의 하나인 연결의
송신 버퍼(send buffer)
로 데이터를 보낸다. - 때때로 TCP는 송신 버퍼에서 데이터 묶음을 만들어서 네트워크로 보낸다. (TCP가 언제 버퍼된 데이터를 전송해야 하는지는 정해져 있지 않음)
최대 세그먼트 크기(maximum segment size, MSS)
세그먼트로 모아 담을 수 있는 최대 데이터 양은 최대 세그먼트 크기(maximum segment size, MSS)로 제한된다.
MSS를 결정하는 요소
- 로컬 송신 호스트에 의해 전송될 수 있는 가장 큰 링크 계층 프레임의 길이
(
최대 전송 단위(maximum transmission unit, MTU
)- TCP 세그먼트(IP 데이터그램 안에 캡슐화되었을 때)와 TCP/IP 헤더 길이(통상 40바이트)가 단일 링크 계층 프레임에 딱 맞도록 한다.
💡 MSS는 헤더를 포함하는 TCP 세그먼트의 최대 크기가 아니라, 세그먼트에 있는 애플리케이션 계층 데이터에 대한 최대 크기이다.
TCP 세그먼트(TCP segment)
TCP 헤더 + 클라이언트 데이터
- 네트워크 계층에 전달되어
네트워크 계층 IP 데이터그램
안에 각각 캡슐화된다.
세그먼트
는 네트워크로 송신된다.
- TCP가 상대에게서 세그먼트를 수신했을 때, 세그먼트의 데이터는 TCP 연결의
수신 버퍼
에 위치한다.
→ 애플리케이션은 이 버퍼로부터 데이터의 스트림을 읽는다.
TCP 연결의 양 끝은 각각 자신의 송신 버퍼와 수신 버퍼를 갖고 있다.
💡 즉, TCP 연결은 한쪽 호스트에서의 버퍼, 변수, 프로세스에 대한 소켓 연결과다른 쪽 호스트에서의 버퍼, 변수, 프로세스에 대한 소켓 연결의 집합으로 이루어진다.
3.5.2 TCP 세그먼트 구조
구조
헤더 필드
출발지와 목적지 포트 번호(source and destination port number)
체크섬 필드(checksum field)
- 32비트
순서 번호 필드(sequence number field)
- 32비트
확인응답 번호 필드(acknowledgement number field)
- 16비트
수신 윈도(receive window)
: 흐름 제어에 사용된다.
(수신자가 받아들이려는 바이트의 크기를 나타내는데 사용됨)
- 4비트
헤더 길이 필드(header length field)
: 32비트 워드 단위로 TCP 헤더의 길이를 나타낸다.
옵션 필드(option field)
- 이 필드는 선택적이고 가변적인 길이를 가진다.
- 송신자와 수신자가 최대 세그먼트 크기(MSS)를 협상하거나 고속 네트워크에서 사용하기 위한 윈도 확장 요소로 이용된다.
플래그 필드(flag field)
: 6비트를 포함한다.ACK
비트 : 확인응답 필드에 있는 값이 유용함을 가리키는 데 사용된다.RST
,SYN
,FIN
비트 : 연결 설정과 해제에 사용된다.PSH
비트 : 이 비트가 설정되었다면 이것은 수신자가 데이터를 상위 계층에 즉시 전달해야 함을 가리킨다.URG
비트- 이 세그먼트에서 송신 측 상위 계층 개체가 ‘긴급’으로 표시하는 데이터임을 가리킨다.
- 이 긴급 데이터의 마지막 바이트의 위치는 16비트의
긴급 데이터 포인터 필드(urgent data pointer field)
에 의해 가리켜진다.
데이터 필드
애플리케이션 데이터의 일정량을 담는다.
MSS
MSS는 세그먼트 데이터 필드의 크기를 제한한다.
- 큰 파일 전송 시, 일반적으로 MSS 크기로 파일을 분절한다.
- 많은 대화식 애플리케이션은 MSS보다 작은 양의 데이터를 전송한다.
순서 번호와 확인응답 번호
이 두 필드는 TCP의 신뢰적인 데이터 전송 서비스의 중대한 부분이다.
순서 번호
TCP는 데이터를 구조화되어 있지 않고 단지 순서대로 정렬되어 있는 바이트 스트림으로 본다.
세그먼트에 대한 순서 번호는 세그먼트에 있는 첫 번째 바이트의 바이트 스트림 번호다.
e.g.,
- 데이터 스트림은 500,000 바이트로 구성된 파일이라고 가정한다.
- MSS는 1,000 바이트
- 데이터 스트림의 첫 번째 바이트는 0으로 설정한다.
아래 그림처럼 TCP는 데이터 스트림으로부터 500개의 세그먼트들을 구성하며, 각 세그먼트가 할당받는 순서 번호는 다음과 같다.
💡 각각의 순서 번호는 적절한 TCP 세그먼트의 헤더 내부의 순서 번호 필드에 삽입된다.
확인응답 번호
TCP는 전이중 방식임을 상기하자.
(호스트 A가 호스트 B로 데이터를 송신하는 동안에 호스트 B로부터 데이터를 수신하게 해줌)
호스트 B로부터 도착한 각 세그먼트는 B로부터 A로 들어온 데이터에 대한
순서 번호
를 갖는다.💡 호스트 A가 자신의 세그먼트에 삽입하는 확인응답 번호는 호스트 A가 호스트 B로부터 기대하는 다음 바이트의 순서 번호다.
e.g.,
호스트 A가 호스트 B로부터
0 ~ 535의 바이트를 포함하는 어떤 세그먼트
와 900 ~ 1,000의 바이트를 포함하는 또 다른 세그먼트
를 수신했다고 가정하자.(어떤 이유 때문인지 몰라도, 호스트 A는 그 사이
536~899의 바이트
를 아직 수신하지 않았음)호스트 A는 B의 데이터 스트림을 재생성하기 위해 536번째(와 그 다음의) 바이트를 아직 기다리고 있다.
그러므로 B에 대한 A의 다음 세그먼트이
확인응답 번호 필드
에 536을 가질 것이다.TCP는 스트림에서 첫 번째 잃어버린 바이트까지의 바이트들까지만 확인응답하기 때문에,TCP는누적 확인응답(cumulative acknowledgment)
을 제공한다고 한다.
위 예에서 호스트 A는
세 번째 세그먼트(900~1,000 값의 바이트)
를 두 번째 세그먼트(536~899 값의 바이트)
가 수신되기 전에 수신했다.즉, 세 번째 세그먼트는 순서가 틀리게 도착했다.
이 상황에서 호스트는 어떻게 행동을 할까?
TCP RFC는 TCP 연결에서 순서가 바뀐 세그먼트를 수신할 때 호스트가 어떤 행동을 취해야 하는지에 대한 어떤 규칙도 부여하지 않았고,TCP 구현 개발자에게 맡기고 있다.
두 가지 선택지
- 수신자가 순서가 바뀐 세그먼트를 즉시 버린다.
- 수신자는 순서가 바뀐 데이터를 보유하고, 빈 공간에 잃어버린 데이터를 채우기 위해 기다린다.
후자가 네트워크 대역폭 관점에서는 효율적이며, 실제에서도 취하는 방법이다.
텔넷: 순서 번호와 응답확인 번호 사례연구
텔넷(Telnet)
- 원격 로그인을 위해 사용되는 유명한 애플리케이션 계층 프로토콜
- TCP 상에서 실행되며, 한 쌍의 호스트들 사이에서 동작하도록 설계되었다.
호스트 A가 호스트 B로 텔넷 세션을 시작한다고 가정하자.
- 호스트 A가 세션을 시작하므로
클라이언트
- 호스트 B는
서버
💡 세그먼트의 순서 번호 = 데이터 필드 안에 있는 첫 번째 바이트의 순서 번호
- 클라이언트의 초기 순서 번호 :
42
→ 클라이언트에서 송신된 첫 번째 세그먼트는 순서 번호 42를 가진다.
- 서버의 초기 순서 번호 :
79
→ 서버에서 송신된 첫 번째 세그먼트는 순서 번호 79를 가질 것이다.
💡 확인응답 번호 = 호스트가 기다리는 데이터의 다음 바이트의 순서 번호
TCP 연결이 설정된 후에 어떤 데이터도 송신되기 전,
- 클라이언트는 바이트
79
를 기다리고 있다.
- 서버는 바이트
42
를 기다리고 있다.
사용자가 하나의 문자 ‘C’를 입력하고, 커피를 마신다고 가정하자.
위 그림처럼 3개의 세그먼트가 송신된다. =
세 방향 핸드셰이크(three-way handshake)
1️⃣ 첫 번째 세그먼트
- 클라이언트에서 서버로 송신된다.
순서 번호 필드
안에42
를 가진다.
2️⃣ 두 번째 세그먼트
- 서버에서 클라이언트로 송신된다.
- 두 가지 목적을 가진다.
- 수신하는 서버에게 데이터에 대한 확인응답을 제공
- 문자 ‘C’를 반대로 반향되도록 하는 것
확인응답 필드 안에 43을 넣음으로써,(1) 서버는 클라이언트에게 바이트 42를 성공적으로 수신했고(2) 앞으로 바이트 43을 기다린다는 것을 말해준다.
→ 그의 데이터 필드에 ‘C’의 ASCII 표현을 한다.
두 번째 세그먼트는 TCP 연결의 서버-클라이언트 데이터 흐름의 최초 순서 번호인 순서 79를 갖는다.(이는 서버가 보내는 데이터의 맨 첫번째 바이트)
💡 클라이언트/서버 데이터에 대한 확인응답은 서버와 클라이언트 간에서 데이터를 운반하는 세그먼트 안에서 전달된다.= 확인응답은 서버-클라이언트 데이터 세그먼트 상에서피기백된다(piggybacked)
3️⃣ 세 번째 세그먼트
- 클라이언트에서 서버로 송신된다.
- 목적 : 서버로부터 수신한 데이터에 대한 확인응답을 하는 것
- 이 세그먼트는 빈 데이터 필드를 갖는다.
- 즉, 확인응답은 어떤 클라이언트-서버 데이터와 함께 피기백되지 않는다.
- 세그먼트는
확인응답 필드
안에80
을 갖는다. - 클라이언트가
순서 번호 79
의 바이트를 통해 바이트의 스트림을 수신했기 때문이다. - 앞으로
80
으로 시작하는 바이트를 기다린다.
이 세그먼트가 데이터를 포함하지 않는데도 순서 번호를 갖는다는 것이 이상하지만,TCP가 순서 번호 필드를 갖고 있으므로 세그먼트 역시 어떤 순서 번호를 가져야 한다.
3.5.3 왕복 시간(RTT) 예측과 타임아웃
💡 TCP는 손실 세그먼트를 발견하기 위해 타임아웃/재전송 매커니즘을 사용한다.
타임아웃은 연결의
왕복 시간(round-trip time, RTT)
보다 좀 커야 한다.왕복 시간 예측
💡 왕복 시간(round-trip time, RTT) : 세그먼트가 전송된 시간부터 긍정 확인응답될 때까지의 시간
TCP가 송신자와 수신자 사이에 왕복 시간을 어떻게 예측하는지에 대하여 알아보자.
✅
SampleRTT
라고 표시되는 세그먼트에 대한 RTT 샘플 : 세그먼트가 송신된 시간(IP에게 넘겨진 시간)으로부터 그 세그먼트에 대한 긍정응답이 도착한 시간까지의 시간 길이대부분의 TCP는 한 번에 하나의
SampleRTT
측정만을 시행한다.- 즉, 어떤 시점에서
SampleRTT
는 전송되었지만 현재까지 확인응답이 없는 세그먼트 중 하나에 대해서만 측정된다.
- 이는 대략 왕복 시간마다
SampleRTT
의 새로운 값을 얻게 한다.
✅ SampleRTT 값은 라우터에서의 혼잡과 종단 시스템에서의 부하 변화 때문에 세그먼트마다 다르기 때문에
대체로 RTT를 추정하기 위해
SampleRTT 값의 평균값
을 채택한다.→ TCP는 SampleRTT 값의 평균(
EstimatedRTT
)을 유지한다.EstimatedRTT = (1 - α) × EstimatedRTT + α × SampleRTT(권장되는 α의 값 : 0.125)
💡 EstimatedRTT는 SampleRTT의 가중평균(weighted average)이다.
- 이 가중평균은 예전 샘플보다 최근 샘플에 높은 가중치를 준다.
- 최신 샘플들이 네트워크상의 현재 혼잡을 더 잘 반영한다. →
지수적 가중 이동 평균(exponential weighted moving average, EWMA)
아래는 TCP 연결에 대해
α = 1/8
의 값에 대한 SampleRTT
값들과 EstimatedRTT
를 보여준다.✅
DevRTT
는 RTT 변화율을 의미한다.이는
SampleRTT
가 EstimatedRTT
로부터 얼마나 많이 벗어나는지에 대한 측으로 정의한다.DevRTT = (1 - β) × DevRTT + β × | SampleRTT - EstimatedRTT |(권장되는 β의 값 : 0.25)
DevRTT는 SampleRTT와 EstimatedRTT 값 차이의 EWMA이다.
재전송 타임아웃 주기의 설정과 관리
주어진
EstimatedRTT
와 DevRTT
값에서, TCP 타임아웃 주기
에는 어떤 값이 사용되어야 하는가?- 주기는
EstimatedRTT
보다 크거나 같아야 한다.
(그렇지 않다면 불필요한 재전송이 보내질 것)
EstimatedRTT
보다 너무 크면 안된다.
(너무 크면 세그먼트를 잃었을 때, TCP는 세그먼트의 즉각적인 재전송을 하지 않게 됨)
→ 타임아웃값은
EstimatedRTT
에 약간의 여윳값을 더한 값으로 설정하는 것이 바람직하다.TimeoutInterval = EstimatedRTT + 4 × DevRTT
- 초기
TimeoutInterval
의 값으로는 1초를 권고한다.
- 타임아웃이 발생할 때,
TimeoutInterval
의 값은 두 배로 하여
조만간 확인응답할 후속 세그먼트에게 발생할 수 있는 조기 타임아웃을 피하도록 한다.
3.5.4 신뢰적인 데이터 전송
💡 TCP는 IP의 비신뢰적인 최선형 서비스에서 신뢰적인 데이터 전송 서비스(reliable data transfer service)를 제공한다.
- 프로세스가 자신의 수신 버퍼로부터 읽은 데이터 스트림이 손상되지 않음을 보장한다.
- 중복이 없다는 것과 순서가 유지된다는 것을 보장한다.
즉, 바이트 스트림은 송신자가 전송한 것과 같은 바이트 스트림이다.
타이머 관리는 상당한 오버헤드를 유발할 수 있다.
따라서 전송되었지만 확인응답이 안 된 다수의 세그먼트들이 있다고 하더라도,
권장되는 TCP 타이머 관리 절차에서는 오직
단일 재전송 타이머
를 사용한다.(이 장에서 설명하는 TCP 프로토콜은
단일 타이머
를 따름)TCP 송신자의 데이터 전송/재전송에 관련된 세 가지 주요 이벤트
1️⃣ 상위 애플리케이션으로부터 수신된 데이터
첫 번째 이벤트 발생으로,
- TCP는 애플리케이션으로부터 데이터를 받고,
세그먼트
로 이 데이터를 캡슐화하고,
- IP에게 이 세그먼트를 넘긴다.
- 각 세그먼트는 세그먼트의 첫 번째 데이터 바이트의 바이트 열 번호인
순서 번호
를 포함한다.
- 타이머가 이미 다른 세그먼트에 대해 실행 중이 아니면, TCP는 이 세그먼트를 IP로 넘길 때
타이머
를 시작한다.
2️⃣ 타이머 타임아웃
- TCP는 타임아웃 이벤트에 대해 타임아웃을 일으킨 세그먼트를 재전송하여 응답한다.
- 그리고 TCP의 타이머를 다시 시작한다.
3️⃣ 수신 확인응답 세그먼트(ACK) 수신
이 이벤트가 발생하면, TCP는 변수
SendBase
와 ACK 값 y
를 비교한다.SendBase
: 수신 확인응답이 확인되지 않은 / 가장 오래된 바이트의 순서번호
SendBase-1
: 수신자에게서 정확하게 차례대로 수신되었음을 알리는 마지막 바이트의 순서번호
TCP는 누적 확인응답을 사용하고, y는 y바이트 이전의 모든 바이트의 수신을 확인한다.
y > SendBase
이면, ACK는 이전에 확인응답 안 된 하나 이상의 세그먼트들을 확인해준다.- 송신자는 자신의
SendBase
변수를 갱신한다.
- 아직 확인응답 안 된 세그먼트들이 존재한다면 타이머를 다시 시작한다.
몇 가지 흥미로운 시나리오
TCP 프로토콜이 어떻게 작동하는지 몇 가지 간단한 시나리오를 통해 알아보자.
손실된 확인응답에 기인하는 재전송
호스트 A로부터 세그먼트가 호스트 B 측에서 수신되었음에도 B로부터 A로의
긍정 확인응답이 손실된다면
- 타임아웃이 발생한다.
- 호스트 A는 같은 세그먼트를 B에게 재전송한다.
- 호스트 B의 TCP는 재송신된 세그먼트의 바이트를 버린다.
세그먼트 100이 재전송되지 않는 경우
호스트 A가 연속해서 두 세그먼트를 전송한다.
호스트 A에서 타임아웃 이전에 어떠한 긍정 확인응답도 수신하지 못한다고 가정하자.
타임아웃 이벤트가 발생하면
- 호스트 A는
순서 번호 92
로 첫 번째 세그먼트를 재전송한다.
- 타이머를 다시 시작한다.
- 새로운 타임아웃 이전에 두 번째 세그먼트에 대한 ACK가 도착하는 한, 두 번째 세그먼트는 재전송을 하지 않을 것이다.
누적 확인응답은 첫 번째 세그먼트의 재전송을 방지한다
호스트 A가 연속해서 두 세그먼트를 전송한다.
첫 번째 세그먼트의 긍정 확인응답이 네트워크에서 분실되었지만,
첫 번째 세그먼트의 타임아웃 전에 호스트 A가 긍정 응답번호 120의
긍정 확인응답을 수신하면
- 호스트 A는 호스트 B가 119바이트까지 모든 데이터를 수신했음을 알게 된다.
- 그러므로 호스트 A는 두 세그먼트 중 어느 것도 재전송하지 않는다.
TCP는 GBN인가 SR인가?
💡 TCP 확인응답은 누적되고 올바르게 수신되지만, 순서가 잘못된 세그먼트는 수신자가 개별적으로 ACK를 받지 않는다.
TCP 송신자는 전송했지만 확인응답 안 된 바이트의 가장 작은 순서 번호, SendBase와 전송될 다음 바이트의 순서 번호, NextSeqNum을 유지해야 한다.
이런 관점에서 TCP는 GBN 형태의 프로토콜과 비슷해보이나, ’TCP에서는 올바르게 수신되었지만 순서가 바뀐 세그먼트들을 버퍼링한다’는 차이점이 존재한다.
e.g.,
패킷
n < N
에 대한 긍정 확인응답이 손실되었지만,나머지 N-1개의 긍정 확인 응답들은 타임아웃 전에 송신 측에 도달했다고 가정한다.
GBN
: 패킷 n뿐만 아니라, 연속적인 패킷 n+1, n+2, … , N 모두를 재전송한다.
TCP
- 세그먼트 n 하나만을 재전송
- 세그먼트 n에 대한 타임아웃 전에 세그먼트 n+1에 대한 긍정 확인 응답이 도착한다면 세그먼트를 재전송하지 않는다.
💡 TCP에서 수정제안된 선택적 확인응답(selective acknowledgment) : TCP 수신자가 마지막으로 올바로 수신된 ‘순서가 맞는’ 세그먼트에 대해 누적 확인응답을 하기보다는‘순서가 틀린’ 세그먼트에 대해 선택적으로 확인응답을 하게 한다.
이를 선택적 재전송과 결합했을 경우, SR 프로토콜과 매우 유사하다.
따라서 TCP의 오류 복구 메커니즘은 GBN과 SR 프로토콜의 혼합으로 분류하는 것이 적당하다.
빠른 재전송
타임아웃이 유발하는 재전송의 문제 : 타임아웃의 주기가 때때로 비교적 길다.
긴 타임아웃 주기는 종단 간의 지연을 증가시키지만,
다행히도 송신자는 종종
중복 ACK
에 의한 타임아웃이 일어나기 전에 패킷 손실을 발견한다.💡 중복 ACK(duplicate ACK) : 송신자가 이미 이전에 받은 확인응답에 대한 재확인응답 세그먼트 ACK
수신자가 중복 ACK를 보내는 이유
TCP는 부정 확인응답을 사용하지 않으므로, 수신자는 송신자에게 부정 확인 응답을 보낼 수 없다.
- TCP 수신자가 기다리는 다음 것보다 더 큰 순서 번호를 가진 세그먼트를 받았을 때, TCP 수신자는
손실 세그먼트
를 찾아낼 것이다.
- 수신자는 마지막으로 수신된 순차적인 바이트를 갖는 데이터를 그냥 다시 확인응답한다.즉,
중복 ACK
를 생성한다.
TCP ACK 생성 권고
이벤트 1️⃣기다리는 순서 번호를 가진 ‘순서가 맞는’ 세그먼트의 도착기다리는 순서 번호까지의 모든 데이터는 이미 확인응답된다.
TCP 수신자 동작 :
지연된 ACK
- 또 다른 ‘순서가 맞는’ 세그먼트의 도착을 위해 500 ms까지 기다린다.
- 만약 다음 ‘순서에 맞는’ 세그먼트가 이 기간에 도착하지 않으면, 그냥 ACK를 보낸다.
이벤트 2️⃣기다리는 순서 번호를 가진 ‘순서가 맞는’ 세그먼트의 도착ACK 전송을 기다리는 다른 하나의 ‘순서에 맞는’ 세그먼트가 있다.
TCP 수신자 동작 : 2개의 ‘순서가 맞는’ 세그먼트들을 ACK하기 위해,
하나의 누적된 ACK
를 즉시 보낸다.이벤트 3️⃣기다리는 것보다 높은 순서 번호를 가진 ‘순서가 바뀐’ 세그먼트의 도착 격자가 발견된다.
TCP 수신자 동작 : 순서 번호가 다음의 기다리는 바이트(즉, 격차의 최솟값)를 나타내는
중복 ACK
를 즉시 보낸다.이벤트 4️⃣수신 데이터에서 격차를 부분적으로 또는 모두 채우는 세그먼트의 도착
TCP 수신자 동작 : 그 세그먼트가 격차의 최솟값에서 시작한다고 하면, 즉시
ACK
를 보낸다.빠른 재전송(fast retransmit)
만약 TCP 송신자가 같은 데이터에 대해 3개의 중복 확인응답을 수신한다면,
이것은 ACK된 세그먼트의 다음 3개의 세그먼트가 분실되었음을 의미한다.
💡 3개의 중복 ACK를 수신할 때, TCP는 세그먼트의 타이머가 만료되기 이전에 손실 세그먼트를 재전송한다.
아래 그림을 보면 두 번째 세그먼트를 잃어버린 경우, 타이머가 만료되기 전에 재전송되었다.
3.5.5 흐름 제어
💡 TCP는 송신자가 수신자의 버퍼를 오버플로시키는 것을 방지하기 위해 애플리케이션에게 흐름 제어 서비스(flow-control service)를 제공한다.
→ 수신하는 애플리케이션이 읽는 속도와 송신자가 전송하는 속도를 같게 한다.
TCP 송신자는 IP 네트워크에서 혼잡 때문에 억제될 수도 있다. = 혼잡 제어(congestion control)
흐름 제어와 혼잡 제어는 명백히 각기 다른 목적으로 수행된다. (잘 구별하여야 함)
과정
💡 TCP는 수신 윈도(receive window)라는 변수를 유지하여 흐름 제어를 제공한다.
- 수신 측에서 가용한 버퍼 공간이 얼마나 되는지를 송신자에게 알려주는데 사용된다.
- TCP는
전이중(full-duplex)
이므로 연결의 각 측의 송신자는 별개의 수신 윈도를 유지한다.
e.g., TCP 연결상에서 호스트 A가 호스트 B에게 큰 파일을 전송한다고 가정
- 호스트 B는 이 연결에 수신 버퍼를 할당한다.
(이때 할당된 수신 버퍼의 크기 :
RcvBuffer
)- 호스트 B의 애플리케이션 프로세스는 버퍼로부터 데이터를 읽으며 다음과 같은 변수들을 정의한다.
LastByteRead
: 호스트 B의 애플리케이션 프로세스에 의해 버퍼로부터 읽힌 데이터 스트림의 마지막 바이트 번호LastByteRcvd
: 호스트 B에서 네트워크로부터 도착하여 수신 버퍼에 저장된 데이터 스트림의 마지막 바이트 번호rwnd
- 수신 윈도 = 버퍼의 여유 공간
- 시간에 따라 여유 공간은 변하므로 이 변수는 동적이다.
LastByteRcvd - LastByteRead ≤ RcvBuffer
rwnd = RcvBuffer - [LastByteRcvd - LastByteRead]
- 호스트 B는 호스트 B가 호스트 A에게 전송하는 모든 세그먼트의 윈도 필드에 현재
rwnd
값을 설정한다.
이를 통해 호스트 A에게 연결 버퍼에 얼마만큼의 여유 공간이 있는지를 알려준다.
- 호스트 A는 두 변수
LastByteSent
와LastByteAcked
를 유지한다.
LastByteSent - LastByteAcked = 호스트 A가 이 연결에 전송 확인응답이 안 된 데이터의 양
💡 rwnd의 값보다 작은 확인응답 안 된 데이터의 양을 유지함으로써 호스트 A는 호스트 B의 수신 버퍼에 오버플로가 발생하지 않는다는 것을 확신한다.
호스트 A는 연결된 동안 다음의 내용을 보장한다.
LastBySent - LastByteAcked ≤ rwnd
문제점과 해결법
- 호스트 B의 수신 버퍼는
rwnd = 0
으로서 가득 찼고
- 호스트 A에게 rwnd = 0이라고 알린 후 호스트 B는 호스트 A에게 전송할 것이 없는 경우
호스트 B에서의 애플리케이션 프로세스가 버퍼를 비우더라도,TCP는 호스트 A에게 새로운 rwnd로 새로운 세그먼트를 전송하지 않는다.
즉, TCP는 전송할 데이터가 있거나, 전송해야 할 확인응답을 가진 경우에만 호스트 A에게 세그먼트를 전송할 것이다.
→ 호스트 A는 차단되고 더는 데이터를 전송할 수 없다.
따라서 TCP 명세서는 호스트 A가 호스트 B의 수신 윈도가 0일 때, 1바이트 데이터로 세그먼틀르 계속해서 전송하도록 요구한다.
3.5.6 TCP 연결 관리
TCP 연결이 어떻게 설정되고 해제되는가?
하나의 호스트(클라이언트)에서 운영되는 프로세스가 다른 호스트(서버) 안의 또 다른 프로세스와 연결을 시작하길 원한다고 가정하자.
- 클라이언트 애플리케이션 프로세스는 서버에 있는 프로세스와 연결 설정하기를 원한다는 것을 클라이언트 TCP에게 알린다.
- 클라이언트 안의 TCP는 다음과 같은 방법으로 TCP를 이용해 서버와 TCP 연결 설정을 시작한다.
연결의 설정 : 세 방향 핸드셰이크(three-way handshake)
1단계
- 클라이언트 측 TCP는 서버 TCP에게 특별한 TCP 세그먼트,
SYN 세그먼트
를 송신한다. - 애플리케이션 계층 데이터를 포함하지 않는다.
- 세그먼트 헤더에
SYN 비트
를 1로 설정한다.
- 클라이언트는 최소 순서 번호(
client_isn
)를 임의로 선택하고, 최초의 TCP SYN 세그먼트의순서 번호 필드
에 이 번호를 넣는다.
- 이 세그먼트는 IP 데이터그램 안에서 캡슐화되고 서버로 송신된다.
2단계
TCP SYN 세그먼트를 포함하는
IP 데이터그램
이 서버 호스트에 도착하면,- 서버는 데이터그램으로부터 TCP SYN 세그먼트를 추출한다.
- 연결에 TCP 버퍼와 변수를 할당한다.
- 클라이언트 TCP로 연결 승인 세그먼트,
SYNACK 세그먼트
를 송신한다. - 애플리케이션 계층 데이터를 포함하지 않는다.
SYN 비트
는 1로 설정된다.- TCP 세그먼트 헤더의
확인응답 필드
는client_isn+1
로 설정된다. - 서버는 자신의 최초의 순서 번호(
server_isn
)를 선택하고, TCP 세그먼트 헤더의순서 번호 필드
에 이 값을 넣는다.
3단계
연결 승인 세그먼트를 수신하면,
- 클라이언트는 연결에 버퍼와 변수를 할당한다.
- 클라이언트 호스트는 서버로 또 다른 세그먼트를 송신한다.
- 클라이언트는 TCP 세그먼트 헤더의
확인응답 필드
안에server_isn+1
값을 넣어, 서버의 연결 승인 세그먼트를 확인한다. - 연결이 설정되었기 때문에
SYN 비트
는 0으로 설정된다.
세 번째 단계는 클라이언트에서 서버로의 데이터를 세그먼트 페이로드에서 운반할 수 있다.
위의 세 단계가 완료되면,
- 클라이언트와 서버 호스트들은 각각 서로에게
데이터를 포함하는 세그먼트
를 보낼 수 있다.
SYN 비트
는 0으로 설정된다.
연결의 종료
TCP 연결에 참여하는 두 프로세스 중 하나가 연결을 끊을 수 있다.
연결이 끝날 때, 호스트의 ‘자원’(버퍼와 변수)는 회수된다.
- 클라이언트 애플리케이션 프로세스는 종료 명령을 내린다.
- 이는 클라이언트 TCP가 서버 프로세스에게
특별한 TCP 세그먼트
를 보내도록 한다.
(
FIN 비트
를 1로 설정)- 서버가 이 세그먼트를 수신하면, 서버는 클라이언트에게
확인응답 세그먼트
를 보낸다.
- 그 다음에
FIN 비트
가 1로 설정된 자신의종료 세그먼트를 송신
한다.
- 마지막으로 클라이언트는 서버의 종료 세그먼트에
확인응답
을 한다.
이 시점에서 두 호스트의 모든 자원은 할당이 해제된다.
TCP 상태(TCP state)
TCP 연결이 존재하는 동안 각 호스트에서 동작하는 TCP 프로토콜은 다양한 TCP 상태를 두루 전이한다.
아래의 두 그림은 클라이언트가 연결 해제를 시작한다는 것을 가정한다.
클라이언트 TCP에서 TCP 상태 변이의 일반 순서
서버 TCP에서 TCP 상태 변이의 일반 순서
SYN 플러드 공격
TCP의 세 방향 핸드셰이크
- 서버는
수신된 SYN
에 대한 응답으로 연결 변수와 버퍼를 할당하고 초기화한다.
- 그 다음, 서버는 응답으로
SYNACK
을 보내고 클라이언트의ACK 세그먼트
를 기다린다.
클라이언트가 이 세 방향 핸드셰이크의 세 번째 단계를 완료하기 위한 ACK를 보내지 않으면결국(종종 1분 이상 후에) 서버가 절반만 열린 연결을 종료하고 할당된 자원을 회수한다.
→ 이는
SYN 플러드 공격
의 무대가 된다.SYN 플러드 공격(SYN flood attack)
고전적인
서비스 거부(Denial of Service, DoS) 공격
- 공격자는 핸드셰이크의 세 버너째 단계를 완료하지 않은 상태에서 무수한
TCP SYN 세그먼트
를 보낸다.
- 서버의 연결 자원이 반쪽 연결에 할당된다.
- 결국 서버의 연결 자원이 소진됨에 따라 합법적인 클라이언트들이 서비스 거부가 된다.
SYN 쿠키
이는 SYN 플러드 공격에 대한 방어책으로, 현재 대부분의 운영체제에 존재하고 있다.
동작 방법
- 서버는 SYN에 대해 반만 열린(half-open) TCP 연결을 만들지 않고,
초기 TCP 순서 번호(쿠키, cookie)
를 만든다. - 서버가 SYN 세그먼트를 받을 때, 그 세그먼트가 정당한 사용자로부터 또는 공격자로부터 온 것인지 구별할 수 없기 때문이다.
- 해시 함수에 아래의 항목들을 사용하여 쿠키를 생성한다.
- 비밀번호
- SYN 세그먼트의 출발지와 목적지 IP 주소들과 포트번호
- 서버는 이 특별한 초기 순서 번호를 가진
SYNACK
패킷을 보낸다.
💡 서버는 SYN에 관련된 쿠키나 어떤 다른 상태 정보를 기억하지 않는다.
- 합법적인 클라이언트는
ACK
세그먼트를 회신한다.
- 이 ACK를 받은 서버는 ACK가 이전에 보낸 일부 SYN에 관한 것인지 확인해야 한다.
- 이는 이전에 보낸 일부 SYN에 관한 것인지는
쿠키
를 통해 확인한다. - 서버는 SYNACK에 있는 출발지와 목적지 IP 주소와 포트번호, 비밀번호를 사용해서 동일한 해시 함수를 실행한다.
만약 함수의 결과에 1을 더한 것이 클라이언트의 SYNACK에 있는 확인응답 번호(쿠키)와 같다면
서버는 ACK가 초기 SYN 세그먼트에 관련된 것, 즉 올바른 것이라고 결론짓는다.
→ 이후 서버는 소켓을 가지고 완전하게 열린 연결을 만든다.
만약 클라이언트가 ACK 세그먼트를 회신하지 않으면
서버가 처음의 가짜 SYN에 대해 어떤 자원도 할당하지 않았기 때문에 처음의 SYN은 서버에 해를 끼치지 못한다.