TCP : 연결 지향형 프로토콜로, 신뢰성 있는 데이터 전송을 보장한다. TCP는 데이터 전송의 신뢰성을 보장하기 위해 다음 세 단계를 거쳐 통신을 진행한다.
Connection establishment (연결 설정)
Data transfer (데이터 전송)
Connection teardown (연결 종료)
Stream Delivery : TCP의 데이터 전송은 Stream of bytes 형태를 띄고 있다.

각 패킷마다 순서가 존재하여 논리적 연결이 존재한다. 즉, 하나의 데이터 스트림을 이룬다.
데이터를 여러 패킷(프래그먼트)으로 나누어 전송한 뒤 수신측에서 이를 재조립한다.
TCP Segment : 네트워크 환경마다 전송할 수 있는 데이터의 크기가 다르므로, 데이터를 여러 조각의 Segment로 나눠 전송하는 방식은 매우 중요하다.
Segment Format

Window in TCP : TCP 환경의 Window는 Flow Control, Congestion Control, Error Control을 구현하는 핵심적인 역할을 한다.
Send Window
: 전송했지만 ACK 메시지를 받지 못한 첫 번째 패킷(바이트)이다. ACK를 수신하면 이 경계는 오른쪽으로 이동(Sliding)하며, 해당 데이터는 버퍼에서 제거된다.
: 다음으로 전송할 바이트(메시지)이며, 수신자가 허용한 윈도우만큼 전송이 가능하다.
파란색 영역은 ACK 메시지를 받지 못한 패킷들의 영역, 흰색 영역은 전송 가능한 패킷들의 영역이다.
Shrinking : 네트워크 혼잡이나 수신자의 처리 용량 부족 등으로 인해 윈도우의 크기가 작아질 수 있다.
ACK 메시지를 받으면 이미 전송한 데이터에 대해서는 버퍼가 Closing, 전송할 데이터에 대해서는 버퍼가 Opening 된다.
Receive Window
파란색 영역은 ACK 메시지를 전송하였지만, 아직 프로세스에 의해 처리되지 않은 패킷들의 영역, 흰색 영역은 패킷을 수용할 수 있는 버퍼 영역이다.
송신자가 전송할 수 있는 패킷의 양은 수신자의 버퍼 크기(Window size)에 따라 결정된다.
: 다음 패킷을 수신할 것으로 예상되는 버퍼 공간이다.
이미 ACK를 보낸 패킷들은 프로세스에 의해 처리되면 Closing되며, Closing되는 만큼 버퍼가 Opening된다.
TCP Connection : TCP환경에서 메시지(모든 세그먼트)는 송신자와 수신자 사이에 연결 설정된 논리적 경로를 통해 전송된다.
2-way handshake scenario : TCP에서 2-way handshake로 연결 설정이 이루어지는 과정을 살펴 보자.
첫 번째 시나리오 : 문제 없음
두 번째 시나리오 : 연결 설정을 요청하는 메시지에 대해 응답 메시지(ACK)가 너무 늦게 도착해서 연결 설정이 실패
세 번째 시나리오 : 이전에 보낸 메시지가 다음 연결에 영향을 미치는 경우
3-way handshake : TCP는 2-way handshake에서 발생할 수 있는 문제를 방지하기 위해 3-way handshake 방식을 사용하여 연결을 설정한다.
1) SYN (클라이언트 서버)
클라이언트는 연결을 요청하기 위해 SYN 세그먼트를 서버로 전송한다.
seq = 8000 : 세그먼트 시퀀스 번호
SYN : SYN flag
2) SYN + ACK (서버 클라이언트)
서버는 항상 열려있는(Passive open) 상태에서 클라이언트의 요청을 대기한다.
SYN 요청을 받은 서버는 연결 요청에 대한 응답 메시지 SYN + ACK가 설정된 세그먼트를 클라이언트로 전송한다.
seq = 15000 : 세그먼트의 시퀀스 번호
ack = 8001 : 클라이언트의 시퀀스 번호에 대한 응답 번호
rwnd(receive window) = 5000 : 서버에서 수용 가능한 용량(바이트)
SYN + ACK : SYN + ACK flag
3) ACK (클라이언트 서버)
클라이언트는 서버의 응답에 대해 ACK 플래그가 설정된 세그먼트를 전송한다.
seq = 8001 : 세그먼트 시퀀스 번호
ack = 15001 : 상대의 시퀀스 번호에 대한 응답 번호
rwnd = 10000 : 클라이언트에서 받을 수 있는 바이트
4) Data Transfer : 연결이 설정되었고, 데이터 전송이 시작된다.
5) Terminate : 데이터 전송이 끝나면, 3-way handshake 방식으로 연결이 종료된다.
FIN (클라이언트 서버)
FIN + ACK (서버 클라이언트)
클라이언트 : 연결이 끊어짐
서버 : 아직 연결되어 있음
ACK (클라이언트 서버)
클라이언트 : 연결이 끊어진 상태
서버 : 연결 끊어짐
Half Close : 한쪽의 TCP 연결이 종료되더라도, 반대편 한쪽은 여전히 연결되어 있는 상태를 유지한다. TCP 환경에서는 양쪽의 연결을 안전하게 종료하기 위해 이러한 방식을 사용한다.
FIN (클라이언트 서버) : 클라이언트가 연결을 종료하기 위해 보내는 flag이다.
FIN + ACK (서버 클라이언트) : 서버는 FIN 메시지에 대해 응답을 보낸다.
응답 메시지를 받은 클라이언트는 연결을 종료한다.
서버는 여전히 연결을 유지한다.
Passive close : 서버가 보내야 할 잔여 데이터가 존재하는 등 안전한 종료를 위해 일정 시간 대기한 후, 서버가 클라이언트에게 종료 메시지를 보낸다.
FIN (서버 클라이언트) : 서버가 연결 종료 신호를 클라이언트에게 보낸다.
ACK (클라이언트 서버) : 클라이언트는 FIN 메시지에 대한 응답을 보낸다.
TCP는 위와 같은 메커니즘으로 안전한 연결 종료를 지원한다.
2MSL Timer : 안전한 연결 종료를 지원하기 위해 일정 시간 고의적으로 대기 상태를 유지하도록 하는 타이머이다.
MSL(Maximum Segment Lifetime) : TCP 세그먼트가 네트워크 상에 존재하는 최대 시간이다.
지연 패킷이 뒤늦게 도착한다던가, 잔여 데이터가 남아있다던가 등의 이유로 인해 도착할 데이터가 남아있음에도 연결이 종료되는 상황을 방지하기 위해 활용된다.
Flow Control in TCP (흐름 제어) : TCP의 주요 기능 중 하나로, 생산자의 데이터 생성 속도와 소비자의 데이터 처리 속도 간 균형을 맞추는 메커니즘이다.
Control flow and feedback
생산자는 데이터를 Push한다.
소비자는 데이터를 처리하고, 필요하다면 흐름 제어 신호를 생산자에게 전달한다.
생산자는 feedback을 통해 전송하는 데이터의 양을 조절한다.
흐름 제어 예시
수신자는 ACK메시지에 자신이 수용 가능한 버퍼의 크기(Receive Window size)를 기록한다.
송신자는 수신자가 보낸 Receive Window size에 알맞게 자신의 Send Window size를 조절하며, 데이터를 전송한다.
Error control in TCP : TCP의 오류 제어는 데이터 전송 중 발생할 수 있는 오류를 탐지하고 수정하는 기능을 제공한다. 이는 데이터의 무결성을 보장한다.
동작 예시
Normal Operation
Rule 1 : 클라이언트가 더이상 보낼 데이터가 없을 경우, ACK 메시지만 전송한다.
Rule 2 : ACK delay-timer는 데이터 수신 후 즉시 ACK를 보내지 않고, 일정 시간동안 전송을 지연시킨다. time-out이 발생할 경우 ACK를 전송한다.
Rule 3 : time-out 발생 전에 새로운 세그먼트를 수신하면, 누적 ACK를 전송하고 타이머를 다시 시작한다.
Case : Lost Segment
전송 과정에서 한 세그먼트(seq : 701 - 800)가 손실되고, 다음 세그먼트(seq : 801 - 900)가 전송된다.
Rule 4 : 받을 것이라고 예상한 순서의 세그먼트가 도착하지 않았기 때문에, 받아야 할 순서의 세그먼트를 알리는 ACK를 전송한다.
time-out(Retransmission time-out, RTO)이 발생하고, 해당 ACK를 받은 클라이언트는 손실된 세그먼트를 재전송한다.
Rule 5 : 3번째 세그먼트를 정상적으로 받았기 때문에, 다음 순서 세그먼트를 담은 ACK를 클라이언트에 전송하여 정상적으로 받았음을 알린다.
Case : Fast retransmission
TCP Fast retransmission : 전송한 프래그먼트에 대해 동일한 ACK 메시지를 3번 수신하면, 즉시 해당 세그먼트를 재전송한다.Case : Lost ACK
Rule 6 : 수신자가 중복 세그먼트를 수신할 경우, 중복 세그먼트를 폐기하며 마지막으로 보낸 ACK를 즉시 재전송하여 이미 수신한 세그먼트임을 알린다.Congestion control in TCP : TCP는 네트워크 상태를 고려하여 전송하는 데이터의 양을 조절하는 메커니즘을 통해 혼잡을 조절한다.
Slow start (Exponential increase)
현재 네트워크 상태를 정확히 알지 못하므로 cwnd(혼잡 윈도우)의 초기 size를 1로 설정한다.
ACK를 수신할 때마다 cwnd의 size를 2배씩 증가시킨다.
각 RTT마다 전송 가능한 세그먼트의 양이 지수적(Exponential increase)로 증가한다.
Congestion Avoidance (Additive increase)
cwnd이 혼잡 임계값(ssthresh) 근처에 도달하면, TCP는 느린 시작에서 혼잡 회피로 전환하여 동작한다.
이때부터 전송되는 지수적으로 증가하던 데이터의 양(size)이 선형적으로 증가(Additive increase)한다.
TCP는 위와 같이 네트워크가 혼잡에 가까워질수록 cwnd를 섬세하게 조정하는 메커니즘을 통해 혼잡을 제어한다.
TCP Tahoe : Tahoe 방식 혼잡 제어를 살펴보자.
초기 설정
혼잡 임계값(ssthresh)를 16 MSS로 설정
느린 시작 단계에서 시작하며, cwnd는 1 MSS로 초기화된다.
느린 시작 단계
cwnd가 지수적(2배씩)으로 증가
cwnd가 3 RTT에서 Time-out이 발생
TCP는 네트워크에 혼잡이 있다고 판단하며, ssthresh를 현재 cwnd size의 절반으로 설정(4 MSS)
혼잡 윈도우의 크기를 다시 1 MSS로 초기화하고 느린 시작 상태로 복귀
cwnd가 다시 지수적으로 증가
혼잡 회피 단계
cwnd가 ssthresh(4 MSS)에 도달하였기 때문에, 혼잡 회피로 전환
cwnd는 선형적(1씩)으로 증가
3dupACKs
RTT 13에서 3개의 중복 ACK를 수신함 Fast Retransmission
ssthresh를 현재 cwnd의 절반으로 설정(6 MSS)
cwnd를 다시 1로 초기화하고, 느린 시작 상태로 복귀
TCP Tahoe는 위와 같은 방식으로 느린 시작과 혼잡 회피를 오가며 혼잡을 제어한다.
TCP Reno : Reno 방식의 혼잡 제어를 살펴보자.
3dupACKs 이전까지는 Tahoe와 동일하게 동작한다.
3dupACKs
RTT 13에서 3개의 중복 ACK를 수신하였다.
ssthresh는 현재 cwnd의 절반인 6 MSS로 감소한다.
cwnd size는 ssthresh + 3 MSS로 설정되어 빠른 복구(Fast Recovery) 단계로 전환된다.
Fast Recovery
중복 ACK를 수신했던 ssthresh까지 cwnd size를 지수적으로 증가시킨다.
새로운 ACK를 수신하였기 때문에 빠른 복구 상태는 종료되며, cwnd size가 ssthresh(6 MSS)로 감소한다.
혼잡 회피 단계로 전환한다.
Tahoe 방식에 비해 혼잡 구간에 가까운 구간에서 데이터를 전송하는 시간이 길다. 따라서 그만큼 네트워크 활용률이 높다.
Additive increase, Multiplicative decrease(AIMD) : 네트워크 혼잡 구간까지 cwnd size를 선형적으로 증가시킨 뒤, 혼잡 구간에 도달하였을 때 cwnd size를 절반으로 감소시키는 방식으로 혼잡을 제어하는 메커니즘이다.
Wmax : 혼잡 손실이 발생한 순간의 전송 속도의 평균이다.
TCP Throughput : 0.75 * Wmax / RTT
TCP Throughput의 계산 결과에서 알 수 있듯이, AIMD는 혼잡 구간에 근접(75%)한 구간에서 데이터 전송을 하도록 유도하는 메커니즘이다.
TCP CUBIC : AIMD 기반의 TCP 혼잡 제어 방식의 한계를 개선하기 위해 제안된 알고리즘이다.
AIMD : 전송 속도를 혼잡 구간까지 선형적으로 증가시키며, 혼잡 발생 시 이를 절반으로 줄이며 혼잡을 제어한다.
CUBIC
네트워크의 병목 구간의 혼잡 상태는 크게 변하지 않았을 가능성이 높다. 이는 곧 Wmax 이전의 전송 속도를 유지하면 혼잡이 발생할 가능성이 낮다는 것을 의미한다.
혼잡 손실 발생 시, cwnd size를 Wmax/2로 설정한다.
초기 가속 (Ramp up faster) : 감소 초반에는 cwnd size를 매우 빠르게 증가시켜 Wmax에 빠르게 근접하도록 한다.
점진적 접근 (Approach more slowly) : Wmax에 가까워질수록 매우 천천히 증가하도록 전송 속도를 조절한다.
마치 3차 함수와 유사한 형태를 띄게된다. 이는 선형적 증가 방식보다 효율적으로 cwnd size를 조절할 수 있다.
TCP CUBIC은 위와 같은 방식으로 Wmax에 근접한 구간에서 데이터를 전송할 수 있는 시간을 늘려 효율적으로 대역폭을 사용한다. 현재 가장 널리 사용되는 혼잡 제어 방식이다.
TCP CUBIC vs Reno
K : cwnd가 Wmax에 도달하는 순간의 시간을 나타낸다.
CUBIC : K에 먼 구간에선 빠르게 윈도우 크기가 증가하고, K에 가까워질수록 윈도우 크기의 증가폭이 감소한다.
Reno : K구간에 대한 고려 없이 선형적으로 윈도우 크기를 증가시킨다.
TCP는 통신의 신뢰성과 데이터 전송의 무결성을 보장하기 위해 4가지 주요 타이머를 활용한다. 각 Timer에 대해 자세히 살펴보자.
TCP Round Trip Time (RTT) : 송신측에서 전송한 패킷이 수신측에 도달하고, 수신측이 전송한 ACK메시지가 송신측에 도달하는 데 걸리는 시간이다.
Tiemout은 RTT를 고려하여 적당한 값으로 설정해야 한다.
Timeout을 너무 짧게 설정한 경우 (Timeout < RTT) : 오류가 발생하지 않았음에도 불필요한 재전송이 자주 발생한다. 이는 네트워크 혼잡을 야기할 수 있다.
Timeout을 너무 길게 설정한 경우 (Timeout > RTT) : 패킷 손실에 대한 처리까지 걸리는 시간이 너무 길기 때문에, 대역폭을 효율적으로 사용하지 못하게 된다.
RTT 측정(추정) 방법
SampleRTT : 세그먼트를 전송한 시점부터 ACK를 받을 때까지 걸린 시간을 측정한 값이다.
SampleRTT는 네트워크 상태에 따라 조금씩 오차가 발생할 수 있으므로, 안정적인 RTT 측정을 위해 여러 SampleRTT의 평균값을 활용한다.
Estimate RTT : RTT는 지수 가중 이동 평균(Exponential Weighted Moving Average, EWMA) 방식으로 측정된다.
: 가중치, 일반적으로 0.125로 설정
EstimatedRTT : 현재 까지의 RTT 측정값
SampleRTT : 가장 최근 샘플의 RTT 값

