전송 계층 2

윤상준·2022년 4월 3일
0

네트워크

목록 보기
6/19
post-thumbnail
post-custom-banner

TCP Overview

TCP를 전체적으로 살펴보면 다음과 같다.

Point-to-point

TCP는 한 쌍의 프로세스 간의 데이터 통신을 담당한다. 더 염격하게 말하면 한 쌍의 소켓끼리의 통신을 담당한다. 즉, 하나의 포인트 (전송자 프로세스)에서 다른 포인트 (수신자 프로세스) 간의 통신을 의미한다.

Reliable, in-order byte stream

데이터는 Byte Stream 단위로 전송되며, 신뢰성있고 순서대로 전송된다.

Pipelined

데이터는 Pipeline 방식으로 전송되며 데이터의 혼잡 제어와 흐름 제어가 이루어진다.

Send & receive buffers

TCP에서는 모든 프로세스가 전송자와 수신자가 될 수 있다. 따라서 모든 프로세스는 Send 버퍼, Receive 버퍼를 보유하고 있어야한다.

버퍼 (Buffer)
Window size가 곧 버퍼의 크기이다.
보내는 데이터 하나하나마다 ACK을 받지 않고, 일단 Window 크기 만큼 데이터를 쏟아부으므로 데이터가 유실될 수 있다.
이때 유실된 데이터를 재전송하기 위해 임시로 저장해놓을 공간이 필요한데 그게 바로 Send 버퍼이다.
Receive 버퍼는 순서에 맞지 않은 패킷들을 버리지 않고 임시로 저장해놓는 공간이다.

Full duplex data

TCP는 양방향 데이터 통신으로 이루어진다.
즉, 전송자와 수신자가 정해져있지 않아서 한 프로세스가 데이터를 전송할 수도 있고 수신할 수도 있다. (웹 브라우저 - 웹 서버)

Connection-oriented

TCP는 연결 지향성이다. 따라서 본격적으로 데이터를 주고받기 전에 3-way-handshake 등의 과정을 통해 프로세스 간의 연결을 이룬다.

Flow-controlled

전송자는 수신자가 받을 수 있는 데이터의 양을 초과해서 보내지 않는다.

Congestion-controlled

내부의 네트워크가 처리할 수 있을 만큼의 데이터만 전송한다.

TCP segment structure

각 계층마다 데이터의 전송 단위가 있다.

  • 어플리케이션 계층 : 메시지 (Message)
  • 전송 계층 : 세그먼트 (Segment)
  • 네트워크 계층 : 패킷 (Packet)
  • 데이터링크 계층 : 프레임 (Frame)

어플리케이션 계층의 메시지가 소켓과 인터페이스를 통해 전송 계층으로 내려오면, 전송 계층의 데이터 전송 단위인 세그먼트의 데이터 영역으로 들어간다. 이때 세그먼트는 헤더와 데이터 영역으로 구성되어있다.

전송 계층에서 하위 네트워크 계층으로 세그먼트를 내려보내면, 네트워크 계층의 데이터 전송 단위인 패킷의 데이터 영역으로 들어간다.

이처럼 하위 계층으로 계속 내려간다.

포트 넘버

TCP 세그먼트는 Multiplexing과 Demultiplexing을 위해 출발지와 목적지의 포트 넘버가 필요하다. 총 32비트 중에서 포트 번호는 각각 16비트씩 할당된다. 따라서 포트 번호는 2^16 = 65536 (0 ~ 65535) 개만큼 존재할 수 있다.

시퀀스 넘버

시퀀스 넘버는 세그먼트 데이터의 첫 바이트의 Byte stream number를 의미한다.

총 100바이트의 데이터를 전송하는 상황이 있고, 첫 번째 세그먼트는 0~9번 바이트, 두 번째 세그먼트는 10~29번 바이트를 보낸다고 가정하자.

