2010년 4월 13일 화요일

2009년 10월 8일 목요일

[TCP] Sequence Number

Sequence Numbers

받은 ACK는 보낼 SEQ가 된다.
보낼 ACK는 받은 SEQ+N이 된다.
  • SYN : N = 1
  • FIN  : N = 1
  • PSH : N = LEN

[TCP] Pipeline 전송 2 : TCP에서의 Pipeline.

파이프 라인 기법에 대한 개괄적인 설명을 했으니 이제 TCP에서 왜 파이프라인 전송이 필요한지 알아보자. 앞으로 언급할 예에서 나올 수치는 계산하고, 이해하기 쉽도록 과장한 부분이 많을 것이기 때문에 실제적인 수치라 생각해서는 안된다.

 

다음과 같은 상황을 생각해보자.

 

- 서울에 있는 사람이 부산에 있는 사람에게  10Mbit짜리 파일을 전송한다. 파일 전송 시 거리에 따른 지연 시간은 10초이다.

- 네트웍에는 트래픽 부하가 전혀 걸려있지 않고 두 사람만 사용하고 있다. 즉 두 사람은 네트웍의 모든 전송 속도를 사용할 수 있다.

- 이 네트웍은 초당 1Mbit를 전송할 수 있다.

- 계산상의 편의를 위해 Gbit, Mbit에서의 단위는 1024가 아니라 1000로 정한다.

- 수신자는 패킷을 받자마자 ACK를 보내고, 그 ACK의 크기는 송신된 패킷과 같다고 가정한다.

 

이 상황에서 TCP가 파일을 1Kbit의 MSS로 분할하여, 한 패킷 전송 후 ACK를 받은 후 그 다음 패킷을 전송한다고 하면 서울의 송신자는 얼마동안 일을 하게되는가?

 

t=0 가 최초로 전송을 시작한 때라고 생각하고 각각의 상황을 좀 더 자세히 생각해보자.

 

t=0 : 서울의 송신자가 첫 번째 세그먼트를 전송하기 시작한다. 네트웍이 초당 1Mbit를 보낼 수 있으므로,

       첫 번째 비트부터 마지막 비트를 네트웍에 전달하기까지 0.1초가 걸릴 것이다.

t=0.1 : 송신자가 세그먼트의 마지막 비트를 네트웍에 전달했고, 이 마지막 비트가 부산에 도착해야 수신자는 패킷을 인식할 것이다.

t=10.1 : 서울부터 부산까지의 거리 지연 시간이 10초이므로 이 때에 마지막 비트가 부산에 도착한다. 수신자가 패킷을

         받자마자 Ack를 한다고, 또한 그 ACK의 size도 패킷과 동일하다고 가정했기 때문에 바로 Ack 과정이 시작된다.

t=20.1 : 수신자가 Ack한 패킷의 첫번째 비트가 서울에 도착한다. 이제부터 송신자는 네트웍으로부터 ACK 패킷을 받는 동작을 시작한다.

t=20.2 : 송신자가 모든 패킷을 받았다.

 

이 과정에서 송신자가 일하는 시간을 패킷의 전체 송,수신 시간에 대한 비율로 계산해보자.

총 시간 20.2초 중에 송신자는 네트웍으로 데이터를 전달하고, 수신하는 0.2초만 바빴다. 나머지 시간은 ACK가 오기를 기다리면서 멍청히 아무 일도 하지 않았다는 것이다. 비율로 따지면 0.99퍼센트이다. 이것은 웃기지도 않은 효율로서 이 프로토콜을 사용하여 예에서 들었던 네트웍을 사용하면, 아무런 트래픽 부하가 없는 즉, 1Mbps의 속도를 full로 사용할 수 있는 네트웍을 혼자 사용한다 하더라도, 9.9Kbps정도의 속도밖에 사용할 수 없다는 의미이다. 이것은 잘못 설계된 프로토콜이 얼마나 망의 속도를 제한하는지를 보여주는 예라 할 수있다.

 

이러한 문제점을 해결하기 위해 TCP는 파이프라이닝 전송을 사용한다. 즉 패킷에 대한 응답이 도착하기 전에 다음 패킷을 전송시킨다는 것이다. 이것은 우리가 다루었던 송, 수신자의 Idle time을 줄이는 올바른 해결책이지만, 언제나 그렇듯이 또 다른 문제를 발생시킨다. 그것은 패킷 전송 도중 손실된 패킷이나 손상된 패킷이 발생했을 때 그것을 해결하기 위한 좀 더 복잡한 방법을 필요로 한다는 것이다. 그 점에 대해서는 다음 포스트에서 다루도록 하겠다.


