Computer Network

[Lecture 7, 8] TCP Service

Sara.H 2020. 7. 15. 11:46

[강의 내용만 대략적으로 요약]

 

1. TCP 의 전반적인 특징에 대하여 

* point-to-point : 항상 단일 송신자와 단일 수신자 사이의 점대점 연결이다. 하나의 송신자가 여러 수신자에게 데이터를 전송하는 멀티캐스팅은 불가능하다. 

* 데이터의 순서를 보장한다. 

* pipelined : 한꺼번에 많은 메시지를 보낼 수 있다. 

* full-duplex : 양쪽에서 이야기한다. 

* 하나의 소켓마다 별도의 TCP 를 갖고 (송신버퍼 수신버퍼, 그리고 데이터 전송과 관련된 여러 자료구조 및 변수들) 동작한다. 

 

* TCP 가 전송하는 세그먼트의 크기? 

TCP 명세에는 TCP가 자신이 편한대로 세그먼트의 데이터를 전송한다고 기술되어있다. 세그먼트의 크기는 최대 세그먼트 크기로 제한되고, 이는 통상적으로 1500byte 이다. (MSS; Maximum Segment Size) MSS는 혼잡제어 부분에서 상대방에게 보내는 세그먼트의 단위로 사용된다. 뒤에서 더 자세히 설명하겠지만, 가장 처음에는 네트워크의 혼잡 상태를 모르기 때문에 하나의 세그먼트를 전송하면서 연결이 시작되는데, 이 때의 전송 크기는 1MSS 이다. MSS 는 헤더의 크기를 제외한 어플리케이션에서 받은 데이터의 크기이다. 

 

TCP 세그먼트의 구조

TCP 세그먼트의 헤더는 위와같이 복잡하게 이루어져 있다. 이는 위에서 언급한 TCP 의 대략적인 특징들을 지원해주기 위한 목적으로 존재하는 요소들이다. 

 

* Application Data : 데이터필드는 어플리케이션에서 넘겨받은 데이터를 담는다. 데이터필드의 크기는 MSS 의 크기로 제한된다. 

* Sequence Number Field 와 Acknowledgement number field 는 신뢰적인 데이터 전송을 위해 사용된다. 

* 수신 윈도우 (rwnd) 는 흐름 제어에 사용된다. 이는 수신자가 받아들이려는 바이트의 크기를 나타내는 데 사용된다. 

* 헤더 길이 필드는 32비트 워드 단위로, TCP헤더의 길이를 나타낸다. TCP헤더는 TCP option 필드 때문에 가변적인 길이가 될 수 있다. 

* 선택적이고 가변적인 길이의 option 필드는 송신자와 수신자가 최대 세그먼트 크기를 현상하거나 고속 네트워크에서 사용하기 위한 윈도우 확장 요소로 이용된다. (흐름제어 및 혼잡제어시에 좀 더 유연하게 대처할 수 있도록 한다.)

* Flag 는 6비트이다. ACK비트는 확인응답 필드에 있는 값이 유용함을 가리키는 데 사용된다. 즉, 이 세그먼트는 성공적으로 수신된 세그먼트에 대한 확인응답을 포함한다. 

* RST, SYN, FIN 비트는 TCP 초기 연결 설정과 해제에 사용된다. PSH비트가 설정될 때 이것은 수신자가 데이터를 상위 계층에 즉시 전달해야 한다는 것을 가리킨다. URG비트는 이 세그먼트에서 송신 측 상위 계층 개체가 긴급으로 표시하는 데이터임을 가리킨다. 

TCP 순서번호, ACK 

* 순서번호 : 세그먼트 안에 담긴 바이트의 순서에 붙여진 번호이다. 만약 100바이트짜리를 10바이트짜리의 세그먼트로 쪼개서 전송한다면 1번 세그먼트는 1~10 이라는 순서번호를 갖게 될 것이고, 2번 세그먼트는 (10바이트+ 1) 11 ~ 20 이라는 순서번호를 갖게 될 것이다. 

* ACK : 송신자가 세그먼트를 전달하면 이에 대한 응답으로 수신자는 또 다른 세그먼트를 보낸다. 이 때 Acknowledgement number 라는 필드에는 수신자가 기대하는 다음 세그먼트의 순서 번호를 적는다. 위의 예를 그대로 활용하면 1번 세그먼트를 송신한 후 응답으로 받는 세그먼트의 ACK 넘버 필드에는 11이 적혀있을 것이다. 

* 이렇게 이전에 받은 세그먼트의 순서번호를 고려해 누적하는 식으로 ACK넘버 필드를 채우는 것을 cumulative ACK라고 한다. 

 

Cumulative ACK

2. TCP가 데이터 손실에 대처하는 방식 

TCP에서는 보내는 측에서 타이머를 설정하고, 만약 보낸 세그먼트에 대한 ACK가 일정 시간동안 도착하지 않으면 해당 세그먼트를 재전송한다. 

