TCP에 대해 알기 전에 TCP에서 제공하는 신뢰성있는 데이터 전송 원리에 대해 알아보자

신뢰성있는 데이터 전송이 복잡한 이유는 네트워크 레이어 같이 unreliable한 채널 위에서 신뢰성있는 데이터 전송을 구현해야 하기 때문에 어렵다.
송신자는 메세지가 잘 갔는지 확인 할 수 없다.

rdt : 신뢰적인 데이터 전송(reliable data transfer) 프로토콜을 나타낸다. rdt_s
가정) 송신 -> 수신으로의 단방향 데이터 전송을 가정한다.
하지만 단방향 데이터 전송만 생각하더라도 프로토콜의 송신 측과 수신 측이 양방향으로 패킷을 전달할 필요가 있다.
(송수신 측은 전송 데이터를 포함하는 패킷을 교환외에 **`제어 패킷`을 양쪽으로 전송한다.**)
rdt의 송신 측과 수신 측 모두 udt_send()를 호출함으로써 다른 쪽에 패킷을 전송한다.
udt : 비신뢰적인 데이터 전송(unreliable data transfer)을 나타냄
rdt 1.0이란?
1. data 전송시 에러 X
2. data 전송시 loss X 인 상황

이런 단순한 상황에서는 위와 같이 송수신자의 FSM모델이 분리되어 있다.
rdt 2.0이란?
1. data 전송시 에러 발생 가능
2. data 전송시 loss X 인 상황
3.ACK/NAK패킷 손상 X
4.전송 후 대기(stop-and-wait): 데이터를 주고 수신자의 피드백을 기다림
그렇다면 에러가 발생했을 때 수신자가 송신자에게 에러 발생을 알려야 오류를 처리할 수 있다.
오류의 검출은 checksum을 통해 발견한다.
수신자 피드백 (1 bit 표현 0/1)
ACK : 긍정 확인응답(positive acknowledgment, “OK”)
NCK : 부정 확인응답(negative acknowledgment, “그것을 반복해주세요”)
-> retransmission

왼쪽 상태 - 어플에서 데이터를 주기를 기다림
rdt_sent(data) 이벤트가 발생
1) 패킷을 만들고 수신자에 보냄
2) 오른쪽 상태로 전이
오른쪽 상태 - 수신자의 ACK 또는 NAK 패킷을 기다린다.
1) ACK - 왼쪽상태 전이
2) NAK - retransmission
이렇게 하면 송신자는 데이터가 잘 갔는지 확인할 수 있게 된다.

단일 상태를 갖는다.
패킷이 도착했을 때, 수신자는 수신된 패킷이 손상되었는지 아닌지에 따라 ACK 또는 NAK로 응답한다.
ACK/NAK 패킷이 손상될 수 있다만약 ACK 또는 NAK가 손상되었다면,
송신자는 수신자가 전송된 데이터의 마지막 부분을 올바르게 수신했는지를 알 방법이 없다.
단순히 재전송을 하기 때문에
수신자 입장에서는 ACK를 보냈는데 상대가 ACK를 못받아 재전송을 하는데,
도착하는 패킷이 새로운 데이터를 포함하고 있는 것인지 아니면 재전송인지를 알 수 없다.
rdt 2.1이란?
1. data 전송시 에러 발생 가능
2. data 전송시 loss X 인 상황
3.ACK/NAK패킷 손상 가능(loss는 아님)
4.전송 후 대기(stop-and-wait): 데이터를 주고 수신자의 피드백을 기다림
송신자가
시퀀스 넘버를 패킷에 붇이는 것으로 해결할 수 있다.
ACK - 다음번에 보내는 새 패킷은 시퀀스 가 이전과 달라짐NCK를 받아 다시 보내야 할 떄 - 같은 시퀀스의 패킷을 보냄

