TCP 제대로 이해하기
❒ Description
전송계층에서 중요하게 알아야 할 TCP의 연결과정에 대해서 깊이있게 공부해보자.
❒ TCP란?
TCP는 TCP/IP 모델의 전송계층의 프로토콜 중 하나이다. 기본적으로 다음의 특징을 가지고 있다.
- 패킷 사이의 순서 보장한다.
- 연결 지향형 프로토콜을 사용하여 신뢰성을 구축한다.
- 가상회선 패킷 교환 방식을 사용한다.
💬 가상회선 패킷 교환 방식이란?
각 패킷에는 가상회선 식별자가 포함되며 모든 패킷을 전송하면 가상회선이 해제되고
패킷들은 전송된 순서대로 도착하는 방식을 말한다.
TCP는 "연결 지향형 프로토콜을 사용하여 신뢰성을 구축한다." 라고 했다.
연결 지향형 프로토콜은 송신측 & 수신측 컴퓨터가 데이터를 전송하기 전에 먼저 데이터를
송수신할 수 있는 신뢰성 있는 연결 통로를 만들고 데이터를 전송하는 프로토콜이다.
❒ TCP Header 형식
Source Port | Destination Port | ||||
Seqeunce number | |||||
Acknowledgment number | |||||
Data offset | Reserved | Code bit | Window size | ||
Check sum | Urgent Pointer | ||||
Options |
Code bit는 연결의 제어 정보가 기록되는 코드 비트는 TCP 헤더 107 ~ 112 번째 비트이다.
이 코드 비트는 비트별로 역할이 정해져 있으며, 초깃값이 0이고 활성화 되면 1이 된다.
Code Bit에는 다음과 같은 것들이 있다.
- URG - 긴급하게 처리할 데이터가 들어있음.
- ACK - 응답확인 번호 사용
- PSH - 데이터를 최대한 빠르게 응용프로그램에게 전송 (버퍼 없이 즉시 전송)
- RST - 연결 재설정(비정상적으로 끊길 때)
- SYN - 연결을 초기화하려고 순서 번호 동기화
- FIN - 데이터 송신 종료
추가적으로 NS, CWR, ECE는 네트워크 장애의 혼잡한 상태를 제어 할 때 사용되는 Code Bit다.
❒ 연결 - Connection, Session, Virtual Circuit
연결이라는 단어가 중요하다. 연결이란 논리적인 개념으로 프로토콜이 관리하고 있는 정보를 교환하는 것이다.
TCP 연결에서는 Sequence Number와 MSS(Maximum Segment Size)를 교환한다.
보통 원서에서 연결은 Connection, Session, Virtual Circuit 이렇게 세 개의 단어로 표현된다고 한다.
💬 IT에서 Virtual(가상화)의 의미
Virtual은 가상화라는 의미를 갖는데, JVM에서도 사용되는 단어이다.
예를 들면, Java Virtual Machine은 Java Application이 OS에 종속되지 않게 해준다는 특성이 있다.
JVM은 SW로 볼 수 있다. 즉, HW를 software화 하는 것을 Virtual(가상화)라고 한다.
TCP는 기본적으로 Client와 Server로 구성된다.
Client는 Active하게 연결을 하는 주체이고, Server는 기다리고 있다가 연결을 받는 주체이다.
- Server는 Socket을 연결대기(Listen) 상태로 열어둔다.
- Client측에서 PID를 갖는 어떠한 Process가 Socket을 Open한다.
- 이때 운영체제는 해당 Socket에다가 TCP Port 번호를 부여한다.
결과적으로 TCP 연결을 시도해보기 위해서는 상대방의 IP와 TCP Port 번호를 알아야만 한다.
만약 Server 쪽에 Listen 상태의 Socket이 없다면 Client로 부터 연결이 온다고 해서 OS 단에서 거부(reject) 해버린다.
참고로 Create 대신 Open이라는 단어를 쓰는 이유는 Client는 Socker Pool에서 Socket을 꺼내서 쓰기 때문이다.
만약 파일을 다운 받는데 연결을 끊게 되면 어떻게 될까?
연결은 논리적인 개념이라고 했다. 물리적으로 LAN 케이블을 빼더라도 논리적인 연결은 끊기지 않는다.
TCP에는 재전송 타이머라는 것이 있는데 기본 근사 값은 대략 3초 정도다. 하지만 대부분의 운영체제들은
이를 1초 미만으로 설정해둔다. 재전송 타이머가 만료된 후에도 확인 응답을 받지 못한 경우에는 세그먼트를
재전송하고 RTO(Retransmission Time-Out) 값은 두 배로 증가된다.
예를 들면 `1 > 2 > 4 > 8 ...` 간격으로 재전송된다.
보통 최대 5회 재전송을 시도하고 5회 이상 모두 실패할 경우 대게 전송 오류가 발생한다고 한다.
❒ 3-way handshake
그럼 어떻게 신뢰성 있는 연결 통로를 만들까? 바로 3-way handshake라는 작업을 통해서 통로를 만든다.
3-way handshake 과정을 살펴보자.
1. SYN(SYNchronization) 단계
클라이언트는 서버에 클라이언트의 ISN을 담아 SYN을 보낸다.
2. SYN + ACK 단계
서버는 클라이언트의 SYN을 수신하고 서버의 ISN을 보내며 승인번호로 클라이언트의 ISN + 1을 보낸다.
3. ACK(ACKnowledgement) 단계
클라이언트는 서버의 ISN + 1한 값인 승인번호를 담아 ACK를 서버에 보낸다.
위 과정에서 Client와 Server가 주고 받는 PDU를 Segment라고 한다. 보통 Segment는 `header + payload` 로
구성되는데 여기서는 아래와 같이 오직 Header(IP Header + TCP Header)로만 구성된다.
❒ 4-way handshake
반대로 연결을 해제 할 때는 4-way handshake 과정이 발생한다.
1. FIN (client → server)
먼저 클라이언트가 연결을 닫으려고 할 때 FIN으로 설정된 세그먼트를 보낸다.
클라이언트는 FIN_WAIT_1 상태로 변경되고, 서버의 응답을 기다린다.
2. ACK (server → client)
서버는 클라이언트에게 ACK라는 승인 세그먼트를 보낸다. 그리고 CLOSE_WAIT 상태가 된다.
클라이언트가 세그먼트를 받으면 FIN_WAIT_2 상태에 들어간다.
3. FIN (server → client)
서버는 ACK를 보내고 일정 시간 이후에 LAST_ACK 상태가 되고 클라이언트에게 FIN이라는 세그먼트를 보낸다.
4. ACK (client → server)
클라이언트는 TIME_WAIT 상태가 되고 다시 서버로 ACK를 보내면 서버는 CLOSED 상태가 된다.
이후 클라이언트는 어느 정도의 시간을 대기한 후 CLOSED 상태가 되며, Socket을 반납한다.
이렇게 클라이언트와 서버의 모든 자원의 연결이 해제된다.
위 과정에서 눈여겨봐야 할 부분은 TIME_WAIT 상태이다.
그냥 연결을 닫으면 되는데 일정 시간 뒤에 연결을 끊는건 왜 그럴까?
1. 지연 패킷이 발생할 경우를 대비.
이미 CLOSED 상태인 연결에 네트워크 이슈로 인해 패킷이 늦게 도착해 데이터를 처리하지 못하면
데이터 무결성 문제가 발생하기 때문이다.
2. 두 장치의 연결이 닫혔는지 확인하기 위해.
만약 서버의 상태가 LAST_ACK 상태에서 연결이 닫히게 되면 다시 새로운 연결을 하려고 할 때
서버는 줄곧 LAST_ACK로 되어 있기 때문에 접속 오류가 나타난다.