TCP는 Transmission Control Protocol의 약자로
데이터의 전송을 제어하는 프로토콜이다.
TCP는 데이터의 전송을 신뢰할 수 있는 연결을 지향한다.
이를 위해 3-way handshake 방식으로 통신을 시작하며
4-way handshake 방식으로 연결을 끊고 통신을 종료한다.
흐름/오류/혼잡 제어 방식으로 데이터의 순서와 수신 여부, 무결성을 보장한다.
데이터를 일정 경로를 통해 목적지로 보내는 것을 라우팅이라고 한다.
송신측에서 수신측 사이의 경로는 여러가지가 있을 수 있다.
데이터를 효율적으로 보내고, 한 목적지로 향하는 여러 경로로 보내기 위해
데이터를 패킷이라는 단위로 나누어서 전송한다.
(TCP는 OSI 4계층이기 때문에 바로 밑의 3계층에서 사용하는 패킷이라는 단어를 사용한다)
패킷은 받는 입장(수신측)에서 다시 재조립되어야 한다.
그리고 조립될 때는 원본의 순서도 정확히 알아야 한다.
왜냐하면 보낼 때는 ABC의 순서로 보냈지만 도착하는 순서는 BCA일 수도 있기 때문이다.
그래서 1-A, 2-B, 3-C 로 보내야, 2-B, 3-C, 1-A 순서로 받더라도 다시 ABC로 만들 수 있다.
ACK는 Acknowledgement 의 약자이다.
NAK 또는 NACK는 Negative Acknowledgement 의 약자이다.
ACK는 데이터 수신측에서 패킷을 올바르게 받았다는 뜻으로,
다음으로 필요한 패킷의 번호(받은 패킷 번호 + 1)와 함께 보내진다.
NAK는 받은 패킷에 오류가 있다거나 잘못된 순서로 받았다는 뜻으로,
문제가 있는 패킷의 번호와 함께 보내진다.
Acknowledgement 는 '인정'이라는 뜻이다.
즉 ACK는 "어 ㅇㅈ~ 다음 거 내놔~",
NAK는 "ㄴㅇㅈ~ 다시 내놔~"란 말과 같다.
송신측과 수신측 사이의 데이터 처리 속도 차이를 해결한다.
송신측에서 데이터 생성이 완료되었다고 마구잡이로 데이터를 던지면 안 된다.
수신측에서 데이터를 처리하는 속도가 좀 느려서 다 못 받아볼 수 있기 때문이다.
이 속도의 간극을 맞춤으로서 흐름을 제어한다.
이 이후로 데이터는 패킷으로 표현한다.
보낼 때 데이터를 통째로 보내는 게 아니라 패킷이란 단위로 보내기 때문이다.
흐름 제어는 다음과 같은 방식으로 구현할 수 있다.
송신측에서 패킷을 보내면 일단 멈추고 (Stop),
수신측에서 ACK가 올 때까지 기다린다. (Wait)
패킷을 하나씩 보내므로 비효율적이라는 단점이 있다.
일정 개수까지는 일일히 수신측에서 확답을 안 받아도 연속해서 보낼 수 있다.
여기서 이 '일정 개수'를 정하는 것을 윈도우(window)라고 한다.
수신측이 정한 윈도우의 크기 n개만큼 송신측은 패킷을 연속해서 보낼 수 있다.
연속해서 보낸 패킷에 대해 수신측이 ACK를 보내면 송신측은 해당 윈도우를 그만큼 확장한다.
예를 들어 n=7이고 송신측에서 1, 2, 3, 4, 5, 6, 7의 패킷을 보낼 준비 후
1, 2를 보내면 윈도우는 3, 4, 5, 6, 7 가 된다.
이후 수신측에서 1, 2에 대해 ACK가 오면 윈도우는 우측으로 확장되어
3, 4, 5, 6, 7, x, x 가 된다. x는 빈 공간으로, 송신측에서 새로 생산된 패킷이 들어갈 수 있다.
택배가 도중에 파손되거나 이상한 데로 갈 수 있듯
패킷도 정보가 손실되거나 분실될 수도 있다.
이를 해결하기 위해 TCP는 오류나 분실된 패킷을 감지하고 이를 재전송할 수 있는 기능을 지원하는데,
이를 ARQ (Automatic Repeat Request) 전략이라고 한다.
이 전략은 다음과 같은 방법으로 구현할 수 있다.
ARQ 구현 기법은 각각의 흐름 제어 기법과 관련되어 있다.
송신측에서 하나 보내고 수신측에서 ACK나 NAK를 보냄을 반복한다.
송신측이 NAK을 받거나 시간이 지나도 수신측으로부터 아무런 응답도 없다면 (타임아웃)
패킷을 재전송한다.
흐름 제어의 Sliding Window 기법과 연결된다.
마지막으로 확인된 패킷 이후의 패킷들을 다시 전송한다.
1, 2, 3, 4, 5, 6, 7... 를 보내는데 ACK(4)까지만 받고 (3번까진 제대로 왔다는 뜻)
타임아웃이 발생하거나 NAK를 받았다면
4번 패킷부터 싹 다 재전송한다.
이렇게 하려면 송신측에서는 아직 확인 안 된 패킷들을 계속 갖고 있어야 한다.
재전송을 해야하는 경우는 크게 세 가지가 있다.
첫째는 패킷에 오류가 있을 경우이다.
1번 받고 2번 패킷에 오류가 있을 경우 NAK(2)를 보낸다.
송신측은 2번부터 다시 보내고, 수신측도 2번부터 다시 받는다.
두번째는 순서가 바뀌었을 경우이다.
1을 제대로 받았는데 그 다음에 뜬금없이 3번이 오면 수신측은 2번을 잃어버렸다고 판단해버리고
바로 NAK(2)를 보내고 나서 2번 이후의 모든 패킷을 다 폐기시켜버린다.
3번이 제대로 된 패킷인지 아닌지는 상관 없다.
세번째는 패킷이 분실되어 수신측에 도달하지 못할 때이다.
송신측에서 보낸 패킷에 대해 시간이 아무리 지나도 ACK나 NAK가 오지 않는다.
이러면 타임아웃 상황이 발생해서 마지막으로 ACK 받은 데이터부터 다시 보낸다.
만약에 최근에 ACK(2)를 받았으면 2번 패킷부터 다시 보낸다.
첫번째는 그렇다 치고 두번째는 좀 비효율적인 것 같다.
순서가 좀 바뀔 수 있지, 그걸 다시 다 보내라고 하는 것은 송신측에게 무리한 요구라는 생각이 든다.
1, 3, 2 순서로 받으면 수신측에서 1, 2, 3으로 정렬해서 쓰는 게 더 낫지 않나...
(일반적으로 처리 시간은 CPU < Memory < I/O < Network 순으로 증가한다)
그래서 다음의 기법이 등장한다.
Go-Back-N ARQ가 문제가 발생한 패킷부터 다시 다 보내는 반면
SR ARQ는 문제가 발생한 패킷만 보낸다.
수신측은 패킷을 새로 재정렬해야 한다.
이를 위해 수신측은 버퍼를 따로 두어야 한다.
송/수신 측의 데이터 처리 속도가 십중팔구 다르다.
네트워크의 혼잡을 피하기 위해 보내는 데이터의 전송 속도를 제어해야 한다.
이때 전략은 다음의 두 가지가 있다.
합 증가 곱 감소 알고리즘이라고도 한다.
패킷 하나를 보내고, ACK가 오면 윈도우의 크기를 1씩 증가시킨다.
NAK나 타임아웃이 발생하면 절반으로 감소시킨다.
여러 송신측과 한 수신측이 있고 송신측 하나가 추가된다고 할 때
이는 처음엔 불리하겠지만 (윈도우 크기가 작으므로)
나중에 가면 크기가 평형을 이루게 된다.
따라서 공평하다는 장점이 있다.
하지만 초기에는 높은 대역폭을 사용하지 못하며,
문제가 발생하고 나서야 해결한다는 단점이 있다.
AIMD 는 처음에 전송속도를 올리기 어렵다는 단점이 있다.
이를 보완해서 Slow Start 는 윈도우의 크기를 2배씩 늘리고,
혼잡 현상이 발생하면 크기를 1로 줄인다.
처음엔 네트워크의 수용량을 예측할 수 있는 정보가 없지만
시간이 지나면서 혼잡 현상이 발생했던 윈도우의 크기가 얼만지를 알아낼 수 있다.
이때 이 크기의 절반까지는 2배씩 증가시키고 그 이후부턴 1씩 증가시킨다.
윈도우의 크기(y축)가 지수함수 꼴로 증가하면서
네트워크 혼잡이 발생했던 지점(congestion point of network)의 절반
(40가 좀 안 되므로 20 정도) 부턴 1씩 증가하는 것을 확인할 수 있다.
단, 그래프에선 실제 절반인 20이 아닌 28에 임계값을 설정해놨기 때문에 28부터 증가폭이 1로 변경된다.
Slow Start에서 적용할 수 있는 정책은 다음과 같다.
윈도우 크기를 2배씩 늘리다가 임계 지점부턴 1씩 증가시킨다.
패킷이 일정 시간 동안 ACK/NAK를 받지 못하는 경우 이는 혼잡이라고 간주한다.
혼잡이 발생하면 윈도우 크기를 1로 줄이고, 임계값을 현재 크기의 절반으로 줄인다.
혼잡 발생 시 윈도우 크기를 1로 줄이는 게 아니라 절반으로 줄인다.
줄인 다음엔 1씩 증가시킨다.
한 번 혼잡이 발생한 이후부턴 AIMD로 작동한다.
수신측에서 패킷을 받을 때
먼저 도착해야 할 패킷이 도착하지 않고 다음 패킷이 도착한 경우에도 ACK 패킷을 보내도록 한다.
단, 순서대로 잘 도착한 마지막 패킷의 다음 패킷의 순번을 ACK 패킷에 실어서 보낸다.
만약 순서가 꼬여서 1, 2, 3, 6, 4, 5가 온다면
ACK(4), ACK(4), ACK(4)가 올 것이다.
왜냐면 순서대로 잘 도착한 마지막 패킷의 다음 패킷(3)의 순번은 4이기 때문이다.
따라서 중간에 패킷 하나가 손실되면 송신측에서는 순번이 중복된 ACK 패킷을 받게 된다.
이것을 감지하면 문제가 되는 순번의 패킷을 재전송할 수 있다.
(오류 제어로 SR ARQ가 적용된다면 재전송은 안 하지 않을까?)
빠른 재전송은 중복된 순번의 패킷을 3개(3 ACK) 받으면 재전송한다.
그리고 이러한 현상이 일어나는 것은 약간의 혼잡이 발생한 것으로 간주하여
윈도우의 크기를 절반으로 줄인다.