다른 시퀀스가 오면 (Wait for 1 상태에서 0 패킷이 수신되면)
이는 중복된 패킷이거나 잘못된 순서로 패킷이 도착한 것을 의미한다.
이 경우 프로토콜은 중복 데이터를 다시 처리하지 않고, 마지막으로 정상적으로 수신된 패킷에 대한 ACK(확인 응답)를 다시 보낸다.
Wait for 1 상태는 시퀀스 번호가 1인 패킷을 기다리는 상태.
그러나 시퀀스 번호가 0인 패킷이 도착하면, 이는 이미 수신된 패킷이거나 잘못된 순서로 도착한 패킷이다
이때 프로토콜은 중복된 패킷으로 간주하고 drop,
이전에 성공적으로 수신한 패킷(시퀀스 번호 0)의 ACK을 다시 전송합니다.
이렇게 하는 이유는
1. 피드백 패킷의 손상으로 인한 재전송과
2. 오류가 발생시 재전송을 구분하기 위함이다.
1. 송신자가 시퀀스 번호 0을 가진 패킷을 보냄 → 수신자는 ACK 0을 송신. 그 후 1을 기다리는 상태로 전이
2. 하지만 ACK 0이 송신자에게 도착하지 않음(손상 또는 손실).
3. 송신자는 시퀀스 번호 0을 가진 데이터를 다시 전송.
4. 수신자는 중복된 시퀀스 번호 0을 보고 ACK 0을 다시 보냄.
5. 송신자는 ACK 0을 받고 시퀀스 번호 1로 넘어감.
1. 송신자가 시퀀스 번호 1을 가진 패킷을 보냄 → 수신자가 해당 패킷을 손상된 상태로 수신.
2. 수신자는 NAK 또는 응답을 보내지 않음.
3. 송신자는 타임아웃이 발생해 시퀀스 번호 1을 가진 데이터를 다시 전송.
4. 수신자는 정상적인 시퀀스 번호 1 패킷을 받고 ACK 1을 송신.
5. 송신자는 ACK 1을 받고 다음 시퀀스 번호 0으로 넘어감.
하지만 수신자는 발신자가 마지막 ACK/NAK을 받았는지 알 수 없다
수신자는 피드백패킷을 보내는데 잘 도착했는지 모른다.
-> 이 피드백패킷을 재전송 할 방법이 없음
-> 때문에 발신자는 동일 패킷을 재전송함 (중복패킷의 발생)
-> 3.0 의 타임아웃으로 해결
rdt 2.2이란?
1. data 전송시 에러 발생 가능
2. data 전송시 loss X 인 상황
3. 응답 피드백 패킷 손상 가능(loss 불가)
4.전송 후 대기(stop-and-wait): 데이터를 주고 수신자의 피드백을 기다림
2.1 과 같은 기능을 하지만 NAK을 쓰지 않는다.
NAK의 문제점 해결중복 ACK을 사용해 수신자가 패킷을 잘못 받았음을 알림ACK, 0 - 수신자가 시퀀스 번호가 0인 패킷을 정상적으로 수신했음을 알림ACK, 1 - 수신자가 시퀀스 번호가 1인 패킷을 정상적으로 수신했음을 알림단순화된 오류 처리중복 ACK를 받으면 NAK와 똑같이 동작
1. 시퀀스 번호 0 패킷 전송:
• 송신자가 시퀀스 번호 0인 패킷을 보냅니다.
• 수신자가 이를 정상적으로 받으면, 수신자는 ACK 0을 송신자에게 보냅니다.
• 송신자는 수신자로부터 ACK 0을 받으면, 패킷이 올바르게 전달되었다고 판단하고 다음 패킷(시퀀스 번호 1)을 보낼 준비를 합니다.
2. 시퀀스 번호 1 패킷 전송:
• 송신자가 시퀀스 번호 1인 패킷을 보냅니다.
• 수신자가 이를 정상적으로 받으면, 수신자는 ACK 1을 송신자에게 보냅니다.
• 송신자는 수신자로부터 ACK 1을 받으면, 패킷이 올바르게 전달되었다고 판단하고 다음 전송을 위해 시퀀스 번호를 다시 0으로 전환합니다.
1. 오류가 발생한 경우:
• 예를 들어, 송신자가 시퀀스 번호 1의 패킷을 보냈지만, 중간에서 손실되었거나 수신자가 오류로 인해 이 패킷을 제대로 받지 못했다고 가정해 보겠습니다.
• 수신자는 시퀀스 번호 1의 패킷을 기다리고 있었기 때문에, 이 패킷이 오지 않으면 문제가 발생합니다.
• 수신자가 패킷을 받지 못했거나 손상된 패킷을 받았다면, 기대하던 패킷이 오지 않았음을 나타내기 위해 이전에 성공적으로 받았던 패킷에 대한 중복 ACK을 보냅니다.
2. 중복 ACK을 보내는 이유:
• 송신자가 시퀀스 번호 1인 패킷을 보냈지만 수신자는 이를 제대로 받지 못했을 때, 수신자는 이전에 받았던 마지막 유효한 패킷에 대한 ACK 0을 다시 보냅니다. 이는 수신자가 아직 시퀀스 번호 1인 패킷을 받지 못했음을 나타내기 위한 것입니다.
• 중복 ACK 0을 보내는 것은 “나는 아직 시퀀스 번호 0까지의 패킷을 받았고, 시퀀스 번호 1 패킷은 제대로 못 받았다”는 의미입니다. 따라서 송신자는 이 신호를 보고 시퀀스 번호 1 패킷을 다시 전송해야 함을 알게 됩니다.
3. 수신자가 시퀀스 번호를 기다리는 상태 전이:
• 수신자가 시퀀스 번호 0인 패킷을 받으면 그 상태에서는 시퀀스 번호 1을 기대하게 됩니다.
• 송신자가 시퀀스 번호 1인 패킷을 보냈으나 이 패킷이 손실되었을 경우, 수신자는 여전히 시퀀스 번호 1 패킷을 기다리고 있는 상태입니다. 이 상태에서 패킷이 손실되었다면, 수신자는 이전에 성공적으로 받은 패킷에 대한 ACK을 반복해서 송신자에게 보내 오류를 알리게 됩니다.
보낸걸 다시 보낸이유는 수신자가 보낸 ACK 응답에 손실이 있어서 송신자가 재전송한 상황이기 때문이다.
1. 중복 패킷을 받은 경우:
• 수신자가 시퀀스 번호 1 패킷을 정상적으로 받고, ACK 1을 보낸 후 시퀀스 번호 0을 기다리고 있다고 가정합니다.
• 만약 송신자가 시퀀스 번호 1의 패킷을 다시 보내면, 수신자는 “이 패킷은 내가 이미 받은 패킷이다”라고 판단하게 됩니다.
• 이 경우, 수신자는 중복된 패킷을 받았으므로 중복 ACK 1을 송신자에게 보내야 합니다. 이는 송신자에게 “나는 이미 이 패킷을 받았으니, 새로운 패킷을 보내라”는 신호가 됩니다.
rdt 3.0이란?
1. data 전송시 에러 발생 가능
2. data 전송시 loss가능 상황
3. ACK/NAK 패킷 손상가능 (loss도 가능)
하위채널이 패킷의 손실할 수 있다
다음과 같은 두 가지 부가 내용을 프로토콜이 다루어야 한다.
수신자가 보낸 ACK신호가 loss발생하면 송신자는 하염없이 기다리게 된다.
즉, 송신자는 데이터 패킷이 손실되었는지, ACK가 손실되었는지, 패킷 또는 ACK가 단순히 지나치게 지연된 것인지를 알지 못한다.
만약 송신자가 패킷을 잃어버렸다고 확신할 정도로 충분한 시간을 기다릴 수만 있다면, 데이터 패킷은 간단하게 재전송될 수 있다.
-> 하지만 얼마동안이나 기다려야 할까?
송신자는 적어도
송신자와 수신자 사이의 왕복 시간 지연(중간 라우터에서의 버퍼링을 포함) + 수신 측에서 패킷을 처리하는 데 필요한 시간
시간 만큼을 기다려야 한다.
-> 이 시간 만큼 타이머를 두어 일정시간 오지 않는다면 재전송을 하자
- 매 패킷(첫 번째 또는 재전송 패킷)이 송신된 시간에 타이머를 시작한다.
- 타이머 인터럽트에 반응한다. (즉, 적당한 행동을 취함)
- 타이머를 멈춘다.


