[Network] 전송계층 2
💻CS/Network

[Network] 전송계층 2

728x90
반응형

http://www.kocw.net/home/cview.do?mty=p&kemId=1169634 

 

이전에 Reliable Data Transfer를 구현하기 위해 어떤 것들이 필요했는지 이야기 했었다. 

RDT를 위한 파이프라인 형태의 전송 방식이 필요했는데 이에 사용되었던 것들이 go-back-N, Selective Repeat이었다.

 

이번에는 본격적으로 TCP에서 이야기 해볼 것이다. 

(TCP의 헤더 필드에 대해서)

티시피 헤더 알아보기. segment structure, rdt 

 

  • TCP
    • point to point : 한 쌍의 통신을 한다. 프로세스와 프로세스들 간의 통신만을 관장한다. 즉 1:1(한 쌍)이며 더 자세히 보면 하나의 소켓과 하나의 소켓(소켓 한 쌍)끼리의 통신을 책임진다. 
    • reliable, in order : 데이터는 유실 없이 순서대로 전달된다.
    • pipelined : sender는 한 꺼번에 전송한다. 그 단위는 window
    • full duplex(양방향) : 사실 모두가 sender이면서 receiver이다. request를 보내고 response를 받는 것은 입장에 따라 다르게 볼 수 있다. A의 입장에서는 내가 request를 보내고 response를 받는 것이고, B의 입장에서 보면 내가 request를 받고 response를 보내는 것이기 때문이다.
      • 그렇기에 모두 sender buffer, recevier buffer를 한 쌍으로 가지게 된다. (각각에 대응된다)
      • sender buffer에서 window size만큼 전송하게 되는데, receiver측에서는 out of order를 막고자 모든 데이터를 저장하게 되는데, 이를 위해 receiver buffer의 크기는 window size와 동일하게 된다. 
    • retransmission timer는 하나만 사용한다 : go-back-N과 유사하게 하나의 타이머만을 사용하지만 go-back-N은 타임아웃시 유실된 부분만 재전송하는게 아니라 window size(N)만큼 다시 재전송하고 TCP는 유실된 해당 데이터만 재전송한다는 점에서 차이가 있다. 

 

아래 추가적인 TCP의 3가지 특징은 다음 시간에 알아보자.

  • connection-oriented : 데이터 교환 이전에 handshaking을 통해 sender/receiver를 초기화한다. 
  • flow-controlled : receiver의 역량만큼 전송한다
  • congestion controlI : 네트워크상 수용 가능한 만큼만 전송한다

 

TCP Segment structure

 

레이어(계층) 전송 단위
APP Message
TCP Segment
IP Packet
Link Frame
Physical -

위에서 아래 계층으로 전달할수록 점점 더 큰 편지봉투에 내용을 담는다고 생각하면 된다. 

 

먼저 APP 계층의 message부분이 소켓을 통해 TCP에 내려오게 되는데, 이 때 message는 segment의 data 부분에 들어오게 된다. 

 

Segment

Header Data
부가정보 APP Message

그리고 segment 부분은 다시 또 소켓을 통해 IP로 내려가게 되며, 이 때 segment는 packet의 data 부분에 담기게 된다. 

 

IP

Header Data
부가정보 TCP Segment

같은 방법으로 frame에게도 전달되게 되며, 이 방식은 봉투가 더 큰 봉투에 담기는 모습과 유사하다고 봐도 무방하다. 

또한 TCP 부분을 이야기하고 있기에 편지 봉투에 해당하는 Header만 보면 됨.

(우체국 배달부가 편지지 내용에는 관심이 없고, 봉투에 적힌 내용을 기반으로 일을 하는 것 처럼)

 

헤더의 필드를 봐보자. 

 

TCP Header

헤더는 32비트로 source/destination 포트 넘버가 반반씩 차지하고 있어 각 포트 넘버의의 범위가 2^16 - 1정도인 65,500정도까지 될 수 있다는 점을 알 수 있다. 

