Skip to content

Dev

TCP 핸드쉐이크

Network2 min read

TCP는 연결지향 프로토콜이다. 양 종단간의 논리적으로 접속 가능한 상태로 만든 뒤 서버와 클라이언트는 지속적인 통신을 할 수 있게 된다.

해당 포스트에서는 TCP의 접속가능상태로 만드는 3way handshake와 접속 종료로 만드는 4way handshake에 대해 살펴보고, 이 과정을 통해 TCP가 의미하는 연결지향 프로토콜이 무엇인지 알아보도록 하겠다.

3way Handshake 과정

클라이언트와 서버간의 접속가능 상태를 만들기 위해서 서버와 클라이언트는 3way 핸드쉐이크 과정을 거친다. 핸드쉐이크 과정을 거치기전에 우선 클라이언트는 서버의 IP주소를 알아야 핸드쉐이크 요청이 가능하다.

클라이언트는 DNS 서버를 통해 IP주소를 조회하고 클라이언트의 TCP Header에 송,수신처에 대한 IP와 포트번호 값을 넣음으로써, 3way 핸드쉐이크 과정을 시작할 준비단계를 마친다.

이제 어떻게 서버와 클라이언트간의 3way 핸드쉐이크 과정이 진행되는지 살펴보겠다.

3way-handshake (여기서는 가장 일반적인 웹상황을 생각하여 클라이언트-서버의 단어를 사용하였지만, 요청자-수신자라는 단어가 조금 더 적절하다.)

1) 클라이언트는 서버에 접속의사를 밝히는 SYN 비트를 보낸다.(SYN 비트는 난수값으로 보낸다)
2) 서버는 클라이언트로 받은 SYN비트에 1을 더한 승인비트인 ACK비트와 또 다른 난수인 SYN 비트를 클라이언트에게 보낸다.
3) 클라이언트는 서버로부터 받은 SYN비트에 1을 더한 ACK비트를 서버로 보내주면, 클라이언트와 서버는 ESTABLISHED 상태로 접어들면서 데이터 전송 프로세스가 시작된다.

이렇게 3번의 통신프로세스를 통해서 서버와 클라이언트는 연결 가능한 상태로 만들어진다. 그런데 왜 총 3번의 통신 프로세스를 거치는걸까? 두번의 통신만으로도 연결 가능한 상태가 될 수 있을 것 같기도 하다.

1클라이언트 서버
2 ---- SYN -->
3 <- SYN, ACK --

(SYN비트는 클라이언트에서 생성한 값과 서버에서 생성한 값이 다르다)

위와 같이 생각할 수도 있는데, 이렇게 될 경우 서버에서 랜덤으로 생성된 SYN 비트를 클라이언트측이 잘 받았는지에 대해서 확인이 안된다. 그래서 서버에서 생성한 난수값을 클라이언트에서 잘 받았으면 +1을 더한 값인 ACK를 넘겨 상호 신뢰성있는 3way 핸드쉐이크과정을 거치게 된다.

또한 위의 과정은 가장 기본적인 TCP의 3way 핸드쉐이크 과정이다. RFC문서를 참고해보면 요청자와 수신자간의 동시에 접속의사를 밝히는 SYN비트를 보내는 케이스에 대해 3way 핸드쉐이크가 처리하는 방법, SYN 비트가 중복되어있을 경우 처리하는 방법등 다양한 예외상황들을 고려하여 3way 핸드쉐이크 과정에 대해 기술 되어있다.

여기서 중요한 포인트는 TCP가 다양한 예외상황까지 고려하여 접속가능상태를 안전하고 신뢰성있게 만든다는 점이다.

4way 핸드쉐이크 과정
1클라이언트                            서버
2 1. ESTABLISHED <---------------------------------------> ESTABLISHED
3
4 2. +--(Close)---+ +------------+
5 | FIN-WAIT-1 | ---------------- FIN ----------------> | CLOSE-WAIT |
6 +------------+ +------------+
7 +------------+ +------------+
8 3. | FIN-WAIT-2 | <--------------- ACK ----------------- | CLOSE-WAIT |
9 +------------+ +------------+
10 |
11 close()
12 |
13 +-----------+ +----------+
14 4. | TIME-WAIT | <--------------- FIN ------------------- | LAST-ACK |
15 +-----------+ +----------+
16 +-----------+ +----------+
17 5. | TIME-WAIT | --------------- ACK -------------------> | CLOSED |
18 +-----------+ +----------+
19
20 6. +--(2 MSL)--+
21   | CLOSED |
22 +-----------+

1) 클라이언트는 서버에게 접속 종료의사를 밝히는 FIN비트를 보낸다. 여기서 클라이언트는 자기가 능동적으로 연결해제를 요청하기 때문에 Active Close라고 부르며 close()함수를 실행하여 FIN-WAIT1 상태로 변경된다.

2) 서버는 FIN 비트를 받은 후 승인비트인 ACK 비트를 클라이언트에게 보낸 후 소켓의 close() 함수를 요청한다. (여기서 서버는 클라이언트가 접속종료 의사에 따라 서버도 종료 과정 프로세스를 진행해야하기 대문에 수동적인 close라 하여 Passive Close라 한다.) close()함수를 요청하기 때문에 서버는 CLOSE-WAIT 상태가 되며, 클라이언트는 서버가 아직 처리하지 못한 데이터가 있을 수도 있기 때문에 자신이 보낸 FIN비트를 다시 받기 위하여 대기하는 상태이므로 FIN-WAIT2 상태로 변경된다.

3) 서버는 더이상 송신할 데이터가 없으면, 나는 데이터 처리도 다 끝났으니 이제 접속종료가 가능하다는 의미로 클라이언트에게 처음에 받은 FIN비트를 다시 클라이언트에게 보내게 된다.

4) 클라이언트는 서버로부터 받은 FIN 비트가 자신이 보낸 FIN비트와 일치하는지 확인 후에 승인비트인 ACK 비트를 보낸 후에 CLOSED 상태에 들어간다. 여기서 TIME_WAIT의 시간을 결정하는 2MSL(Maximum segment lifetime : 최대 세그먼트 수정)로 정의된 시간만큼 유지하다가 CLOSED 상태로 변경된다. 서버는 ACK비트를 받고나서 CLOSED 상태에 들어가게 된다.

[Refference]