[TCP] Pipeline 전송 1 : Pipeline이란 무엇인가?

이제까지 신뢰적인 데이터 전송에 대해 살펴봤다. 데이터를 손실없이 목적했던 곳으로 보내는 것은 우리가 인터넷을 안심하고 사용하는데 있어서 가장 중요한 요소라고 말할 수 있다. 그래서 그런 중요한 부분을 해결하기 위해 TCP는 여러 가지 기법을 적용했다. 그렇다면 데이터가 신뢰적으로 전송된다면 그것으로 모든 것이 해결된 것인가?

 

요즘과 같이 콘텐츠가 홍수처럼 쏟아지고 동영상을 인터넷으로 실시간 플레이해서 보는 경우가 많은 때에는 데이터의 신뢰적인 전송 외에도 그 데이터의 속도 역시 매우 중요한 factor가 된다. 물론 데이터의 속도는 데이터를 많이 보낼 수 있는 매체(광 LAN과 같이...)에 기반하지만, 그 기반이 잘 갖춰진 상태라 하더라도 데이터를 보내는 송신자가 그 속도를 충분히 이용할 수 있도록 데이터를 보내지 않는다면 소용이 없는 것이다. 그렇다면 TCP는 어떤 방법으로 제공된 대역폭을 효율적으로 사용하는가? 그 방법 중 하나가 Pipeline 전송 방식이다. 이번 포스트에서는 TCP가 사용하는 pipeline 전송 방식이 어떤 것인지를 살펴보기위해, 우선 pipeline 전송 방식이라는 것이 무엇인지를 살펴볼 것이다.

 

누가 이름을 Pipeline이라고 붙였는지는 잘 모르겠지만.. 그리고 경우에 따라 여러 가지 용도로 그 의미가 해석되지만, 대체적으로 특정 process의 빠른 처리를 위해 어떤 하나의 operation이 완료되기 전에 그 다음 operation을 진행시키는 방식을 pipeline 방식이라고 한다. 이해를 돕기 위해 CPU에서의 pipeline 방식을 살펴보자. 보통 CPU는 특정 명령을 수행하기 위해 몇 가지 단계를 거치는 데 ARM7의 경우는

 

명령어를 메모리로부터 가져오고 (fetch)

가져온 명령어를 해석한 후(decode)

실행하는(excute)

 

세 가지의 단계를 거친다. 이러한 명령어 수행 단계 중에 CPU가 일을 안하고 놀게 되는 경우가 종종 생기는데, 예를 들어 첫번째 fetch의 과정에서 메모리가 CPU에 비해 속도가 많이 느릴 경우, 메모리에서 명령어를 가져오기까지 CPU가 일을 안하고 놀 수 있고, 또한 실행하는 과정에서도 CPU보다 속도가 현저히 느린 I/O장치 등의 결과를 기다려서 처리하는 과정에서도 그럴 수 있다. 이렇게 CPU가 일을 안하는 상태를 Idle상태라고 하는데 이처럼 CPU의 본래 성능을 다 활용하지 못하는 경우를 피하기 위해 명령어를 pipeline방식으로 실행시킨다. 즉 첫 번째 커맨드를 fetch할 때 메모리로부터 응답을 기다리는 동안 두 번째 커맨드의 fetch를 시작시키는 것이다. 이러한 방식은 CPU와 메모리를 모두 Idle상태에 들어가게 하지 않고 원래 가진 성능을 최대한 활용하게 해준다.

 

그렇다면 TCP는 어떤 방식으로 pipeline 방식을 전송 시에 사용하는가? 쉽게 생각할 수 있듯이 세그먼트를 보내고 응답이 올 때까지 기다리는 시간(Idle)을 없애기 위해 pipeline을 사용한다. 즉 특정 데이터의 전송 시에 Idle 타임을 최소화 시키기 위하여 pipeline 전송 방식을 사용하는 것이다.


[TCP] 신뢰적인 전송 5 : TCP는 실제 Sequence number와 Ack number field에 무엇을 채우는가?

사실 이 포스트를 Timer 전에 넣을까 뒤에 넣을까 나름대로 고민을 많이 했었는데, 왜냐하면 또 다시 Sequence number와 Ack number 이야기를 해야하기 때문이다. 이전 포스트들을 통해 Sequence & Ack number가 어떤 역할을 하는지에 대해 충분히 다루었기 때문에 별도로 추가해야 할 이야기는 없다. 다만 이번 포스트에서는 실제 TCP가 해당 field에 어떤 값을 채우는지에 대해서 다룰 것이다.

 