크게 네가지 상황으로 나눌 수 있다.

loss가 없을 떄
➡️ 아주 잘 동작
전송패킷의 loss
➡️ 타임 아웃 후 재전송

응답패킷의 loss
➡️ 타임 아웃 후 재전송
응답패킷을 보냈지만 loss없이 늦게 도착한 경우
➡️ 타임 아웃 후 재전송
➡️ 수신자는 중복을 감지해야하고
➡️ 송신자는 수신자의 중복감지에 대한 ACK응답을 무시해야 함
(ACK응답 받고 어플에서 데이터를 기다리는 상태인데 ACK응답패킷이 온다면 무시)
송신자의 이용률(utilization): 송신자가 실제 패킷을 보내느라 busy한 시간
이러한 이용률을 계산하여 성능을 분석한다.
stop & wait 때문에 보낼 패킷이 남아 있어도 송신자의 ACK를 받아야지만 보낼수 있다.
전송률: 1Gbps
프로파게이션 딜레이: 15 ms
8000 bit 패킷 전송 시
-> 전송지연 = L / R = 8000bit / 1Gbps = 8 msec

송신자가 실제 데이터를 보내는데 바쁜시간 = 패킷 1개 전송시간 / (RTT + L/R)
= 0.00027
전체 시간 중 엄청 적은 시간만 실제로 패킷을 전송한다.
매우 적은 이용률을 갖는다.
stop & wait때문에 성능이(적은이용률) 안나왔던 rdtx.x를 개선하기 위해
수신자의 응답을 기다리기 전에 패킷을 순차적으로 보내면서 이용률을 높이는 방식을 채택한다.