source는 전송자, dest는 수신자를 뜻하며, seq#, ACK #등 많이 본 것들이 등장한다. 

  • checksum : 네트워크 거쳐오면서 에러가 있었는지 확인하는 error detection 역할을 한다. 
  • receive window : 내 receiver buffer의 빈 공간을 계속 리포트 해줘야 sender에서 확인하고 딱 그만큼 보내주게 된다. 

 

그렇다면 실제로 Segment header의 필드 정보에는 뭐가 적힐까?

"C"(한 바이트)라는 캐릭터를 보내고자 하는 상황에서 해당 데이터는 TCP Segment Data 부분에 담긴다. 

seq #의 경우 전송하고자 하는 바이트들 중 가장 앞에 있는 숫자를 가져오게 된다. (0~5를 보내는 경우 : 0번  / 6 ~ 10를 보내는 경우 : 6번)

 

TCP의 ACK는 cumulative이기에 ACK 10의 의미는 9번까지는 오케이 잘 받았다, 이제 10번 보내주라고 하는 것이다. 

 

Host A가 seq42를 보내주어 Host B가 받았으니 이제 43을 기다리게 되어 ACK 43을 다시 보내주게 되는 것이다. 

또한 ACK 79를 Host B가 받고난 후, 79를 기다리는 것을 알았으니 seq 79를 보내주게 된다

그리고 다시 Host A는 (seq 79, ACK 43)을 받았기에, receiver가 기다리는게 43번이라는 것으로 인지하고 seq 43을 보내고, 79번까지는 잘 받았기에 ACK80을 보내주게 되는 것이다. 

 

사실 Host B가 만든 것이 실제로 B가 보내고자 했던 메세지인지 그냥 A의 request를 받아 내뱉는 에코인지 구분이 안된다. 이를 구분해주기 위한건 사실 편지지에 해당하는 Header에 적혀야한다. (그냥 C가 아니라 프로토콜상 헤더에 구분자가 존재할 것이다. 예시에서는 C만 갔으니 사실상 현재는 헤더가 없는 것과 다름이 없다)

 

Timeout

Host A ~ B 사이에서 segement 유실이 일어난 경우 대부분 타이머를 통해 탐지한다. 

이에 타임아웃 값을 어느정도로 설정하는게 좋은지에 대해서는 지난번에도 알아보았지만, 

작게하면 리액션 빨라서 좋은 반면, 네트워크 오버헤드도 증가하게 되고, 반대로 타임아웃 값이 크게 되면 또 반대의 상황을 겪게 된다. 그래서 적당한 타임아웃 값을 설정하는게 좋다고까지 알아봤다. 

 

그래서 사람들은 RTT(Round Trip Time) 즉, sender-receiver-sender로 돌아오는 왕복 시간을 기준으로 삼기로 했다. 

하지만 RTT는 Host A ~ B에서 주고받는 segement들 마다 값이 천차만별이어서 하나의 RTT 값으로 이를 통제한다는 것은 매우 비효율 적이었다. 즉 RTT가 고정적인 경우에서나 잘 먹히는 방법이라는 것인데, 현실에서는 RTT가 고정적이지 못하다는 것이다. 

 

RTT가 매번 다른 이유는 다음과 같다. 

  • Segment들이 지나가는 경로가 달라서
  • Segment들이 지나가는 경로가 같더라도 RTT가 다른 이유
    • Queueing Delay 가 상황마다 다르다
    • 같은 운전길이어도 교통상황에 따라 도착시간이 달라지는 것 처럼 segment 전송도 마찬가지이다. 

즉 핵심은 하나의 RTT 값을 두기가 어렵다는 것이다!

 

실제 RTT와 EstimatedRTT

SampleRTT(실제RTT / 파란색)는 중구난방인 모습을 볼 수 있다. 이에 가중치를 부여하여 조정해보고자 했는데 이것이 EstimatedRTT이다. 