TCP의 가장 중요한 목적인 신뢰성 있는 전송이라는 측면에서 보면, TCP 패킷 헤더에서 가장 중요한 정보는 Sequence & Ack number이다. 그렇다면 TCP는 이 field에 무엇을 채우는가? 이것과 관련해서 TCP가 데이터를 어떻게 다루는지를 이해하는 것이 중요하다. TCP는 데이터를 바이트 단위로 다룬다. 즉 전송해야할 데이터를 순서대로 정렬된 바이트 스트림으로 취급한다는 이야기다. 따라서 모든 Sequence number와 Ack number는 바이트 단위로 표시된다.

 

설명을 하기 전에 새로운 용어에 대해서 말할 필요가 있을 것 같다. 바로 MSS (Max Segment Size)인데, 이것은 TCP가 한번에 보낼 수 있는 최대의 데이터 량을 의미한다. 또한 MSS를 직역하면 최대 세그먼트 크기인데 여기에서 TCP가 자신이 보내는 패킷의 기본 단위를 세그먼트라고 부른다는 것을 알 수 있다. 이제까지 설명을 할 때 좀 더 친숙한 용어인 패킷으로 설명했지만, 이제부터 TCP와 관련된 설명을 할 때에는 세그먼트라는 단어를 사용하도록 하겠다.

 

예를 들어 5000byte 짜리 파일을 전송하는 경우를 살펴보자. 이 때의 MSS가 100byte라고 생각하면 TCP는 5000 byte짜리 파일을 전송하기 위해 50개의 세그먼트를 생성하게 될 것이다. (5000 / 100 = 50). 이런 경우 TCP는 각 세그먼트의 Sequence Number Field에 Byte 기준 번호를 붙인다. 즉, 첫 번째 세그먼트의 Sequence number가 0이라 가정하면 두 번째 세그먼트는 100번, 세번째 세그먼트는 200번의 식으로 Sequence number가 설정된다.

그렇다면 Ack 번호는 어떠한가? Ack번호의 경우에는 Sequence number보다 약간 복잡한데, 그것은 말 그대로 받은 것에 대한 응답이기 때문이다. 기본적으로 Ack번호는 수신자의 입장에서 송신자로부터 앞으로 받아야할 다음 데이터의 Sequence number이다. 말이 복잡한데, 예를 들어보자. 위에서 든 예에서 만일 송신자가 첫 번째 세그먼트를 보냈다고 생각해보자. 수신자가 데이터를 받으면 그 데이터의 Sequence number는 0번일 것이고 MSS가 100 byte이기 때문에 0번째 byte부터 99번째 byte까지의 데이터가 도착했을 것이다. 그러면 수신자는 자신이 99번째 byte까지 잘 받았고, 앞으로 100번째 byte로 시작되는 세그먼트를 받기 원한다는 표시로 Ack번호를 100으로 붙여 응답하는 것이다.

 

이것은 송신자에게 있어서 수신자의 상태에 대해 부가적인 정보를 주는데, 이 정보는 자신이 어떤 데이터를 보내야할지, 즉 새로운 데이터 스트림을 줘야할지 아니면 이미 보낸 데이터를 재전송해야할지를 결정할 수 있는 중요한 것이다.

이 점을 이해하기 위해 또 다른 예를 들어보자. 위의 파일 전송에서 송신자가 순서대로 3개의 세그먼트를 보냈다고 생각해보자. 즉 Sequence number 0, 100, 200번의 3개의 세그먼트를 통해 300byte를 전송한 것이다. 그런데 첫번째 세그먼트와 세번째 세그먼트는 수신자에게 잘 도착했는데 두 번째 세그먼트가 네트웍에서 손실되었다고 생각했을 때 수신자는 송신자에게 어떻게 응답해야 하는가?

수신자의 입장에서 생각해 보면 현재 두 개의 세그먼트를 받았다. Sequence number 0번의 0~99번째까지의 100byte와 Sequence number 200번의 200 ~ 299번째까지의 100byte이다. 이 때 수신자는 중간의 100 ~ 199번째의 데이터를 기대한다는 의미로 Ack number 100으로 응답할 수 있다. 그러면 송신자는 수신자가 두 번째 세그먼트를 못받았다는 것을 알고 다시 재전송할 수 있다.

 