첫 번째 세그먼트의 시퀀스 넘버 (seq #)는 0이 되고, 두 번째 세그먼트의 시퀀스 넘버는 10이 된다.

ACK 넘버 (Acknowledgement number)

TCP에서의 ACK 넘버는 다음 번에 받을 세그먼트의 시퀀스 넘버이다.
ACK10은 9번 세그먼트까지 잘 받았고 이제 10번 세그먼트를 줘 라는 의미이다.
앞서 Go-Back-N의 Cumulative ACK 과는 살짝 다른 개념이다.
(Cumulative ACK에서의 ACK10 : 10번 세그먼트까지 잘 받았다.)

체크섬 (Checksum)

에러를 검출하기 위해 사용하는 비트이다.

Receive Window

현재 수신자가 수신할 수 있는 바이트의 개수이다. Receive 버퍼에 남은 공간이 얼마나 되는지와 의미가 같다.

TCP 시퀀스 넘버와 ACK

세그먼트의 필드 중에서 시퀀스 넘버 필드와 ACK 필드에는 어떤 정보가 담겨있는지 살펴보자.

호스트 A와 B가 TCP 통신하는 과정을 나타낸 그림이다.
'C'라는 메시지를 데이터로 주고받는다고 가정했을 때, 호스트 A는 Send 버퍼에서 'C'를 꺼내서 B에게 전송하고, B 역시 자신의 Send 버퍼에서 'C'를 꺼내어 A에게 전송한다. 이처럼 같은 방식으로 문자 'C'를 주고받는 상황이다.

이때 전송되는 데이터와 함께 세그먼트의 헤더에는 시퀀스 넘버 (Seq)ACK이 포함된다.

앞서
시퀀스 넘버 : 전송할 데이터 스트림의 제일 앞 번호.
ACK(N) : 다음 번에 받을 데이터의 번호가 N.
이라고 했다.

먼저 A가 B에게 Seq : 42, ACK : 79, data : 'C'를 전송하고, B가 A에게 Seq : 79, ACK : 43, data : 'C'를 전송했다.

잘 살펴보면 ACK = 상대방 Seq + 1이고 Seq = 상대방 ACK임을 알 수 있다.

즉, Seq는 내가 보낼 데이터의 번호, ACK는 내가 받아야 할 데이터의 번호이다.

A는 B에게 42번 데이터를 보내주고 (Seq : 42) 79번 데이터를 달라고 요청 (ACK : 79)했다는 의미이다. (A는 직전에 B로부터 Seq : 79, ACK : 41를 받았을 것이다.)

B는 이를 토대로 A에게 79번 데이터를 보내주고 (Seq : 79) 43번 데이터를 달라고 요청 (ACK : 43)했다.

다시 A는 Seq : 43, ACK : 80을 보냈다. 즉, B가 43번 데이터를 달라고 요청했으니 43번을 보내주고 (Seq : 43), 79번 데이터까지 받았으니 이제 80을 달라고 요청 (ACK : 80)한 것이다.

이처럼 TCP에서는 서로가 전송자(Sender)인 동시에 수신자(Receiver)이므로, 상대방에게서 받은 메시지에 ACK 피드백을 보내는 동시에 메시지를 보낼 수도 있다(Seq)는 것이 특징이다. (Full duplex data, 양방향통신)

Timeout - Function of RTT

앞서 TCP는 타이머를 통해 데이터의 재전송 여부를 판단한다고 했다.

  • Time-out되기 전에 피드백이 오면 정상적으로 전송되었다고 판단하고 다음 패킷을 보낸다.
  • Time-out이 되면 패킷이 유실된 것으로 판단하고 재전송한다.

이때 한 세그먼트가 수신자에게 도착하고 그 피드백이 다시 전송자에게 돌아오는 시간을 RTT (Round Trip Time) 라고 한다.

또한 앞서 타이머를 적절한 선에서 결정해야한다고 했다.
(너무 짧으면 오버헤드가 발생하고 너무 길면 유실 대응이 느려진다고 했다.)

타이머의 정도 기준은 패킷 유실 처리는 확실하게 하면서 Time-out value는 작게 하는것이 목표라고 할 수 있다.

그렇다면 타이머를 RTT만큼 설정하면 괜찮지 않을까? 라고 생각할 수 있지만 여기에는 한 가지 변수가 있는데, 바로 모든 패킷의 RTT가 똑같지 않다는 점이다.

남색 점이 RTT인데 딱 봐도 편차가 매우 크다는 것을 알 수 있다. 이처럼 패킷이 전송되는 경로 또는 Queueing Delay 등에 의해서 RTT는 얼마든지 달라질 수 있다.

따라서 매번 RTT를 측정해서 타이머를 설정하는 일은 무리가 있으므로 RTT의 대표값을 결정할 필요가 있다. 대표값은 지금까지 측정해온 RTT 값을 모두 더해서 평균을 낸 값이며 이를 Estimated RTT 라고 한다. 그림에서는 분홍색 점이며 격차가 매우 줄어들었음을 확인할 수 있다.

Estimated RTT = (1-a) * Estimated RTT + (a * sample RTT)

하지만 그림에서 보듯이 실제 RTT에 비해 Estimated RTT는 매우 작다. 이 상태에서 Estimated RTT로 타이머를 설정하면, 실제로 피드백이 오기도 전에 타임아웃이 주구장창 발생할 수 있다.

이를 보완하기 위해 Dev RTT (데비에이션 RTT)를 따로 구한 후, 이를 4배 곱한 값을 Estimated RTT에 더해서 최종적으로 타이머 값을 설정한다.

DevRTT = (1-B) * DevRTT + B * |SampleRTT - EstimatedRTT| (Typically, B = 0.25)

Timeout Interval = EstimatedRTT + (4 * DevRTT)

중요한 것은 타임 아웃 값을 잡을 때 어느 정도 마진을 더해서 잡아준다는 사실이다.

TCP Reliable Data Transfer

TCP 에서 제공하는 RDT (Reliable Data Transfer)은 다음과 같은 특징이 있다.

  • Pipelined segments : 파이프라인 방식을 사용한다.
  • Cumulative ACKs : TCP에서의 ACKsms Cumulative ACK이다.
  • Single retransmission timer : 타이머를 하나만 사용한다. (Go-Back-N과 비슷하다.)
    • 다만 Go-Back-N은 Time-out이 발생하면 버퍼 안에 데이터를 모두 재전송하지만, TCP는 Time-out이 발생하면 해당 세그먼트만 재전송한다.

TCP : Retransmission scenarios (재전송 시나리오)

TCP에서 재전송이 이루어지는 가상의 시나리오를 살펴보자.

ACK이 유실되었을 경우

데이터는 8바이트씩 전송된다.

  • A의 시퀀스 넘버는 92번이다. 즉 92번 ~ 99번의 데이터를 B에게 전송했다.
  • B는 92번 ~ 99번 데이터를 잘 받아서 위로 올리고 ACk100을 보냈다. 그런데 이 ACK100이 유실되었다.
  • A는 92번 데이터에서 Time-out이 발생했으므로 다시 92번 ~ 99번 데이터를 B에게 전송했다.
  • 이번에는 B가 보낸 ACK100이 제대로 도착했다.

ACK이 전송되는 도중에 Time-out이 발생한 경우

  • A는 92번 ~ 99번 까지의 8바이트짜리 하나와 100번 ~ 119번 까지의 20바이트짜리 하나를 B에게 전송했다.
  • B는 둘 다 잘 받아서 위로 올리고 ACK100, ACK120을 보냈다.
  • 그런데 ACK100이 도착하기 전에 A에서 Time-out이 발생했다. 따라서 A는 92번 ~ 99번 까지의 8바이트 데이터를 재전송했다.
  • B는 120번 이후의 데이터를 기다리고있는데 92번 ~ 99번의 데이터가 왔으므로 이를 폐기한다. 그리고 다시 ACK120을 보낸다.
  • A는 ACK120을 받고, B가 119번 데이터까지 잘 받았구나 판단하고 120번 데이터부터 다시 보낸다.

ACK이 유실되었지만 그 다음 ACK이 정상적으로 도착했을 경우

A는 92번 ~ 99번까지 하나, 100번 ~ 119번까지 하나를 전송했고, B는 이에 대한 ACK100, ACK120을 전송했다. 이때 ACK100은 유실되었는데 ACK120은 도착했다.
따라서 A는 ACK120을 토대로 B가 119번까지 잘 받았구나 판단하고 Send 버퍼에서 119까지의 내용을 모두 지운다.

여기서 한가지 고민해볼 점이 있다.

수신자는 꼭 메시지를 받을 때 마다 ACK를 전송해야할까?

마지막 시나리오에서 보듯이 ACK100은 유실되었지만 ACK120은 제대로 도착했으므로 아무 문제가 발생하지 않았다. 그렇다면 굳이 ACK을 매번 보낼 필요가 있을까? 가장 마지막 ACK만 보내면 되지 않을까? 하는 의문이 생길 수 있다.

이를 보완하기 위해 TCP의 권고안(RFC 1122, RFC 2581)에서는 ACK를 바로바로 보내지 말고 일정 시간동안 기다렸다가 보내도록 권고하고 있다.

Fast retransmission

TCP는 Time-out이 발생해야지만 패킷의 유실을 파악할 수 있다. 그런데 이 Time-out까지 너무 시간이 오래 걸릴 수 있다.

예를 들어 100바이트의 데이터를 1바이트씩 보낸다고 해보자. 전송자는 1, 2, 3 ... 식으로 하나씩 보낸다. 그러다가 10번째 패킷이 유실되었다. 전송자 입장에서는 10번 패킷의 Time-out이 발생하기 전 까지는 10번 패킷의 유실 사실을 모른다. 그러다가 한참 뒤에 이 Time-out이 발생하고 그제서야 10번 패킷이 유실되었다는 사실을 알게 된다. 그렇다면 굳이 Time-out이 발생될 때 까지 기다려야 할 필요가 있을까? 하는 의문이 생긴다.

앞서 TCP는 데이터의 전송 순서가 보장된다고 했다. 즉 데이터가 순서대로 전송되고 순서대로 도착하게 된다.

수신자는 데이터가 도착하는대로 바로 ACK를 보낸다. 또한 Cumulative ACK이기 때문에 가장 최근에 정상적으로 도착한 데이터의 ACK만을 계속 보낸다.

따라서 10번 패킷이 유실되면 그 다음에 도착하는 11, 12, 13 ... 의 패킷은 모두 ACK100으로 응답된다. 동시에 수신자는 10번 패킷을 위로 올려야되는데 못올리고 있으므로 11, 12, 13 ... 의 데이터를 모두 버퍼에 저장해놓고 있다.

이때 전송자는 Time-out이 발생하기 전에, 특정 번호의 ACK가 계속해서 들어온다면 그 데이터가 유실되었다고 판단할 수 있다.

이에 기반해서 TCP는 Three Duplicated ACK 즉, ACK10을 이미 받았는데 그 뒤로 ACK10이 3번 더 오는 경우 (총 4번의 ACK10이 온다는 점을 유의해야한다.) 그 데이터가 유실되었다고 판단해도 무리가 없다고 권고하고 있다. (강제성은 없다.)

이처럼 Three Duplicated ACK를 기반으로 재전송하는 기법을 Fast Retransmission이라고 한다.

다만 이 방법은 어디까지나 최적화 권고 기법이므로 꼭 지켜야 할 사항은 아니다.

profile
하고싶은건 많은데 시간이 없다!
post-custom-banner

0개의 댓글