연결지향형 트랜스포트: TCP
흐름 제어
TCP 연결의 각 종단에서 호스트들은 연결에 대한 개별 수신 버퍼를 설정합니다. TCP 연결이 순서대로 올바르게 바이트를 수신할 때 TCP는 데이터를 수신 버퍼에 저장합니다. TCP는 송신자가 수신자의 버퍼를 오버플로시키는 것을 방지하기 위해 애플리케이션에게 흐름 제어 서비스(flow-control service)를 제공합니다. 흐름 제어는 속도를 일치시키는 서비스로 수신하는 애플리케이션이 읽는 속도와 송신자가 전송하는 속도를 같게 합니다. 송신자 제어의 이 형태는 혼잡 제어(congestion control)이라 부릅니다.
TCP는 송신자가 수신 윈도(receive window)라는 변수를 유지하여 흐름 제어를 제공합니다. 수신 윈도는 수신 측에서 가용한 버퍼 공간이 얼마나 되는지를 송신자에게 알려주는데 사용합니다. TCP는 전이중(full-duplex)이므로 연결의 각 측의 송신자는 별개의 수신 윈도를 유지합니다.
파일 전송의 환경에서 수신 윈도를 살펴보겠습니다. TCP 연결상에서 호스트 A가 호스트 B에게 큰 파일을 전송한다고 가정할 때 호스트 B는 이 연결에서 수신 버퍼를 할당합니다. 이때 할당된 수신 버퍼의 크기를 RcvBuffer라고 명명합니다. 시간 나는 대로 호스트 B의 애플리케이션 프로세스는 버퍼로부터 데이터를 읽으며 다음과 같은 변수들을 정의합니다.
- LastByteRead: 호스트 B의 애플리케이션 프로세스에 의해 버퍼로부터 읽힌 데이터 스트림의 마지막 바이트의 번호
- LastByteRcvd: 호스트 B에서 네트워크로부터 도착하여 수신 버퍼에 저장된 데이터 스트림의 마지막 바이트의 번호
TCP는 할당된 버퍼의 오버플로를 허용하지 않으므로 다음 수식이 가능합니다.
\[LastByteRcvd - LastByteRead \leq RcvBuffer\]rwnd로 명명된 수신 윈도는 버퍼의 여유 공간으로 설정됩니다.
\[rwnd = RcvBuffer - [LastByteRcvd - LastByteRead]\]시간에 따라 여유 공간은 변하므로 rwnd는 동적입니다.
호스트 B는 호스트 B가 호스트 A에게 전송하는 모든 세그먼트의 윈도 필드에 현재의 rwnd 값을 설정함으로써 연결 버퍼에 얼마만큼의 여유 공간이 있는지를 호스트 A에게 알려줍니다. 초기에는 호스트 B가 \(rwnd = RcvBuffer\) 로 설정합니다.
반면에 호스트 A는 명백한 의미가 있는 두 변수 LastByteSent와 LastByteAcked를 유지합니다. 이 두 변수의 차이는 호스트 A가 이 연결에 전송 확인응답이 안 된 데이터의 양입니다. rwnd의 값보다 작은 확인응답 안 된 데이터의 양을 유지함으로써 호스트 A는 호스트 B의 수신 버퍼에 오버플로가 발생하지 않는다는 것을 확신합니다. 이와 같이 호스트 A는 연결된 동안 다음 내용을 보장합니다.
\[LastByteSent - LastByteAcked \leq rwnd\]이 방식에는 사소한 기술적인 문제가 있습니다. 호스트 A에게 \(rwnd = 0\) 이라고 알린 후에 호스트 B는 호스트 A에게 전송할게 없다고 가정해봅니다.
즉 TCP는 전송할 데이터가 있거나 전송해야 할 확인응답을 가진 경우에만 호스트 A에게 세그먼트를 전송할 것이기 때문에 호스트 A는 호스트 B의 수신 버퍼에 약간의 공간이 있다는 것을 모릅니다. 호스트 A는 차단되고 더는 데이터를 전송할 수 없습니다. 이러한 문제를 해결하기 위해 TCP 명세서는 호스트 A가 호스트 B의 수신 윈도가 0일 때 1 바이트 데이터로 세그먼트를 계속해서 전송하도록 요구합니다. 이 세그먼트들은 수신자에 의해 긍정 확인응답될 것입니다. 결과적으로 버퍼는 비워지고 긍정 확인응답은 0이 아닌 rwnd 값을 포함합니다.
TCP 연결 관리
TCP 연결이 어떻게 설정되고 해제되는지 자세히 알아봅니다. 먼저 클라이언트 애플리케이션 프로세스는 서버에 있는 프로세스와 연결 설정하기를 원한다는 것을 클라이언트 TCP에게 알립니다. 그러면 클라이언트 안의 TCP는 다음과 같은 방법으로 TCP를 이용해 서버와 TCP 연결 설정을 시작합니다.
TCP 세 방향 핸드셰이크
1단계
먼저 클라이언트 측 TCP는 서버 TCP에게 특별한 TCP 세그먼트를 송신합니다. 이 특별한 세그먼트는 애플리케이션 계층 데이터를 포함하지 않습니다. 그러나 세그먼트의 헤더에 SYN 비트라고 불리는 하나의 플래그 비트를 1로 설정합니다. 추가로 클라이언트는 최초의 순서 번호(client_isn)을 임의로 선택하고 최초의 TCP SYN 세그먼트의 순서 번호 필드에 이 번호를 넣습니다.
2단계
TCP SYN 세그먼트를 포함하는 IP 데이터그램이 서버 호스트에 도착하면 서버는 데이터그램으로부터 TCP SYN 세그먼트를 추출합니다. 그리고 연결에 TCP 버퍼와 변수를 할당합니다. 그리고 클라이언트 TCP로 연결 승인 세그먼트를 송신합니다. 또한 이 연결 승인 세그먼트도 애플리케이션 계층 데이터를 포함하지 않습니다. 그러나 세그먼트 헤더 안에 3개의 중요한 정보를 포함합니다.
- SYN 비트는 1로 설정됩니다.
- TCP 세그먼트 확인응답 필드는 client_isn +1로 설정됩니다.
- 자신의 최초의 순서 번호(server_isn)를 선택하고 TCP 세그먼트 헤더의 순서 번호 필드에 이 값을 넣습니다.
이 연결 승인 세그먼튼튼 때때로 SYNACK 세그먼트로 불립니다.
3단계
연결 승인 세그먼트를 수신하면 클라이언트는 연결에 버퍼와 변수를 할당합니다. 그다음에 클라이언 호스트는 서버로 또 다른 세그먼트를 송신합니다. 이 마지막 세그먼트가 서버의 연결 승인 세그먼트를 확인하는 것입니다. (클라이언트는 TCP 세그먼트 헤더의 확인응답 필드 안에 servier_isn +1 값을 넣는 것으로 그 일을 합니다.) 연결 설정되었기 때문에 SYN 비트는 0으로 설정됩니다. 세 방향 핸드셰이크의 세 번째 단계는 클라이언트에서 서버로의 데이터를 세그먼트 페이로드에서 운반할 수 있습니다.
TCP 연결 종료
TCP 연결에 참여하는 두 프로세스 중 하나가 연결을 끝낼 수 있습니다. 연결이 끝날 때 호스트의 자원(버퍼와 변수)은 회수됩니다.
클라이언트 애플리케이션 프로세스는 종료 명령을 내리고 이것은 클라이언트 TCP가 서버 프로세스에게 특별한 TCP 세그먼트를 보내도록 합니다. 이 특별한 세그먼트는 1로 설정된 FIN 비트라 불리는 플래그 비트를 세그먼트 헤더에 포함하고있습니다. 서버가 이 세그먼트를 수신하면 서버는 클라이언트에게 확인응답 세그먼트를 보냅니다. 그 다음에 FIN 비트가 1로 설정된 자신의 종료 세그먼트를 송신합니다. 마지막으로 클라이언트는 서버의 종료 세그먼트에 확인응답을 합니다. 이 시점에서 두 호스트의 모든 자원은 할당이 해제됩니다.
해당 글은 컴퓨터 네트워킹 하향식 접근(James F. Kurose, Keith W. Ross)을 읽고 공부한 내용을 정리한 글입니다.
오탈자 및 오류 내용을 댓글 또는 메일로 알려주시면, 검토 후 조치하겠습니다.