Reference : Computer Networking (A top down approach featuring the internet), James F. Kurose, Keith W. Ross 저.


[TCP] 신뢰적인 전송 4 : Timer, Time out, RTT

지금까지 TCP가 신뢰적인 데이터 전송을 위해 사용하는 필수적인 정보인 Ack bit, Sequence number, Ack number 대해 살펴보았다. 이번 포스트에서는 그에 더하여 재전송의 필수적인 기능인 타이머에 대하여 알아보도록 하겠다.

 

패킷을 전송함에 있어서 TCP는 패킷이 어떻게 목적지까지 전달되는지를 전혀 알지 못한다(심지어 전달이나 제대로 될지조차 알 수 없다). 그 이유는 패킷의 전송 경로 및 전송 여부가 TCP의 하위 계층 프로토콜인 IP에 의해 결정되기 때문이다. 이미 언급하였듯이 IP는 기본적으로 Best effort 서비스를 제공하기 때문에 패킷이 어떤 경로로 전달 되는지를 관리할 필요가 없다. 라우터의 입장에서 특정 패킷을 받았을 경우, 패킷의 목적지 주소를 확인하고, 자신이 가지고 있는 라우팅 테이블에 등록되어 있는 라우터들 중에서 목적지 주소로 가기에 가장 비용이 저렴한 라우터를 선택해 패킷을 전달하면 라우터는 자신의 역할을 모두 한 것이다.(자세한 내용은 IP를 다룰 때 설명하겠다.) 즉 자신을 포함하여 다음 라우터까지의 단 1홉에 해당하는 경로만을 관리하는 프로토콜인 IP에 의해 패킷이 전달되기 때문에 TCP에서는 자신이 보낸 패킷이 어떤 경로로 목적지까지 갈 것인지를 알 수 있는 방법이 전혀 없다는 것이다.

 

자신이 전송한 패킷이 어떤 경로로 갈지 알 수 없다는 것은 무엇을 의미하는가? 그것은 패킷이 목적지까지 도착하는데 얼마나 시간이 걸릴지 전혀 알 수 없다는 뜻이며, 이에 따라 당연히 응답이 올 때까지 얼마나 기다려야 할지 전혀 알 수 없다는 뜻이기도 하다. 그렇다면 TCP는 타이머를 돌리면서 얼마나 기다려야 할지, 즉 타임 아웃 시간을 얼마로 설정해야할지 어떻게 결정하는가?

 

TCP가 타임 아웃 시간을 어떻게 설정하는지를 살펴보기 전에, 우선 타임 아웃 시간에 대한 기본적인 사항들을 생각해보자. 타임 아웃 시간은 얼마나 길어야 하는가? 쉽게 생각할 수 있듯이 "Time out > 패킷이 목적지까지 가는 시간 + 응답이 되돌아오는 시간" 이다. 만일 타임 아웃 시간이 왕복 시간보다 짧다면 패킷은 불필요하게 재전송 될 것이다. 그럼 타임 아웃을 좀 길게 잡으면 어떨까? 이것 역시 답이 될 수 없다. 이미 말했듯 왕복 시간은 얼마나 길어질 지 아무도 모르기 때문에 여유있게 잡는다고 잡은 타임 아웃 시간보다 훨씬 길 수 있으며, 또 다른 더 큰 문제는 실제로 패킷이 손실되어 정말로 재전송을 해야할 때에 너무 긴 타임 아웃으로 인해 심각한 시간 지연을 가져 올 수 있다는 것이다. 이와 같은 사실을 통해 타임 아웃 시간은 "고정된" 값을 가져서는 안된다는 것을 알 수 있다.

 

그러면 이제 실제로 TCP는 이러한 타이머의 타임 아웃 시간을 어떻게 결정하는지 살펴보자.

타임 아웃 시간을 결정하는 알고리즘은 여러 가지가 있을 수 있지만, 그 중 Van Jacobson이 1988에 제안한 방법에 근거하여 설명하겠다. 이와 관련된 더 자세한 내용을 알기 원한다면 RFC2988을 찾아보기 바란다.

 

우선 설명에 앞서서 계산식에 사용할 변수들을 정의하겠다. 이 정의는 RFC2988에 근거한 것이다.

- RTO (Retransmission Time Out) : 최종적으로 구해야 할 타임 아웃 시간.