Timeout Interval : 데이터 재전송을 위해 설정하는 timeout 값으로, EstimatedRTT에 Safety margin을 추가한 값으로 설정한다.
Timeout Interval은 항상 EstimatedRTT보다 크거나 같아야한다.
네트워크 상태에 따라 변화하는 RTT의 특성을 고려하여, 안전 마진을 설정한다.
DevRTT : 안전 마진은 다음과 같이 계산된다.
는 가중치이며, 일반적으로 0.25로 설정한다.
RTT의 변동성을 측정하기 위해 SampleRTT와 EstimatedRTT간의 차이를 반영한다.
RTT를 측정하는 방식과 유사한 방식으로 계산된다.
Timeout 계산 예제
SYN 세그먼트 전송
RTT, SRTT(Smoothed RTT), RTTVAR(RTT Variance) 값이 아직 측정되지 않음
초기 RTO(Retransmission Timeout)을 기본값인 6.00초로 설정
SYN + ACK 도착
RTT : 1.5초로 측정됨
SRTT : 첫 번째 측정 값이므로, RTT와 동일하게 설정
RTTVAR :
RTO :
데이터 전송 및 ACK 수신
RTT : 2.5초로 측정
SRTT :
RTTVAR :
RTO :
TCP 재전송 예제 (Karn's Algorithm)
세그먼트가 전송되었지만 손실
RTO 만료 후 해당 세그먼트가 재전송 됨
재전송 후 RTO가 2배로 증가하여 9.48초가 됨
재전송 패킷에 대한 ACK를 수신하였지만, Karn's Algorithm에 따라 RTO 재계산을 생략함.
Karn's Algorithm은 재전송된 패킷의 RTT 측정을 제외함으로써, 잘못된 RTT를 측정을 방지한다.
재전송된 패킷의 ACK를 통해 측정된 RTT는 일반적인 RTT와 오차가 클 가능성이 높기 때문이다.
새로운 세그먼트 전송 후 RTO 계산
Persistent Timer : zero-window-size로 인해 발생할 수 있는 Deadlock을 해결하기 위해 사용되는 타이머이다.
Deadlock : 수신 측이 rwnd(수신 윈도우) size를 0으로 설정하여 송신측이 더이상 데이터를 보내지 않는 상황
수신측이 다시 윈도우 크기를 증가시키더라도, 수신측이 이를 알릴 방법이 없다.
송신측은 이를 감지하지 못하기 때문에 양측 모두 대기하는 Deadlock에 빠진다.
Persistent Timer : 송신측이 rwnd size = 0인 ACK를 수신하면, persistent timer를 설정한다.
타이머가 만료될 경우, 송신측은 Probe Segment를 전송한다.
Probe Segment : 1바이트의 새 데이터를 포함하고 있으며, 수신측 상태를 확인하기 위한 세그먼트이다.
수신측은 Probe Segment에 대한 ACK를 전송하며, 현재 업데이트된 윈도우 크기를 송신측에 알린다.
만약 수신측의 버퍼가 비워져 rwnd가 0이 아니라면, 송신측은 데이터를 다시 전송한다.
Keep-Aliver Timer : 클라이언트와 서버가 연결된 후, 오랫동안 비활성 상태(Idle)가 유지되는 것을 방지하기 위해 사용되는 타이머
클라이언트와 서버가 TCP Connection이 되어있는데, 침묵 상태가 유지될 경우
혹은 클라이언트가 비정상적으로 종료되었지만, 서버가 이를 감지하지 못하는 경우
Timer 동작
서버는 클라이언트로부터 데이터를 수신할 때마다 Timer를 재설정(보통 2시간)한다.
Timeout이 발생하면, 서버는 클라이언트에게 Probe Segment를 전송한다.
클라이언트가 응답할 경우, 연결은 유지된다.
응답이 없을 경우, 서버는 Probe Segment를 75초 간격으로 10번 전송한다.
여전히 응답이 없을 경우, 연결을 종료한다.
Time-Wait Timer : 두 번째 FIN 메시지를 수신하였을 때, 연결을 종료하면서 Time-Wait Timer를 설정한다.
TCP 연결이 종료된 후에도 지연된 패킷이 도착할 수도 있다.
새로운 연결이 이전 연결과 동일한 포트를 사용할 경우, 지연 도착한 패킷들이 다음 연결에 영향을 미칠 수도 있다.
위와 같은 상황들을 방지하기 위해 Time-Wait Timer를 사용하며, 일반적으로 2MSL(Maximum Segment Lifetime)으로 설정한다.