오류가 발생한 패킷 이후의 모든 패킷을 재전송
윈도우 크기(N): 송신자가 ACK(인증 응답)를 받지 않고 보낼 수 있는 최대 패킷의 수sned base: 보낸 패킷중 ACK를 못받은 가장 오래된 패킷 예시로 이해하기:
• 시퀀스 번호가 0에서 7까지 있다고 가정합니다.
• 송신자는 패킷을 차례로 보내면서 시퀀스 번호를 붙입니다. 예를 들어, 0, 1, 2, 3, …, 7까지 보낸 후, 그다음 패킷은 시퀀스 번호 0으로 돌아갑니다.
• 이때, 윈도우 크기가 시퀀스 번호의 절반 이하(예: 4 이하)로 설정되면, 송신자는 ACK 3을 받았을 때 그 시퀀스 번호가 이전 순환에서 온 것인지, 아니면 현재 순환에서 온 것인지 명확히 구분할 수 있습니다.
즉, 시퀀스 번호가 순환하면서 사용되더라도, 윈도우 크기를 제한함으로써 송신자와 수신자는 어떤 패킷이 ACK를 받았는지 혼동하지 않게 됩니다.
시퀀스 번호가 순환하는 과정:
1. 송신자는 시퀀스 번호 0부터 시작해서 패킷을 보냅니다.
2. 시퀀스 번호는 유한한 범위 내에서 0, 1, 2, …, N-1까지 증가합니다.
3. N에 도달하면 다시 0부터 시작해서 시퀀스 번호가 부여됩니다.
4. 송신자와 수신자는 윈도우 크기 덕분에 이전 순환과 현재 순환의 시퀀스 번호를 구분할 수 있습니다.
ACK(인증 응답)
• 수신자가 정상적으로 패킷을 수신하면, 해당 패킷의 시퀀스 번호에 대한 ACK를 송신자에 send
➡️ 송신자는 ACK을 수신하면 해당 패킷이 정상적으로 수신되었음을 확인하고, 그 패킷을 더 이상 재전송할 필요가 없다는 것을 알게 된다.
• 누적 ACK(cumulative)
= ACK을 보낼 때, 그 패킷 이전의 모든 패킷도 정상적으로 수신되었음을 한 번에 확인
➡️ 수신자는 누적 ACK을 사용하여 가장 최근에까지 순서대로 받은 패킷에 대한 ACK를 보냄
(송신자는 ACK 3 이 오면 1,2,3이 잘 갔다고 판단, 오류나 loss시 3번 패킷 이후부터 재전송)
타임아웃
• 타이머는 윈도우에서 가장 먼저 전송된 패킷에 대해 설정하고,
ACK응답을 받으면 그 패킷의 다음 시퀀스를 기준으로 타이머를 설정한다.
• 타임아웃 시 ACK 받은 시퀀스 번호 보다 높은 패킷을 모두 전송한다.
loss
• 1,2,3,4 패킷을 전송하는데, 2번을 못받고 1,3,4만 받으면 2번부터 다시 다 보냄
때문에 GO-Back-N이라 불림