- RTT (Round Trip Time) : 패킷이 목적지에 도달한 후, 그에 대한 응답이 돌아오기까지의 시간. 즉 패킷의 왕복 시간. 각 패킷마다 전송 경로 및 그 때마다의 네트웍 부하가 틀리므로 매번의 RTT는 각 패킷별로 틀릴 수밖에 없으며 RTT는 이러한 각 패킷마다의 개별적인 왕복 시간을 의미한다.

- SRTT (Smoothed RTT) : 각 RTT들의 가중 평균치.

- RTTVAR (RTT variation) : 최근의 RTT가 이전까지의 평균 RTT에 비해서 얼마나 빗나갔는지를 나타내는 가중치.

 

용어 설명에서 일부 설명이 되었지만, RTT는 패킷이 TCP단에서 IP단으로 넘어간 시점으로부터 ACK가 되돌아올 때까지의 총 시간을 의미하고, 각 패킷마다 그 때의 망 상황과 경로 선택에 의해 매번 변경될 수 밖에 없다. 따라서 우리는 RTO를 결정하기 위해 이 RTT의 어떤 평균치를 구해야 한다는 것을 알 수 있다. 그러나 단순히 각 RTT를 더한 후, 총 회수로 나누는 단순 평균은 적절치 않은데, 그 이유는 쉽게 생각할 수 있듯이 가장 최근의 RTT가 현재의 망 상황을 더 잘 반영하기 때문이다. 따라서 가장 최근에 측정된 RTT의 비중이 크게 적용되도록 평균을 구해야 하며 이런 식으로 구해진 평균 RTT를 SRTT이라고 부른다. (이와 같은 통계 기법을 EWMA- Exponential Weighted Moving Average라고 하며 예전 값들이 전체 평균에 미치는 영향이 지수적으로 감수하므로 Exponential이라고 부른다).

 

위와 같은 사실들을 기억하고 다음 식을 보자. 다음 식은 SRTT를 구하는 식이다.

 

SRTT_n = ((1-x) * SRTT_m) + (x * RTT)

 

위의 식을 보면 이전까지의 평균치(SRTT_m)와 최근에 측정된 RTT에 가중치(x)를 곱한값을 더해서 가중 평균치(SRTT_n)을 구하고 있다는 것을 알 수 있다. 여기에서의 가중치 x는 일반적으로 0.125 (1/8)인데 이 수치는 V. Jacobson과 M. Karels가 제안한 [JK88] - Congestion Avoidance and Control을 참조하면 된다. 이 수치를 넣어서 위의 식을 다시 정리하면

 

SRTT_n = (0.875 * SRTT_m) + (0.125 * RTT)

 

가 된다.

 

우리가 지금까지 구한 것은 RTT값이었다. 즉 패킷을 전송한 후, 그 패킷이 돌아올 때까지의 평균 시간을 구한 것이었다. 그렇다면 우리가 정작 구해야 할 RTO는 어떻게 결정해야 할까?  명백하게 타임아웃 값은 Ack가 돌아오기 전에 만료되어서는 안된다는 점을 생각해볼 때, RTT의 평균치에 약간의 여분을 더해서 설정되어야 한다. 즉

 

RTO = SRTT + (4*RTTVAR)

 

이다. 여기에서 왜 RTTVAR에 4를 곱하는지에 대한 이유는 아직 찾지 못했다. 어떤 근거가 있을텐데 나중에 찾게 되면 이 포스트를 다시 업데이트 할 예정이다. 우선 RTTVAR은 최근의 RTT가 SRTT에 비해 얼마나 빗나가있는지를 나타내는 정도라고 얘기했다. 그렇다면 위의 식을 생각해볼때, RTTVAR가 크면, 즉 최근의 RTT가 평균치인 SRTT보다 매우 크거나 매우 작으면 RTO는 크게 변한다는 것을 알 수 있다. 그렇다면 RTTVAR은 어떻게 구하는가? RFC2988에 따르면 다음과 같다.

 

RTTVAR_n = ((1-x)*RTTVAR_m) + (x*|RTT - SRTT|)

 

이다. RTTVAR 역시 SRTT와 마찬가지로 변동치 값에 대한 가중 평균치임을 알 수 있다. 여기에서 x는 0.25(1/4)로 제안되었는데, 이 수치 역시 [JK88] - Congestion Avoidance and Control을 참조하면 된다.

 

Reference : RFC2988, RFC1122

[TCP] 신뢰적인 전송 3 : Ack number