이 때 타이머의 주기는 예상되는 Round Trip Time 에다가 약간의 마진을 더한 만큼의 시간이다. 이렇게 설정하는 경우 타이머가 불필요하게 길어져 지연이 발생할 수 있다. 이 문제를 개선하기 위한 방법으로 송신자의 타이머를 마냥 기다리지 않고 수신자의 응답을 보고 판별하는 방법이 있다. 수신자측에서 보내오는 duplicate ACK 넘버의 개수를 측정하여 3개가 넘으면 재전송하면, 불필요한 ACK 를 줄이고, 빠르게 세그먼트를 재전송 할 수 있을 것이다. 

 

3. TCP 의 흐름 제어 메커니즘 (Flow Control)

보내는 쪽에서 받는 쪽이 감당할 수 없을 만큼의 데이터를 계속해서 보낸다면 수신자측의 버퍼가 넘칠것이다. 이에 따라 보내는 양을 조절해야 하는 필요성이 생긴다. 수신자 측에서는 응답 세그먼트에 현재 얼만큼의 여유공간이 있는지 헤더에 적는다. 이를 receive window (rwnd) 라고 부른다. 추후에 학습할 혼잡 제어(Congestion Control)를 위해서는 Congestion Window(cwnd) 라는 것을 사용한다. 실제 전송 사이즈는 rwnd 와 cwnd 중 작은 값으로 선택한다. 

Flow Control 

송신자 측에서는 응답을 보고 자신의 윈도우 사이즈를 동적으로 조절하여 수신자측으로 너무 많은 데이터가 가지 않도록 조절한다. 

하지만 이와같은 메커니즘에는 다음과 같은 코너케이스가 존재한다. 

* 만약 rwnd 가 0인 경우는 어떻게 되는가? => Probe Packet 을 보낸다. (다시 확인해봐야 할 부분)

 

추가) Silly Window Syndrome 에 대해서 알아보기. 

 


[TCP 의 흐름제어 : 교과서로 보충]

 

* 흐름 제어가 필요한 이유 

TCP는 데이터를 받으면 이를 수신 버퍼에 저장한다. 이 데이터를 받기로 한 애플리케이션은 버퍼에서 데이터를 읽어갈 것이지만, 반드시 받자마자 읽어가지는 않을 것이다. 실제로도 애플리케이션은 다른 작업들로 바쁠 가능성이 높다. 따라서 송신하는 버퍼가 수신 버퍼의 상태를 전혀 고려하지 않고 데이터를 계속해서 보낸다면 수신자측의 버퍼는 오버플로우가 날 것이다. 오버플로우 방지를 위해서 흐름 제어가 필요하다. 

 

=> 흐름제어 : 받는 쪽과 보내는 쪽의 속도를 일치시키는 서비스. 

 

* 흐름 제어를 위해 필요한 도구 : 수신 윈도우 변수 

송신자가 rwnd 변수를 유지하여 흐름 제어를 제공한다. 수신 윈도우는 수신측에서 가용한 버퍼 공간이 얼마나 되는지를 송신자에게 알려주는 역할을 한다. TCP는 전이중 방식이므로 각 측의 송신자는 별개의 수신 윈도우를 유지한다. 

 

* 예를 들어, 

호스트 A가 호스트 B에게 큰 파일을 전송한다고 하자. 이 때 호스트 B는 이 연결에 수신 버퍼를 할당한다. 이 때 할당된 수신 버퍼의 크기를 RcvBuffer 라고 명명한다. 시간 나는 대로 호스트 B의 어플리케이션 프로세스는 버퍼로부터 데이터를 읽으며 다음과 같은 변수들을 정의한다. 

- LastByteRead : 호스트 B에서 버퍼로부터 어플리케이션이 읽은 데이터 스트림의 마지막 바이트의 수 

- LastByteRcvd : 호스트 B에서 네트워크로부터 도착하여 수신 버퍼에 저장된 데이터 스트림의 마지막 바이트 수 

TCP는 할당된 버퍼의 오버플로를 허용하지 않으므로, 

LastByteRcvd - LastByteRead <= RcvBuffer (버퍼 사이즈를 초과하지 않는 한도 내에서 받은 만큼만 읽을 수 있다.)

 

rwnd = RcvBuffer - (LastByteRcvd - LastByteRead)

 

시간에 따라 여유 공간이 변하므로, rwnd 는 동적으로 변한다. 

 

* 흐름제어 서비스에서 rwnd 를 사용하는 방식 

호스트 B는 호스트 B가 A에게 전송하는 모든 (응답) 세그먼트의 윈도우 필드에 현재의 rwnd 값을 설정함으로써 연결 버퍼에 얼마만큼의 여유공간이 있는지를 호스트 A에게 알려준다. 초기에는 B가 rwnd = RcvBuffer 로 설정한다. 

반면에, A는 LastByteSent 와 LastByteAcked 를 유지한다. 이 두 변수의 차는 호스트 A가 이 연결에 전송 확인응답이 되지 않은 데이터의 양이다. (보낸 양에서 Ack 되어 돌아온 것을 빼면 보낸 것 중 Ack 되지 않은 것의 수가 나온다) rwnd 의 값보다 작은 확인응답 안 된 (sent - acked) 데이터의 양을 유지함으로써 호스트 A는 B의 수신 버퍼에 오버플로가 발생하지 않는다는 것을 확신한다

 