중복 ACK가 발생할 수 있다ACK 3을 보냈는데 다음의 전송에서 4,5,6,7 패킷을 보낼 때 loss가 발생하면 다시 ACK 3을 보낸다)rcv_base : 수신자가 다음으로 기대하는 패킷의 시퀀스 번호out-of-order패킷은 일반적으로 drop한다.(빨간색 부분)
오류가 발생한 패킷만 선택적으로 재전송
고백엔의 낭비를 해결하기 위해 다음과 같은 설정이 추가 되었다.
➡️ 때문에 수신자는 도착한 각 패킷당 정보를 저장하고 있어야 해서 버퍼가 필요하다.
슬라이딩 윈도우
송수신자 모두 윈도우를 사용한다.
➡️ 송신자 - 윈도우 크기만큼의 패킷을 연속해서 보낼 수 있음
➡️ 수신자 - 버퍼를 사용해 윈도우 크기만큼의 패킷을 저장한다.
개별 ACK
개별적으로 패킷에 대해 ACK를 보낸다.
오류 및 패킷 손실 처리
SR에서는 수신자가 순서가 맞지 않는(out-of-order) 패킷을 받더라도 버퍼에 저장
우선 수신자는 순서에 맞지 않은 패킷이 도착하면 버퍼에 저장한다.
그 후 문제의 패킷의 타이머가 타임아웃으로 재전송되면 해당 패킷을 받아 ACK응답을 보낸다
따라서, 순서가 맞지 않는 패킷이 도착하더라도 버퍼에 저장해 두었다가, 손실된 패킷이 재전송되면 순서를 맞춰 상위 계층으로 전달한다.
버퍼를 비운다

윈도우 안에 전송대기중인 시퀀스 번호가 있으면 패킷을 전송한다.
노란색 패킷이 타임아웃이면 해당 패킷을 재전송한다
sendbase부터 + N 개 까지 다 초록색이면,(다 ACK응답을 받았다면)
N번 패킷에 마킹을 하고 윈도우를 N다음으로 이동한다.