이전 포스트를 통해 Sequence Number가 송신자의 입장에서 오류가 있거나 변형된 Ack 혹은 Nak을 받았을 때, 오류로부터 회복하기 위한 방법으로 재전송을 쓸 수 있도록 해주고, 그 때 수신자로 하여금 송신자가 보낸 패킷이 자신이 이미 가지고 있는 패킷이 다시 재전송된 것인지, 아니면 정말 새로운 패킷인지 구분할 수 있게 해준다는 것을 다루었다.

 

사실 재전송은 송신자의 입장에서 봤을 때, 만병통치약과 같다. 내가 의사전달을 하려고 하는데 상대방이 못알아듣는다면, 알아들을때까지 다시 얘기하면 되는 것이다. 하지만 좀 더 깊이 생각해보면 재전송이라는 만병통치약을 무작정 아무 때나 사용할 수 없다는 것을 알 수 있다.

 

우선 생각해봐야 할 사항은 재전송을 해야할 때는 언제인가? 라는 것이다.

Ack나 Nak이 정확하게 도착을 했다면 좋지만, 설령 오류가 생겼더라 하더라도 송신자에게 도착만 한다면, 송신자는 재전송 여부를 쉽게 결정할 수 있다. 그러나 실제 우리가 사용하는 네트웍에서는 종종 패킷이 아주 많이 지체가 되거나 아예 손실이 되어 도착하지 못하는 경우가 있을 수 있다. 그러면 송신자는 그 점에 대한 판단을 무엇으로 해야하는가?

쉽게 생각할 수 있듯이 그 점에 대한 답은 바로 타이머다. 일정 시간을 기다려보고 그 시간이 지났는데도, 응답이 오지 않는다면 다시 재전송하면 되는 것이다. 그렇다면 이번에는 그 일정 시간은 어떻게 정해야 하는가라는 질문이 생긴다. 사실 이 기준은 명확하지가 않다. 송신자는 일단 내보낸 패킷이 어떤 경로를 통해 목적지까지 갈지 전혀 알 수가 없기 때문에 정상적인 왕복 시간을 알 수 있는 방법이 없다. 그럼에도 불구하고, 송신자는 이 타이머의 타임 아웃 시간을 "현명하게" 결정해야 하는데, 이 점을 해결하기 위한 방법은 추후 Round Trip Time에 대해 다룰 때 설명할 것이다.

 

재전송과 관련하여 생각해봐야 할 두번 째 문제는 재전송이 만병통치약인 동시에 골칫거리라는 점이다.

송신자의 입장에서, 송신 경로는 트래픽 상황이 좋은데, 수신 경로는 과도한 트래픽에 의해 정체되고 있다고 가정을 해보자. 이러한 상황에서 송신자가 A라는 패킷을 수신자에게 보냈다면, 송신 경로의 상태는 좋기 때문에, 수신자는 이 패킷을 잘 수신했을 것이다. 그러나 수신 경로는 상황이 안좋기 때문에 수신자가 보낸 B라는 Ack는 송신자의 타임아웃이 지날만큼 오랜 시간동안 네트웍에서 지연된 후에 송신자에게 도착할 것이다.

이러한 상황은 정상적인 Ack가 송신자에게 도착했음에도 불구하고 재전송을 일으키게 하고 수신자 입장에서는 이미 받은 패킷을 또 받는 상황을 발생하게 한다. 이미 이전 포스트에서 언급했듯이 Sequence Number가 있기 때문에 수신자 입장에서 패킷을 수용할 것인지의 여부를 결정하는 것은 어렵지 않다. 하지만 수신자가 다시 이 중복된 패킷에 대해 Ack를 보낼 때 송신자는 이 Ack가 가장 최근에 전송된 패킷에 대한 응답인지, 중복된 패킷에 대한 응답인지 알 수 없는 문제가 생긴다.  

 이 문제를 해결할 수 있는 것이 바로 Ack Number이다. Ack number는 송신자로 하여금 자신이 받은 Ack 혹은 Nak이 자신이 보낸 패킷 중 어느 패킷에 대한 응답인지를 알 수 있게 해주면서, 그와 동시에 수신자가 어떤 패킷까지 잘 받고 있는지에 대한 상황 정보를 알려준다.

 

즉 신뢰적인 전송의 핵심은 바로 재전송이며, 그 재전송을 언제, 어떻게 할 것인지에 대한 정보를 Ack bit와 Sequence number, Ack Number를 통해 결정한다고 생각하면 되겠다. 물론 그와 관련된 timer의 역할 역시 중요하지만 그 점은 추후에 다시 다루도록 하겠다.

 

Reference : Computer Networking (A top down approach featuring the internet), James F. Kurose, Keith W. Ross 저.