LastByteSent - LastByteAcked <= rwnd 

 

 

 

4. TCP 의 Connection Management 

TCP는 3 번 세그먼트를 교환함으로써 서버와 클라이언트간의 연결을 형성한다. 처음에는 클라이언트가 서버측으로 SYN 비트를 보내고 연결을 요청한다. 이에 대한 응답으로 서버는 SYNACK 비트를 전송하고, 이를 수신하면 클라이언트는 SYNACK 에 대한 ACK 를 전송한다. 처음 두 개의 세그먼트에는 "페이로드", 즉 애플리케이션 계층 데이터가 없다. 하지만 세 번째 세그먼트에는 페이로드를 포함할 수 있다. 두 호스트 사이에 3개의 세그먼트가 보내지므로, 이 연결 설정 절차는 흔히 '세 방향' 핸드셰이크 (three-way handshake) 라 부른다. 

 

TCP three-way handshake

TCP 연결의 세 단계 

 

1단계 : 여보세요 ~  

클라이언트측 TCP는 서버 TCP에게 특별한 TCP 세그먼트를 전송한다. 이 세그먼트는 어플리케이션 계층 데이터를 포함하지 않는다. 그러나 세그먼트의 헤더에 SYN 비트라고 불리는 하나의 플래그 비트를 가진다. 이 세그먼트를 SYN 세그먼트라 부른다. 추가로 클라이언트는 최초의 순서번호 (client_isn)을 선택한다. 그리고 최초의 TCP SYN 세그먼트의 순서번호 필드에 이 번호를 넣는다. 이 세그먼트는 IP데이터그램 안에서 캡슐화되고 서버로 송신된다. (특정 보안 공격들을 피하기 위해 client_isn 의 선택을 적절히 임의 추출하는 것에 유의해야 한다.)

 

2단계 : 여보세요 ? 

TCP SYN 세그먼트를 포함하는 IP 데이터그램이 서버 호스트에 도착했을 때 서버는 데이터그램으로부터 TCP SYN 세그먼트를 뽑아낸다. 그리고 연결에 TCP버퍼와 변수들을 할당한다. 또한 클라이언트 TCP로 연결 승인 세그먼트를 송신한다. 이 연결 승인 세그먼트 또한 애플리케이션 계층 데이터를 포함하지 않는다. 그러나 세그먼트 헤더 안에 3개의 중요한 정보를 포함한다. 

1. SYN 비트는 1로 설정된다. 

2. TCP 세그먼트 헤더의 확인응답 필드는 client_isn + 1 로 설정된다. 

3. 서버는 자신의 최초 순서번호 server_isn 을 선택하고, TCP 세그먼트 헤더의 순서번호 필드에 이 값을 넣는다. 

이 연결 승인 세그먼트는 사실상 "나는 당신의 최초 순서번호 client_isn 을 갖고 연결을 시작하기 위해서 당신의 SYN 패킷을 수신했다. 나는 이 연결 설정에 동의한다. 나 자신의 최초 순서번호는 server_isn이다."라고 말하는 것이다. 연결 승인 세그먼트는 SYNACK 세그먼트라 불리운다. 

 

3단계 : 네 ~인데요 (전화한 이유는요 ~ )

SYNACK 세그먼트를 수신하면, 클라이언트는 연결에 버퍼와 변수들을 할당한다. 그 다음에 클라이언트 호스트는 서버로 또 다른 세그먼트를 송신한다. 이 마지막 세그먼트가 서버의 연결 승인 세그먼트를 확인한다. 클라이언트는 TCP 세그먼트 헤더의 확인응답 필드에 server_isn + 1 을 넣음으로서 확인을 해준다. 연결이 설정되었기 때문에 SYN 비트는 0으로 설정된다. 세 방향 핸드셰이크의 세 번째 단계는 클라이언트에서 서버로 세그먼트 페이로드에 데이터가 운반될 수 있다. 

 

연결 종료 단계 

TCP 연결에 참여하는 두 개의 프로세스 중 하나는 연결을 끝낼 수 있다. 연결이 끌날 때 호스트의 자원 (버퍼와 변수들)은 회수된다. 예를 들어, 클라이언트가 연결 종료를 결정한다고 하자. 클라이언트 어플리케이션 프로세스는 종료 명령을 내리면 클라이언트 TCP는 서버 프로세스에게 특별한 세그먼트를 보낸다. 이 특별한 세그먼트는 1로 설정된 FIN비트라 불리는 플래그비트를 헤더에 포함하고 있다. 서버가 이 세그먼트를 수신하면, 서버는 클라이언트에게 확인 세그먼트를 보낸다. 그 다음에 FIN 비트가 1로 설정된 자신의 종료 세그먼트를 송신한다. 마지막으로 클라이언트는 서버의 종료 세그먼트에 확인 응답을 한다. 이 시점에서 두 호스트이 모든 자원들은 할당이 해제된다.