전송 계층
이번 포스트는 ‘혼자 공부하는 네트워크‘라는 책을 통해 네트워크 계층의 한계를 극복하고 응용 계층과의 연결점을 제공하는 전송 계층에 대해 알아보는 목적을 갖고 있다. 전송 계층의 역할을 이해하기 위해 IP의 한계와 포트의 개념이 뭔지 먼저 알아볼 것이다.
우선, 이런 좋은 책을 만들어주신 강민철 님에게 감사하다는 것을 전하고 시작하도록 하겠다. 강민철 님은 어려운 내용들을 쉽게 설명해내는 능력이 매우 띄어난 저자로, 같은 설명인 것 같은데도 머리 속에 더 잘 남게해주신다. 개발 관련해서 처음 배우는 학문이나 여러 번 공부했는데 머리 속에 박혀있지 못한 것이 있다면 강민철 님의 책이나 강의를 추천한다.
그럼 시작해보자.
1. IP의 한계와 포트
네트워크 계층의 IP는 신뢰할 수 없는 통신과 비연결형 통신을 수행한다는 한계가 있다. 네트워크 계층과 응용 계층 사이의 전송 계층은 신뢰할 수 있는 통신과 연결형 통신을 가능하게 해서 IP의 한계를 극복하고, 포트 번호를 통해 응용 계층의 애플리케이션 프로세스들을 식별하는 역할을 수행한다.
1.1 신뢰할 수 없는 통신과 비연결형 통신
IP를 통한 패킷의 전달은 신뢰성이 없는 통신이자 연결을 수립하는 과정이 없는 통신이다.
신뢰할 수 없는 통신을 수행한다는 것은 IP 프로토콜이 “패킷이 수신지까지 제대로 전송되었다” 는 것을 보장하지 않는다는 것을 의미한다. 통신 과정에서 패킷의 데이터가 손상되거나 중복된 패킷이 전송되든 말든 확인하지 않고, 재전송도 이루어지지 않으며, 순서대로 패킷이 도착한다는 보장도 하지 않는다는 의미이다. 이런 전송 특성을 최선형 전달$^{best\ effort\ delivery}$ 라고도 부른다.
비연결형 통신은 이름 그대로 송수신하는 호스트 간에 사전 연결 수립 작업을 거치지 않는 통신을 의미한다.
IP가 이런 통신을 하는 주요한 이유는 성능이다. 모든 패킷이 제대로 전송되었는지 일일이 확인하고, 호스트 간에 연결을 수립하는 작업은 일반적으로 패킷의 ‘빠른’ 송수신과는 거리가 멀기 때문이다.
인터넷 상에서 돌아다니는 패킷의 종류와 개수는 매우 다양하다. 금융 서비스처럼 반드시 신뢰성 있는 전송을 보장해야 하는 경우도 있고, 동영상 스트리밍 서비스나 실시간 영상 통화처럼 한 개 내지는 두 개의 패킷 손실은 감수하더라도 빠른 전송을 우선시해야 하는 경우도 있다. 이처럼 신뢰성 있는 전송이 모든 경우에 필요한 것은 아니다.
1.2 IP의 한계를 보완하는 전송 계층
IP의 한계를 전송 계층이 어떻게 보완하는지 알아보자.
첫째, 전송 계층은 연결형 통신을 가능하게 한다.
연결형 통신을 지원하는 대표적인 전송 프로토콜인 TCP는 두 호스트가 정보를 주고받기 전에 가상의 회선을 설정하듯 연결을 사전에 수립한다. 그렇게 연결을 수립하고 송수신을 하는 동안에 연결을 유지하고, 송수신이 끝나면 연결을 종료할 수 있다.
둘째, 전송 계층은 신뢰성 있는 통신을 가능하게 한다.
TCP는 패킷이 수신지까지 올바른 순서대로 확실히 전달되는 것을 보장하기 위해 재전송을 통한 오류 제어, 흐름 제어, 혼잡 제어 등 다양한 기능들을 제공한다.
앞서 1.1 에서 알아보았듯 연결형 통신과 신뢰성 있는 통신을 하지 않는 것이 더 적합한 경우도 있다. 그래서 UDP 라는 프로토콜이 있는데, 이는 신뢰할 수 없는 통신과 비연결형 통신을 가능하게 하는 프로토콜로 TCP에 비해 빠른 전송이 가능하다.
1.3 응용 계층과의 연결 다리, 포트
전송 계층이 네트워크 계층과 응용 계층 사이에서 연결 다리 역할을 할 수 있게 해주는 포트라는 것에 대해 알아보자.
1.3.1 포트의 정의
이런 상황을 가정해보자. 네트워크 외부에서 전송받으려는 사진 파일을 구성하는 패킷들이 라우팅되서 우리의 컴퓨터에 도착했다. 그리고 우리는 컴퓨터로 웹 브라우저, 게임, 메신저 프로그램을 실행하고 있다. 이런 상황에서 전송받은 사진 파일 패킷들이 어떤 애플리케이션 프로세스까지 전달하기 위해 필요한 식별 정보가 바로 포트이다.
1.3.2 포트의 분류
전송 계층에서는 패킷에 포함되어있는 수신지 포트 정보와 송신지 포트 정보를 통해 송수신지 호스트의 애플리케이션을 식별한다. 전송 계층의 핵심 프로토콜인 TCP와 UDP는 이 두 필드를 포함한다. 포트 번호는 16비트로 표현 가능하고, 사용 가능한 포트의 수는 2$^{16}$(65536)개이다. 할당 가능한 포트 번호는 0번부터 65535번까지, 총 65536개이다.
0 ~ 1023 번 포트 번호는 well known port (or 시스템 포트)다.
잘 알려진 포트 번호 | 설명 |
---|---|
20, 21 | FTP |
22 | SSH |
23 | TELNET |
53 | DNS |
67, 68 | DHCP |
80 | HTTP |
443 | HTTPS |
1024 ~ 49151 번 포트 번호는 registerd port 번호다.
등록된 포트 번호 | 설명 |
---|---|
1194 | OpenVPN |
1433 | Microsoft SQL Server 데이터베이스 |
3306 | MySQL 데이터베이스 |
6379 | Redis |
8080 | HTTP 대체 |
49152 ~ 65535 번 포트 번호는 동적 포트$^{dynamic\ port}$, 사설 포트$^{private\ port}$, 임시 포트$^{ephemeral\ port}$라고 부른다. 이 구간의 포트 번호에는 인터넷 할당 번호 관리 기관에 의해 할당된 애플리케이션 프로토콜이 없고, 특별히 관리되지 않기 때문에 자유롭게 사용할 수 있다.
서버로서 동작하는 프로그램은 일반적으로 잘 알려진 포트와 등록된 포트로 동작하는 경우가 많다. 즉, 서버로서 동작하는 프로그램의 포트 번호는 사전에 암묵적으로 정해진 경우가 많다.
반면에 클라이언트로 동작하는 프로그램은 동적 포트 번호에서 임의의 번호가 할당되는 경우가 많다. 대표적인 예시로 웹 브라우저가 있다.
이런 포트 번호는 실행 중인 특정 애플리케이션의 식별자이기 때문에 IP 주소와 포트 번호를 모두 알면 특정 호스트에서 실행 중인 특정 애플리케이션 프로세스를 식별할 수 있다. 그래서 포트 번호는 IP 주소:포트 번호 형식으로 표기되는 경우가 많다.
1.4 포트 기반 NAT
NAT은 IP 주소를 변환하는 기술로, 주로 네트워크 내부에서 사용되는 IP 주소와 외부에서 사용되는 공인 IP 주소를 변환하는데 사용된다. 이런 변환 작업을 위해 NAT 변환 테이블이 주로 사용된다.
1.4.1 NAT 변환 테이블
NAT 변환 테이블에는 아래 표처럼 변환의 대상이 되는 IP 주소 쌍이 명시되어 있다.
네트워크 외부(공인) | 네트워크 내부(사설) |
---|---|
1.2.3.4 | 192.168.0.5 |
1.2.3.5 | 192.168.0.6 |
… | …. |
이 NAT 테이블을 토대로 예를 들어보겠다.
- 네트워크 내부에 192.168.0.5라는 사설 IP 주소를 가진 호스트가 있고, 수신지 주소가 10.11.12.13인 네트워크 외부의 호스트에게 패킷을 전송한다고 가정해보자.
- 패킷이 NAT 기능을 갖춘 라우터를 거쳐 네트워크 외부로 나가게 되면 패킷의 송신지 주소는 네트워크 외부에서 사용되는 IP 주소인 1.2.3.4가 된다.
- 반대의 경우, 수신지 주소가 1.2.3.4인 패킷이 네트워크 외부에서 내부로 전송되는 상황에서, 이 패킷의 수신지 주소는 NAT 라우터를 거쳐 192.168.0.5가 된다.
이렇게 NAT 테이블을 통해 외부와 내부 IP 주소를 나눠 적은 수의 공인 IP 주소를 변환해 많은 사설 IP 주소들을 사용할 수 있게 한다. 이러한 걸 가능하기 위해 포트가 활용된다.
1.4.2 NAPT
포트 기반의 NAT를 NAPT$^{Network\ Address\ Port\ Translation}$(or APT$^{Address\ Port\ Translation}$)이라 한다. 이는 포트를 활용해 하나의 공인 IP 주소를 여러 사설 IP 주소가 공유할 수 있도록 하는 NAT의 일종이다. NAPT는 다음의 표처럼 NAT 테이블에 변환할 IP 주소 쌍과 함께 포트 번호도 기록해 변환한다.
네트워크 외부(공인) | 네트워크 내부(사설) |
---|---|
1.2.3.4:6200 | 192.168.0.5:1025 |
1.2.3.5:6201 | 192.168.0.6:1026 |
… | …. |
네트워크 외부에서 사용할 IP 주소가 같더라도 포트 번호가 다르면 네트워크 내부의 호스트를 특정할 수 있기 때문에, 다수의 사설 IP 주소를 그보다 적은 수의 공인 IP 주소를 변환할 수 있게 된다.
2. TCP와 UDP
네트워크 계층에서 가장 중요한 프로토콜이 IP라면, 전송 계층에서 가장 중요한 프로토콜은 TCP와 UDP이다. TCP$^{Transmission\ Control\ Protocol}$는 신뢰할 수 있는 통신을 위한 연결형 프로토콜이고, UDP$^{User\ Datagram\ Protocol}$는 TCP보다 신뢰성은 떨어지지만 비교적 빠른 통신이 가능한 비연결형 프로토콜이다.
2.1 TCP 통신 단계와 세그먼트 구조
TCP 통신을 크게 세 단계로 나누면 이러하다.
- 연결 수립
- 데이터 송수신
- 재전송을 통한 오류 제어, 흐름 제어, 혼잡 제어
- 연결 종료
TCP는 통신(데이터 송수신)하기 전에 연결을 수립하고 통신이 끝나면 연결을 종료한다. 그리고 데이터 송수신 과정에서 재전송을 통한 오류 제어, 흐름 제어, 혼잡 제어 등의 기능을 제공한다. 이번 절에서 연결형 프로토콜 TCP의 특징을 이해하기 위해 연결 수립과 연결 종료를 먼저 살펴본다.
TCP의 연결 수립과 종료를 이해하기 위해서 MSS라는 단위와 TCP의 세그먼트 구조를 이해해야 한다. MSS는 Maximum Segment Size의 약자로, TCP로 전송할 수 있는 최대 페이로드 크기를 의미한다. 이 크기를 고려할 때 TCP 헤더 크기는 제외한다. 이는 헤더의 크기까지 포함하는 단위인 MTU와는 대조적이다.
이제 세그먼트 구조를 살펴보자.
- 송신지 포트$^{source\ port}$와 수신지 포트$^{destination\ port}$: 송신지 또는 수신지 애플리케이션을 식벼하는 포트 번호가 명시되는 필드
- 순서 번호$^{sequence\ number}$: 순서 번호가 명시되는 필드다. 순서 번호란 송수신되는 세그먼트의 올바른 순서를 보장하기 위해 세그먼트 데이터의 첫 바이트에 부여되는 번호다.
- 확인 응답 번호$^{acknowleadgment\ number}$: 상대 호스트가 보낸 세그먼트에 대한 응답으로, 다음으로 수신하기를 기대하는 순서 번호가 명시된다.
- 제어 비트$^{control\ bits}$: 플래그 비트라고도 불리며, 현재 세그먼트의 부가 정보를 나타낸다.
- 윈도우$^{window}$: 수신 윈도우의 크기가 명시된다. 수신 윈도우란 한 번에 수신하고자 하는 데이터의 양을 나타낸다.
2.1.1 제어비트
순서 번호 필드와 확인 응답 번호 필드를 알아보기 전에 먼저 제어 비트 필드를 이해할 필요가 있다. 제어 비트 필드는 기본적으로 8비트로 구성된다. 그 중 TCP의 기본 동작을 논할 때 가장 자주 언급되는 세 개의 제어 비트는 다음과 같다.
- ACK: 세그먼트의 승인을 나타내기 위한 비트
- SYN: 연결을 수립하기 위한 비트
- FIN: 연결을 종료하기 위한 비트
각 제어 비트가 1로 설정된 세그먼트를 ACK, SYN, FIN 뒤에 세그먼트를 붙여 칭하는 경우가 많다. (예: ACK 세그먼트)
2.1.2 순서 번호와 확인 응답 번호
순서 번호 필드와 확인 응답 번호 필드는 신뢰성을 보장하기 위해 사용되는 중요한 필드로, 한 쌍으로 묶어 기억하는 것이 좋다.
순서 번호 필드에 명시되는 순서 번호는 MSS 단위로 전송될 수 있다. 편의상 MSS를 500바이트라고 가정하면, 1900바이트 데이터 덩어리를 MSS 단위로 쪼개면 네 개의 세그먼트로 쪼갤 수 있을 것이다.
통신을 위해 처음에 연결을 수립한 경우, 그러니까 제어 비트에서 연결을 수립하기 위한 SYN 플래그가 1로 설정된 세그먼트는 순서 번호가 무작위 값이 된다. 이를 초기 순서 번호$^{ISN}$라고 한다.
연결 수립 이후 데이터를 송신하는 동안 순서 번호는 송신한 바이트를 더해 가는 방식으로 누적값을 가진다.
확인 응답 번호 필드에 명시되는 확인 응답 번호는 순서 번호에 대한 응답이다. 이는 수신자가 다음으로 받기를 기대하는 순서 번호이기도 하다. 그래서 일반적으로 ‘수신한 순서 번호 + 1’ 로 설정된다.
확인 응답 번호 값을 보내기 위해선 제어 비트에서 승인을 나타내는 비트인 ACK 플래그를 1로 설정해야 한다.
2.2 TCP 연결 수립과 종료
2.2.1 연결 수립: 쓰리 웨이 핸드쉐이크
TCP의 연결 수립은 Three-way Handshake를 통해 이루어 진다. 이는 다음의 세 단계를 의미한다.
송수신 방향 | 세그먼트 | 세그먼트에 포함된 주요 정보 | 비유 |
---|---|---|---|
A → B | SYN 세그먼트 | - 호스트 A의 초기 순서 번호 - 1로 설정된 SYN 비트 | ‘연결 시작한다.’ |
B → A | SYN 세그먼트 + ACK 세그먼트 | - 호스트 B의 초기 순서 번호 - 호스트 A가 전송한 세그먼트에 대한 확인 응답 번호 - 1로 설정된 SYN 비트 - 1로 설정된 ACK 비트 | ‘확인, 연결 시작하십쇼’ |
A → B | ACK 세그먼트 | - 호스트 A의 다음 순서 번호 - 호스트 B가 전송한 세그먼트에 대한 확인 응답 번호 - 1로 설정된 ACK 비트 | ‘오케이’ |
이때 처음 연결을 시작하는 호스트의 연결 수립 과정을 액티브 오픈이라 한다. 연결을 처음 요청하는 쪽의 동작을 액티브 오픈이라 생각해도 좋다. 이는 주로 서버-클라이언트 관계에서 클라이언트에 의해 수행된다. 반대로 연결 요청을 받고 나서 요청에 따라 연결을 수립해 주는 호스트도 있다. 이런 호스트의 연결 수립 과정을 패시브 오픈$^{passive\ open}$이라 한다. 주로 서버에 의해 수행된다.
2.2.2 연결 종료
연결 수립 후에 데이터 송수신이 끝나면, 송수신 호스트가 한 번씩 FIN과 ACK를 주고받으면서 연결을 종료해야 한다.
송수신 방향 | 세그먼트 | 세그먼트에 포함된 주요 정보 | 비유 |
---|---|---|---|
A → B | FIN 세그먼트 | - 1로 설정된 FIN 비트 | ‘연결을 끊겠다.’ |
B → A | ACK 세그먼트 | - 호스트 A가 전송한 세그먼트에 대한 확인 응답 번호 - 1로 설정된 ACK 비트 | ‘확인, 연결 끊으세요’ |
B → A | FIN 세그먼트 | - 1로 설정된 FIN 비트 | ‘오케이, 연결 끊어요’ |
A → B | ACK 세그먼트 | - 호스트 B가 전송한 세그먼트에 대한 확인 응답 번호 - 1로 설정된 ACK 비트 | ‘네, 확인’ |
이 또한 먼저 연결을 종료하려는 호스트에 의해 수행되는 액티브 클로스와, 연결 종료 요청을 받아들이는 호스트에 의해 수행되는 패시브 클로즈가 있다.
2.2 TCP 상태
TCP는 연결형 통신과 신뢰할 수 있는 통신을 유지하기 위해 다양한 상태를 유지한다. 이런 TCP는 상태를 유지하고 활용한다는 점에서 스테이트풀 프로토콜이라고도 불린다.
아래의 표가 TCP의 상태이다.
상태 분류 | 주요 상태 |
---|---|
1 | CLOSED, LISTEN |
2 | SYN-SENT, SYN-RECEIVED, ESTABLISHED |
3 | FIN-WAIT-1, CLOSE-WAIT, FIN-WAIT-2, LAST-ACK,TIME-WAIT, CLOSING |
2.2.1 연결이 수립되지 않은 상태
- CLOSED: 아무 연결이 없는 상태
- LISTEN: 일종의 연결 대기 상태. 일반적으로 서버로서 동작하는 패시브 오픈 호스트는 LISTEN 상태를 유지하고 있다.
2.2.2 연결 수립 상태
이 과정에서는 주로 SYN-SENT, SYN-RECEIVED, ESTABLISHED 상태를 볼 수 있다.
- SYN-SENT: 액티브 오픈 호스트가 SYN 세그먼트를 보낸 뒤 SYN + ACK 세그먼트를 기다리는 상태다.
- SYN-RECEIVED: 패시브 오픈 호스트가 SYN + ACK 세그먼트를 보낸 뒤 ACK 세그먼트를 기다리는 상태다.
- ESTABLISHED: 연결이 확립되었음을 나타내는 상태다. 쓰리 웨이 핸드셰이크 과정에서 두 호스트가 마지막 ACK 세그먼트를 주고 받으면 이 상태가 된다.
2.2.3 연결 종료 상태
FIN-WAIT-1, CLOSE-WAIT, FIN-WAIT-2, LAST-ACK, TIME-WAIT, CLOSED 상태를 볼 수 있다.
- FIN-WAIT-1: 일반적인 TCP 연결 종료 과정에서 첫 단계가 된다. FIN 세그먼트로 연결 종료 요청을 보낸 액티브 클로즈 호스트는 이 상태가 된다.
- CLOSED-WAIT: 종료 요청인 FIN 세그먼트를 받은 패시브 클로즈 호스트가 그에 대한 응답으로 ACK 세그먼트를 보낸 후 대기하는 상태다.
- FIN-WAIT-2: FIN-WAIT-1 상태에서 ACK 세그먼트를 받으면 FIN-WAIT-2 상태가 된다. 상대 호스트의 FIN 세그먼트를 기다리는 상태다.
- LAST-ACK: CLOSE-WAIT 상태에서 FIN 세그먼트를 전송한 뒤 이에 대한 ACK 세그먼트를 기다리는 상태다.
- TIME-WAIT: 액티브 클로즈 호스트가 FIN 세그먼트를 수신한 뒤, 이에 대한 ACK 세그먼트를 전송한 뒤 접어드는 상태다. 패시브 클로즈 호스트가 마지막 ACK 세그먼트를 수신하면 바로 CLOSED 상태로 전이하는 반면, TIME-WAIT 상태에 접어든 액티브 클로즈 호스트는 일정 시간을 기다린 뒤 CLOSED 상태로 전이한다.
참고로 CLOSING 상태는 보통 동시에 연결을 종료하려 할 때 전이되는 상태다. 서로 FIN 세그먼트를 주고받고 ACK 세그먼트를 보내고 아직 자신의 FIN 세그먼트에 대한 ACK 세그먼트를 받지 못했을 때 접어드는 상태라고 보면 된다.
2.3 UDP 데이터그램 구조
UDP는 스테이트리스 프로토콜의 일종이다. TCP에 비해 제공하는 기능이 적은 만큼 필드도 단순하다.
- 송신지 포트와 수신지 포트: 송수신지의 포트 번호가 담긴다.
- 길이: 헤더를 포함한 UDP 데이터그램의 바이트가 담긴다.
- 체크섬: 데이터그램 전송 과정 중 오류 발생을 검사하기 위한 필드다. 수신지 는 이 필드의 값을 토대로 데이터그램의 정보가 훼손되었는지를 판단하고, 문제가 있다고 판단한 데이터그램은 폐기한다. 참고로 데이터그램이 훼손되었는지를 나타내는 정보일 뿐 ‘수신지까지 잘 도달했는지’를 나타내는 신뢰성/비신뢰성과는 관련이 없다.
3. TCP의 오류·흐름·혼잡 제어
TCP의 신뢰성을 보장하기 위한 기능들을 살펴보자.
3.1 오류 제어: 재전송 기법
TCP는 잘못된 세그먼트를 재전송하는 방법으로 오류를 제어한다.
3.1.1 오류 검출과 재전송
TCP 세그먼트의 체크섬 필드는 훼손 여부만 나타내기 때문에 세그먼트 전송 과정에서의 문제를 파악할 수 있는 정보라고 볼 수 없다. TCP는 신뢰성을 제대로 보장하기 위해 어떤 상황에서 오류를 검출하고 세그먼트를 재전송할까?
첫째, 중복된 ACK 세그먼트를 수신했을 때
TCP는 수신한 세그먼트의 순서 번호 중 일부가 누락되었을 때 중복된 ACK 세그먼트를 전송하게 된다.
둘째, 타임아웃이 발생했을 때
TCP는 타임아웃이 발생하면 문제가 생겼음을 인지한다.
TCP 세그먼트를 송신하는 호스트는 모두 재전송 타이머라는 값을 유지한다. 호스트가 세그먼트를 전송할 때마다 재전송 타이머를 시작하게 되는데, 이 타이머의 카운트다운이 끝난 상황을 타임아웃이라고 한다.
타임아웃이 발생할 때까지 ACK 세그먼를 받지 못하면 세그먼트가 상대 호스트에게 정상적으로 도착하지 않았다고 간주해 세그먼트를 재전송한다.
3.1.2 ARQ: 재전송 기법
ARQ$^{Automatic\ Repeat\ Request}$(자동 재전송 요구)는 TCP의 재전송 기법이다. 이것의 종류는 다양한데, 가장 대표적인 세 가지 방식인 Stop-and-Wait ARQ와 Go-Back-NARQ 그리고 Selective Repeat ARQ에 대해 알아보자
3.1.3 Stop-and-Wait ARQ
이건 가장 단순한 방식인 ARQ 방식으로, 제대로 전달했음을 확인하기 전까지 새로운 메세지를 보내지 않는 방식이다. 이는 전송되었음을 확인해야만 다음 전송을 시작하기 때문에 네트워크 이용 효율이 낮아질 위험이 있다.
3.1.4 Go-Back-N ARQ
Stop-and-Wait ARQ의 문제가 해결되려면 각 세그먼트에 대한 ACK 세그먼트가 도착하기 전이라도 여러 세그먼트를 보낼 수 있어야 한다. Go-Back-N ARQ와 Selective Repeat ARQ는 모두 이런 방식으로 동작한다. 이렇게 연속해서 메세지를 전송할 수 있는 기술을 파이프라이닝이라고 한다. 오늘날 TCP는 이런 파이프라이닝이 사용되는 Go-Back-N ARQ와 Selective Repeat ARQ를 기반으로 동작한다.
Go-Back-N ARQ는 파이프라이닝 방식으로 여러 세그먼트를 전송하고, 도중에 잘못 전송된 세그먼트가 있으면 해당 세그먼트부터 전부 다시 전송하는 방식이다. 그래서 Go-Back-N ARQ에서 순서 번호 n번에 대한 ACK 세그먼트는 ‘n번만의’ 확인 응답이 아니라 ‘n번까지의’ 확인 응답이라고 볼 수 있다. 이런 점에서 Go-Back-N ARQ의 ACK 세그먼트를 누적 확인 응답$^{CACK: Cumulative\ Acknowleadgment}$ 라고 한다.
3.1.5 Selective Repeat ARQ
Go-Back-N ARQ와 달리 Selective Repeat ARQ는 수신 호스트 측에서 제대로 전송 받은 각각의 패킷들에 대해 ACK 세그먼트를 보내는 방식이다. 그래서 Selective Repeat ARQ의 ACK 세그먼트는 개별 확인 응답$^{Selective\ Acknowleadgment}$ 라고 할 수 있다.
이 방식에서 송신 호스트는 올바르게 수신받지 못한 ACK 세그먼트가 있는지 검사하고, 있다면 해당 세그먼트를 재전송합니다.
오늘날 대부분의 호스트는 TCP 통신에서 Selective Repeat ARQ를 지원한다. 두 호스트가 연결을 수립할 때 서로의 Selective Repeat ARQ 지원 여부를 확인하게 되는데, 만약 Selective Repeat ARQ 를 지원하지 않으면 Go-Back-N ARQ 방식으로 동작하게 된다.
3.2 흐름 제어: 슬라이딩 윈도우
흐름 제어는 TCP의 두 번째 핵심 기능이다. 파이프라이닝 기반의 ARQ가 정상적으로 동작하려면 반드시 흐름 제어를 고려해야 한다. 호스트가 한 번에 받아서 처리할 수 있는 세그먼트의 양에는 한계가 있기 때문이다.
이 상황은 수신 호스트의 ‘수신 버퍼’와 ‘버퍼 오버플로’라는 개념을 통해 조금 더 명확히 이해할 수 있다. 수신 버퍼는 수신된 세그먼트가 애플리케이션 프로세스에 의해 읽히기 전 임시로 저장되는 공간이다. 송신 호스트가 흐름 제어를 고려하지 않고 수신 버퍼 크기보다 많은 데이터를 전송하면 그만큼 일부 세그먼트가 처리되지 못할 수 있다. 이런 상황을 버퍼 오버플로라고 한다.
TCP의 흐름 제어는 이런 문제를 방지하기 위해 송신 호스트가 수신 호스트의 처리 속도를 고려하며 송수신 속도를 균일하게 유지하는 것을 의미한다.
오늘날 TCP에서는 흐름 제어를 하기 위해 슬라이딩 윈도우를 사용한다. 여기서 윈도우란 송신 호스트가 파이프라이닝할 수 있는 최대의 양을 의미한다. 윈도우 크기만큼의 데이터는 확인 응답없이도 한 번에 전송 가능하다는 의미다.
이런 슬라이딩 윈도우는 송수신 호스트 모두에게서 고려된다. 송신 측에서는 수신 호스트가 알려주는 수신 측 윈도우를 토대로 윈도우를 설정한다.
3.3 혼잡 제어
TCP의 혼잡 제어 기능 명칭에서의 혼잡은 많은 트래픽으로 인해 패킷의 처리 속도가 늦어지거나 유실될 우려가 있는 네트워크 상황을 의미한다.
같은 네트워크에 속한 여러 호스트가 한 대의 라우터에 연결되어 있다고 가정하자. 모든 호스트가 라우터에게 전송 가능한 최대의 양으로 세그먼트를 전송하면 라우터에 과부하가 생겨 모든 정보를 한 번에 처리하지 못할 수 있다. 그러면 호스트들은 오류를 검출해 재전송을 하게 되고, 그럴수록 라우터의 혼잡 현상은 악화된다.
TCP의 혼잡 제어는 이런 혼잡을 제어하기 위한 기능이다. 흐름 제어의 주체가 수신 호스트라면 혼잡 제어의 주체는 송신 호스트다. 혼잡 제어를 수행하는 송신 호스트는 네트워크 혼잡도를 판단하고 혼잡한 정도에 맞춰 유동적으로 전송량을 조절한다.
이런 일을 수행하기 위해 혼잡 윈도우가 사용된다. 이는 혼잡 없이 전송할 수 있을 법한 데이터양을 의미한다. 이 크기는 송신 호스트가 어느 정도의 세그먼트를 전송해야 혼잡을 방지할 수 있는지를 직접 계산하여 알아내야 한다. 이는 혼잡 제어 알고리즘을 통해 결정할 수 있다.
그 중 가장 기본적인 알고리즘인 AIMD는 Additive Increase/Multiplicative Decrease의 약자로, 해석하면 ‘합으로 증가, 곱으로 감소’를 의미한다. 혼잡이 감지되지 않으면 RTT$^{Round\ Trip\ Time}$ 마다 1씩 선형적으로 증가시키고, 혼잡이 감지되면 혼잡 윈도우를 절반으로 떨어뜨리는 동작을 반복하는 알고리즘이다.
AIMD 알고리즘은 혼잡 제어에 사용할 수 있는 가장 기본적인 아이디어라서, 이 알고리즘을 조금 더 정교하게 만들어야 혼잡 제어가 가능해진다. 그렇게 만들어진 알고리즘들을 세 가지 살펴보자.
3.3.1 느린 시작 알고리즘
느린 시작 알고리즘은 혼잡 윈도우를 1부터 시작해서 문제없이 수신된 ACK 세그먼트 하나당 1씩 증가시키는 방식이다. 그래서 RTT마다 2배씩 지수적으로 증가하게 된다. AIMD 방식은 처음 연결이 수립된 뒤에 혼잡 윈도우 크기가 증가되는 속도가 느리다. 즉, 선형적으로 혼잡 윈도우를 증가시키기 때문에 초기 전송 속도가 확보되지 않는다. 하지만 느린 시작 방식은 혼잡 윈도우의 지수적인 증가를 활용해 초기 전송 속도를 어느 정도 빠르게 확보할 수 있다.
혼잡 윈도우가 1일 때 2가 되고, 그 다음엔 4개가 되므로 지수적으로 증가하게 된다는 것이다.
하지만 계속 지수적으로 증가시키다보면 혼잡 상황을 마주하게 될 확률이 높아지기 때문에 느린 시 임계치라는 값이 정해져 있다. 혼잡 윈도우 값이 계속 증가하다가 느린 시작 임계치 이상이 되거나, 타임아웃이 발생하거나 세 번의 중복된 ACK 세그먼트가 발생해 혼잡이 감지되면 다음 세 가지 방법 중 하나를 선택하게 된다.
상황 분류 | 방법 |
---|---|
타임아웃 발생 | 혼잡 윈도우 값을 1로, 느린 시작 임계치를 혼잡이 감지되었을 시점의 혼잡 윈도우 값의 절반으로 초기화한 뒤 느린 시작 재개 |
혼잡 윈도우 >= 느린 시작 임계치 | 느린 시작 종료, 혼잡 윈도우를 절반으로 초기화한 뒤 혼잡 회피 수행 |
세 번의 중복 ACK 발생 | (빠른 재전송 후) 빠른 회복 수행 |
3.3.2 혼잡 회피 알고리즘
혼잡 회피$^{congestion\ avoidance}$ 알고리즘은 RTT마다 혼잡 윈도우를 1MSS$^{Maximum\ Segment\ Size}$ 씩 증가시키는 알고리즘이다. 느린 시작 임계치를 넘어선 시점부터는 혼잡이 발생할 우려가 있으니 조심해서 혼잡 윈도우를 증가시키는 방식이라고 보면 된다.
이때, 혼잡 회피 도중 타임아웃이 발생하면 혼잡 윈도우 값은 1로, 느린 시작 임계치는 혼잡이 감지된 시점의 혼잡 윈도우 값의 절반으로 초기화한 뒤 다시 느린 시작을 수행한다. 그리고 혼잡 회피 도중 세 번의 중복 ACK 세그먼트가 발생되었을 때는 혼잡 윈도우 값과 느린 시작 임계치를 대략 절반으로 떨어뜨린 뒤 빠른 회복 알고리즘을 수행한다. 물론 이때 타임아웃이 발생한 세그먼트나 세 번의 중복 ACK 세그먼트가 발생한 세그먼트는 재전송한다.
3.3.3 빠른 회복 알고리즘
세 번의 중복된 ACK 세그먼트를 수신하면 빠른 재전송과 더불어 빠른 회복 알고리즘이 수행된다. 빠른 회복 알고리즘은 느린 시작은 건너뛰고 혼잡 회피를 수행함으로써, 빠르게 전송률을 회복하기 위한 목적을 띈다. 단, 빠른 회복 도중이더라도 타임아웃이 발생하면 혼잡 윈도우 크기는 1로, 느린 시작 임계치는 혼잡이 감지된 시점의 절반으로 떨어뜨린 후 다시 느린 시작을 수행한다.