rcv_base ~ rcv_base + N - 1 패킷 수신 시
(수신자가 기대하는 윈도우 내의 패킷을 수신했을 때)
➡️ 1. 잘 받은 패킷은 ACK를 보냄 (윈도우 내 범위에서 온 패킷을 잘 받으면 ACK)
➡️ 2. 순서가 안맞는 패킷은 버퍼에 저장
(예를 들어, 수신자가 rcvbase = 2이고 패킷 4를 먼저 받았다면, 패킷 4는 버퍼에 저장)
rcvbase - N ~ rcvbase - 1 패킷 수신 시
(이미 수신하여 ACK을 보낸 적이 있는 패킷)
➡️ 송신자가 ACK을 제대로 받지 못해 패킷을 재전송한 경우 수신할 수 있는 시퀀스 번호
(수신자는 중복 ACK을 보내 송신자에게 해당 패킷이 이미 수신되었음을 다시 알림)
수신 윈도우 범위 밖의 패킷이 도착
➡️ 무시
현재 상태:
• rcvbase = 2, 윈도우 크기 N = 4
• 수신 가능한 시퀀스 번호 범위: [2, 5]
1. 패킷 4를 수신:
• 패킷 4는 순서가 맞지 않지만, 수신 윈도우 안에 있으므로 버퍼에 저장하고, ACK 4를 보냅니다.
2. 패킷 2를 수신:
• 패킷 2는 순서가 맞는 패킷이므로, 상위 계층에 전달하고, ACK 2를 보냅니다.
• 버퍼를 확인하고, 버퍼에 저장된 패킷 3과 4도 순서대로 상위 계층에 전달합니다.
• rcvbase는 이제 5로 이동하여, 다음으로 기대하는 패킷이 됩니다.
3. 패킷 1을 재전송 받아 수신:
• 패킷 1은 rcvbase - N 범위에 속하므로, 수신자는 ACK 1을 다시 보냅니다. 송신자가 중복 ACK을 받으면 더 이상 재전송하지 않습니다.

충분한 시퀀스 번호의 범위를 확보해야 한다,
SR에서는 송신자와 수신자 모두 각각 윈도우를 사용한다.
1) 송신자의 윈도우: 동시에 전송할 수 있는 패킷의 개수를 나타냄
2) 수신자의 윈도우: 순서대로 도착하지 않은 패킷을 버퍼링할 수 있는 개수를 나타냄


만약 시퀀스 번호가 6까지 이면
위의 상황에서 윈도우가 3 5 6 을 가르키게 되고,
송신자가 다시 보내는 0 1 2에 대해서는 중복 ACK를 보낸다.
추후 기다리는 패킷과 혼동이 일어나지 않음
시퀀스 번호의 범위 >= 2 × 윈도우 크기
를 만족해야 한다.
| 특징 | Go-Back-N (GBN) | Selective Repeat (SR) |
|---|---|---|
| 오류 처리 방식 | 손실된 패킷 이후의 모든 패킷을 재전송 | 손실된 패킷만 선택적으로 재전송 |
| ACK 처리 방식 | 누적 ACK 사용 (가장 최근에 순서대로 수신된 패킷에 대한 응답) | 개별 ACK 사용 (각 패킷마다 ACK 전송) |
| 윈도우 크기 제한 | 송신자와 수신자의 윈도우 크기가 동일 | 수신자 윈도우 크기는 송신자 윈도우 크기와 독립적 |
| 수신자 동작 | 순서가 맞지 않은 패킷은 버림 | 순서가 맞지 않은 패킷도 버퍼링하여 저장 |
| 효율성 | 손실 발생 시 불필요한 재전송이 많아 비효율적일 수 있음 | 불필요한 재전송이 없고 더 효율적 |
| 구현 복잡도 | 구현이 상대적으로 간단 | 버퍼링과 개별적인 ACK 관리로 복잡 |
| 타이머 사용 | 송신자는 하나의 타이머만 사용 | 각 패킷마다 개별 타이머 설정 필요 |
| 시퀀스 번호 범위 | 윈도우 크기보다 같거나 클 수 있음 | 시퀀스 번호 범위는 윈도우 크기의 두 배 이상이어야 함 |
| 윈도우 이동 조건 | 가장 오래된 패킷의 ACK을 받을 때 윈도우 이동 | 순서에 맞는 패킷을 수신할 때마다 윈도우 이동 |
| 사용 예시 | 데이터 손실이 적고 구현의 단순함이 필요한 경우 | 데이터 손실이 많고 네트워크 자원의 효율적 사용이 중요한 경우 |