EstimatedRTT(핑크색)은 안정된 것을 볼 수 있는데 이는 SampleRTT를 보정한 것이다.

하지만 이 경우에도 타임아웃이 너무 타이트 할 때가 있어서 실제 유실이 발생하지 않았음에도 타임아웃이 날 수도 있다.

 

계속 이러한 상황이 반복되다보니 유실이 확실 할 때 타임아웃이 되는 것이 필요해졌다.  

 

이에 devRTT (deviationRTT)라는 값이 등장했는데, 이는 estimatedRTT에 마진을 붙인 것이다. 

 

왼쪽의 경우 Host A가 보낸 Segment를 보면 (seq 92, 8byte)인 것을 알 수 있다. 92 ~ 99번까지 세그먼트가 가는 것이다. 이에 99번까지 Host B가 잘 받았다는 의미로 ACK 100를 보냈는데 유실된 경우이다. Host A가 타임아웃되어 유실되었다고 판단하여 재전송을 하고 다시 ACK100을 받아내는 모습을 볼 수 있다. 

 

오른쪽의 경우 Host A가 92~ 99번 보내고 100~119번 연달아 보내는 것과 같은 경우이다. Host B는 잘 받아서 ACK 100과 120을 보내는데, 그 사이 seq 92가 타임아웃 나서 재전송한 것을 볼 수 있다. 

하지만 Host B는 이미 120번을 기다리고 있는 상황이다(ACK120). 하지만 Host A는 seq 92를 보내니, 리시버는 120을 기다리는 상황이어서, 해당 segment를 버린다. 하지만 ACK가 cumulative하기 때문에 ACK 120을 Host A에게 보내준다.

Host A는 다시 120번을 확인하고 보내줌. (119까지 잘 받았구나 이해함.)

 

이 경우 Host B가 보낸 ACK 100이 유실된 상황이다. 하지만 ACK 120은 도착해서 이미 다 받았다고 처리되어 이전것도 받았다고 할수 있다. 이 또한 cumulative ACK의 힘이다! 앞에 부분이 유실되어도 마지막 부분만 오면 전체 클리어 가능하다!

 

지금까지 보면 타임아웃이 되어야 segment의 유실을 판단하는 모습을 볼 수 있었다. 하지만 타임아웃을 기다리긱에는 너무 길어서 타임아웃되기 전에 유실을 판단할 수 있는 방법을 알아보자.

 

실제 윈도우 사이즈가 100이어서 세그먼트 100개 보내는 상황이라고 해보자. 이 때 10번만 유실되는 상황이라고 했을 때, 기존에는 10번의 해당 타이머가 터져야 10번이 유실되었음을 알게 되었다. 하지만 실제로 보면 ACK9까지 받고 ack 10, ack 10... 계속 반복해서 내뱉는 것을 볼 수 있는데, 이 때 receiver buffer에서는 10번은 공란이고 11, 12, 13 그 이후 부분들만 계속 들어가면서 ACK 10만 뱉게 된다.

 

이에 특정 번호가 3번 반복되면 그건 유실이라고 판단하기로 했다. 초기에 ACK 10을 받고 추가 중복으로 3번 받으면 유실인 것이다. 이 방법이 타임아웃 터지기 전에 재전송을 권고하는 방식이며, Fast retransmit 매커니즘의 개념이다. 꼭 필요하진 않으나 더 빠르게 효율적으로 동작하기 위한 최적화 기법이다. 이렇게 중복으로 판단한다고 해도 그래도 타이머는 무조건 있어야 한다. 타이머는 골키퍼 같은 존재이고 Fast retransmit은 공을 막아주는 수비수...? 정도로 생각해도 좋을 것 같다. 

(ACK 10 받고 10 10 10 나오면 유실되었다고 판단 3dup (총 4번인 것) )

728x90
반응형