TCP에서 중요한 세가지를 꼽으면 신뢰성 있는 데이터 전송(reliable data transfer), 흐름 제어(flow control), 혼잡 제어(congestion control)이다.
1) TCP 연결
TCP는 단일 송신자와 단일 수신자 한 쌍의 소켓을 위해서만 동작한다(point-to-point).
전송 데이터의 신뢰성이 있고 전송 순서를 지킨다.
파이프라인화 되어있어서 다수의 패킷을 한번에 처리한다.
전이중(full-duplex) 서비스를 제공한다. 하나의 TCP 소켓은 송수신이 모두 가능하다.
연결지향형이다. TCP 연결은 오직 종단 시스템에서만 동작한다.
흐름 제어가 가능하다. 네트워크나 수신자의 상태에 맞게 패킷 전송이 제어된다.
2) TCP 세그먼트 구조
◾ 순서번호(seq. no.)와 확인응답(ack. no.) 번호
MSS가 1000인 세그먼트의 데이터 스트림
순서번호는 단순히 1, 2, 3 이렇게 정해지는것이 아니라 전송된 바이트 스트림에 의해서 정해진다.
세그먼트의 첫 번째 바이트의 바이트 스트림 번호가 순서번호가 된다.
확인번호는 조금 다르다.
송신자에게 전달된 피드백에 담긴 확인번호의 의미는 "수신자가 송신자에게 받기를 기대하는 순서번호" 이다.
예를들어 위의 그림처럼 MSS가 1000인 세그먼트 데이터 스트림에서 피드백에 ACK#1000이 담겨져서 돌아왔다면 첫 번째 세그먼트는 잘 받았으니 2번째 세그먼트를 전달받기를 기대한다는 의미가 된다.
3) 왕복시간(RTT) 예측과 타임아웃
타임아웃의 시간은 RTT 시간을 측정하고 여유 시간을 조금 더해서 결정한다.
우선 샘플RTT를 측정해야 하는데, 세그먼트 전송 직후 타이머를 실행해서 피드백을 받을 때까지로 측정한다. 단,재전송한 세그먼트는 샘플RTT에 포함되지 않는다.
샘플RTT는 큐잉 지연 등 변동성이 크기 때문에 불규칙하기 때문에 가중평균을 구한다.
EstimateRTT = (1 - a) * EstimateRTT + a * SampleRTT (a = 0.125. RFC 권장값)
샘플RTT가 측정될 때마다 평균이 갱신된다.
샘플RTT의 평균에 여유값을 약간 더해서 타임아웃 시간을 정한다.
4) 신뢰적인 데이터 전달
송수신측의 소켓이 연결되면 각각 send/receive 2개의 버퍼와 송신측에 타이머가 생성된다.
send 버퍼는 혹시 모를 재전송을 위해, receive 버퍼는 In-order를 지키기 위해 존재한다.
중간에 패킷이 유실되어서 타임아웃이 발생하면 timer가 가리키는 세그먼트가 재전송된다.
호스트의 각 소켓별로 위와 같은 구조를 가지고있다.
◾ 빠른 재전송
패킷 손실에 의한 타임아웃이 발생해서 재전송 되는것은 신뢰성이 보장되기는 하지만 넉넉히 잡은 시간이기 때문에 타임아웃이 빈번하게 발생할수록 지연이 증가되기 때문에 타임아웃 전에 송신자에게 전달된 ACK 피드백이 일정 횟수 중복된다면 손실로 판단하고 타임아웃 전에 재전송을 한다.
일반적으로 3회 중복 수신 시, 손실로 판단한다.
5) 흐름제어
송수신자의 데이터 처리 속도 차이를 해결하기 위한 기법이다. 수신자가 송신자에게 자신의 상태를 피드백 하는것이 기본 개념이다.
receive 버퍼가 프로세스에 의해 읽혀서 비워지는 속도보다 버퍼에 세그먼트가 쌓이는게 더 빨라지면 receive 버퍼는 오버플로우가 일어나게 될 것이다.
이 때, receive 버퍼의 window size(receive window, rwnd)를 송신자에게 알려준다. 그러면 송신자가 송신여부를 판단하여 전송 흐름을 제어할 수 있다. TCP 헤더에 window size가 포함되는 이유는 이 때문이다.
receive 버퍼의 window size만 알면 되기 때문에 흐름 제어는 매우 직관적이다.
◾ receive window(rwnd) = 0인 경우
receive 버퍼가 가득 차서 rwnd가 0이 되면 송신자는 더 이상 전송하지 않고 대기 상태가 된다.
이 때, 송신자가 다시 전송을 시작하려면 수신자는 ACK 피드백을 통해 현재 rwnd 여유가 생겼다는것을 송신자에게 알려주어야 한다. 하지만 수신자의 send 버퍼까지 비어있다면 데드락이 발생하게 된다.
해결책은 rwnd가 0이 되면 송신자의 영속(Persistence) 타이머가 주기적으로 ACK를 받기 위한 1바이트 길이의 데이터(prove packet)를 전송하여 상황을 알아본다.
영속 타이머에 의한 데드락 방지
참고로 UDP는 흐름제어를 제공하지 않는다.
◾ 어리석은 윈도우 신드롬 (Silly Window Syndrome)
송신측의 데이터가 저속으로 1바이트씩 발생하거나, 수신측이 저속으로 1바이트씩 처리하는 경우에 1바이트 데이터 전송을 위해 데이터보다 큰 헤더가 부가적으로 붙는 오버헤드가 발생하는 현상이다. 네트워크의 자원이 낭비된다.
이 때, 송신자는 Nagle 알고리즘, 수신자는 Clark 솔루션 또는 지연 확인을 사용하여 해결할 수 있다.
송신자 해결법: Nagle 알고리즘
최초에는 1바이트만 전송요청이 들어와도 전송을 한다. 이후부터는 ACK 피드백을 받거나 세그먼트 크기가 MSS에 도달할 때 까지 송신을 보류한다.
매우 단순하지만 효과적인 방법이다.
수신자 해결법1: Clark 솔루션
rwnd의 크기가 약간 남아있더라도 송신자에게 0이라고 알려줘서 너무 작은 세그먼트를 송신하지 않도록 하는 최적화 기법이다.
수신자 해결법2: 지연 확인(Delayed Aknowledgment)
수신 즉시 ACK 피드백 하는것이 아니라 일정시간 대기하여 오버헤드를 줄이는 최적화 기법이다.
단, 대기시간이 너무 길어지면 재전송 횟수가 늘어서 오히려 더 혼잡해질수 있다.
6) TCP 연결 관리
TCP는 3-way handshake 절차를 거쳐 소켓이 서로 연결된다.
3-way handshake
클라이언트 TCP가 서버 TCP에게 SYN 세그먼트를 전송한다.
서버 TCP가 버퍼와 변수들을 할당하고 클라이언트 TCP로 연결 승인 세그먼트(SYN ACK)를 전송한다.
클라이언트 TCP도 버퍼와 변수들을 할당하고 서버로 또 다른 세그먼트(SYN ACK에 대한 ACK)를 송신한다.
3번의 ACK 메시지는 일반적인 ACK피드백과 같다. 1,2번은 세그먼트에 헤더만 담기지만 3번부터는 데이터도 담을 수 있다.
연결 종료는 서로 요청-응답을 주고 받으면 연결이 종료된다.
연결 종료 과정
3.6 혼잡제어의 원리
◾ 혼잡
네트워크가 처리할 수 있는 양보다 더 많은 데이터가 들어왔을 때 생기는 현상. (=라우터의 버퍼가 가득 찬 경우)
1) 혼잡의 원인과 비용
세 가지 시나리오를 통해 혼잡제어의 개념을 익히도록 한다.
◾ 시나리오 1: 2개의 송신자와 무한 버퍼를 갖는 하나의 라우터
라우터의 버퍼가 무한하기 때문에 손실이 일어나지 않고 재전송도 필요가 없어진다. 또한 흐름제어나 혼잡제어도 수행하지 않는다.
이 때 패킷은 용량 R의 공유 출력 링크로 전달되고 연결당 처리량은 최대 R/2까지 증가한다.
전송률이 R/2보다 커지더라도 연결당 처리량은 R/2를 넘길 수 없다.
그리고 전송률이 R/2에 근접할수록 큐잉지연이 커진다.
◾ 시나리오 2: 2개의 송신자와 유한 버퍼를 갖는 하나의 라우터
이제 버퍼가 유한해졌으므로 패킷 손실이 발생할 수 있다. 그에 따라 재전송도 일어날 수 있다.
우선 라우터의 버퍼가 비어있는지 알아낼 수 있다고 가정을 해본다.
그러면 손실이 일어나지 않아서 마찬가지로 최대 R/2의 송신률을 가진다.
두번째로 패킷 손실을 확실히 알았을 때만 재전송을 한다고 가정을 해본다.
송신자는 재전송을 통해 패킷 손실을 처리해야하기 때문에 실제 수신된 패킷은 송신된 패킷보다 적게된다.
마지막으로 패킷 손실이 일어나지는 않았지만 타임아웃이 너무 일찍 일어난다고 가정을 해본다.
두번째와 같은 경우가 발생한다.
송신자가 데이터를 많이 보내서 혼잡이 발생했는데, 재전송으로 인해 더 많이 보내는 악순환이 일어난다.
◾ 4개의 송신자와 유한 버퍼를 갖는 라우터, 그리고 멀티홉 경로
각자의 호스트가 데이터를 전송하면 일정 수준까지는 잘 전송되다가 어느 지점에 도달하면 큐가 가득차서 손실이 일어나게된다.
패킷이 경로상에서 버려질 때, 버려지는 지점까지 패킷을 전송하는데 사용된 상위 라우터의 전송용량은 헛된 것이 된다.
결과적으로 송신자가 데이터를 많이 보내면 보낼수록 수신자의 수신률이 떨어지는 역설적인 상황이 발생하게 된다.
2) 혼잡제어에 대한 접근법
종단간의 혼잡제어 네트워크 계층은 트랜스포트 계층에 혼잡제어 목적을 위한 아무런 지원도 해주지 않기 때문에 혼잡의 존재는 종단 시스템에서 추측해야만 한다.
네트워크 지원 혼잡제어 네트워크 계층 구성요소가 송신자에게 직접적인 피드백을 제공한다. 라우터가 직접 송신자에게 초크 패킷을 송신해서 알려주거나 송신중인 패킷에 혼잡 여부를 표시하면 수신자가 송신자에게 피드백으로 돌려주는 방법이 있다. 후자는 왕복 시간이 걸린다는 단점이 있다.
3.7 TCP 혼잡제어
TCP는 ACK 피드백 수신 여부에 따라 네트워크의 상태를 추측한다.
send 버퍼의 window size를 결정하는 요소는 rwnd와 더불어 congestion window(cwnd)가 존재한다.
rwnd와 cwnd 둘 중 작은값으로 send buffer의 window size가 정해진다.
트랜스포트 계층 프로토콜은 애플리케이션 계층과 네트워크 계층간에 메시지를 운반할 뿐이다. 각 계층은 하위 계층에서 무슨 작업을 하는지 신경 쓸 필요가 없다.
2) 인터넷 트랜스포트 계층의 개요
트랜스포트 계층은 TCP와 UDP 두 가지의 프로토콜이 있다.
두 프로토콜의 기본적인 기능은 IP 전달 서비스를 Host-to-Host에서 Process-to-Process로 확장하는 것이다. (다중화와 역다중화)
TCP는 UDP와 다르게 신뢰적 데이터 전달과 혼잡제어를 제공한다.
3.2 다중화와 역다중화
메시지를 알맞는 목적지 프로세스에 전달해주는 작업이다.
UDP와 TCP 모두 지원하는 개념이지만 작동 원리가 조금 다르다.
다중화 : 세그먼트들을 네트워크 계층으로 전달하는 작업. 애플리케이션 계층에서 트랜스포트 계층으로 소켓을 통해 메시지가 전달될 때, 하나의 세그먼트로 캡슐화 하여 네트워크 계층으로 전달하는 과정이다.
역다중화 : 세그먼트들을 애플리케이션 계층의 올바른 소켓으로 전달하는 작업. 세그먼트의 헤더 정보를 읽어서 올바른 소켓을 판단한다.
UDP의 Connectionless 역다중화
UDP는 TCP와 다르게 소켓과 소켓이 1:1 매핑 되지 않는다. (connectionless)
그래서 헤더 정보의 목적지 포트번호만을 보고 전달된다.
TCP의 Connection-oriented 역다중화
TCP는 목적지 포트가 모두 80인데도 불구하고 서로 다른 소켓으로 간다.
그 이유는 "TCP 소켓은 고유의 포트번호가 아닌 고유의 ID(집합)를 가지기 때문"이다.
집합은 출발지의 IP주소/포트번호, 목적지의 IP주소/포트번호 4개 요소로 이루어진다.
4개 요소중 하나라도 다르면 다른 소켓으로 인식하기 때문에 포트번호가 같더라도 역다중화를 통해 올바른 소켓으로 전달된다.
3.3 비연결형 트랜스포트: UDP
UDP 세그먼트 헤더
UDP가 비연결, 비신뢰성이라고 하더라도 간단한 오류 검출 기능은 가지고 있다.
데이터가 유실될지언정 잘못된 데이터가 전달되지는 않는다.
에러가 없으면 세그먼트를 바로 목적지 포트 소켓으로 넘겨준다. 매우 단순한 구조이다.
단순한 구조인 만큼 오버헤드가 적다.
3.4 신뢰성 있는 데이터 전송의 원리
신뢰성 있는 데이터 전송은 TCP의 중요한 개념중 하나이다.
TCP는 트랜스포트 계층의 애플리케이션 계층에 대한 데이터의 신뢰성을 보장한다. 하지만 하위 계층에서는 데이터의 신뢰성을 보장할 수 없다.
1) 신뢰적인 데이터 전달 프로토콜의 구축
비신뢰적인 데이터 전달이라 함은 데이터의 에러 또는 유실 둘 중 하나가 발생할 수 있다는 의미이다.
◾ 완벽하게 신뢰적인 채널 상에서의 데이터 전송: rdt1.0 (reliable data transfer) 하위 채널이 완전히 신뢰적이라고 가정한다. 이 경우에는 각 레이어가 송수신을 바로 진행시키면 된다. 완전히 신뢰적인 채널에서는 수신 측이 송신 측에게 어떠한 피드백도 제공할 필요가 없고 '천천히' 라는것도 요청할 필요가 없다.
rdt1.0의 유한 상태 머신(FSM)
◾비트 오류가 있는 채널 상에서의 신뢰적 데이터 전송: rdt2.0 패킷 안의 비트들이 하위 채널에서 손상될 수 있지만 송신 순서대로 수신된다고 가정한다. 전화통화의 예를 생각해보면 된다. 상대방의 말을 잘 들었다면 확인 응답을 해준다(수신자 피드백, ACK). 상대방의 말이 잘 안들리면(에러 검출) 다시 말해달라고 요청하고(수신자 피드백, NAK) 상대방은 같은 말을 다시 해준다(재전송). 이 세가지가 자동 재전송 요구(ARQ) 프로토콜에 요구된다.
송신자 프로토콜은 수신자의 피드백을 받아야 다음 행동을 결정할 수 있으므로 ACK/NAK 패킷을 기다려야 한다.
rdt2.0의 FSM. 전송-후-대기 프로토콜 이라고도 한다
단, rdt2.0은 피드백 패킷이 손상되어 송신측으로 들어오는 치명적인 문제가 발생할 수 있다.
피드백 패킷이 손상되어 송신자가 ACK/NAK를 판별하지 못할 때, 재전송을 한다면 수신자는 패킷의 중복 수신이 발생하므로 패킷에 순서번호(sequence number)를 추가해서 수신자가 중복 수신을 처리하게 한다. 순서번호는 최소 1비트면 충분하다. 이 개선사항을 rdt2.1이라고 하자. 순서번호 비트인 0, 1을 처리하기 위해 기존의 상태보다 2배 많은 상태를 가지게된다.
개선된 rdt2.1 송신자 FSM
개선된 rdt2.1 수신자 FSM
여기서 한발 더 나아가서 오류검출 및 중복 수신을 송신자 측에서도 같이 처리한다면 NAK 없이 ACK 처리만으로 구현이 된다. (rdt2.2: NAK-free protocol)
수신자에서 오류 검출시 NAK가 아닌 ACK와 함께 마지막으로 수신된 순서번호를 함께 송신자에게 보낸다.
송수신자의 순서번호가 다르면 재전송을 하게 된다.
NAK가 사라진 rdt2.2 송신자 FSM
NAK가 사라진 rdt2.2 수신자 FSM
◾비트 오류와 손실 있는 채널 상에서의 신뢰적 데이터 전송: rdt3.0 손실은 수신자에게 가는 메시지가 유실되거나 송신자에게 돌아오는 피드백이 유실되는 두 가지 경우가 존재한다.
어떤 경우간에 송신자 입장에서는 같은 현상이다. 이 때 시간 제한(timeout)을 걸어서 재전송을 하면 손실은 해결할 수 있다.
rdt3.0 송신자 FSM
rdt1.0 : 모든 채널이 완벽하게 신뢰적일 때. 호출 받는대로 바로 송수신이 이루어진다.
rdt2.0 : 채널에서 비트 오류가 발생할때. 오류 검출, 수신자 피드백, 재전송이 포함된 ARQ 프로토콜에 의해 송수신이 이루어진다.