Network_17 : TCP 통신 흐름

이준익·6일 전

TCP는 데이터를 보내기 전에 먼저 “연결”을 만든다.

여기서 말하는 연결은 물리적으로 선을 새로 꽂는다는 뜻은 아니다.
클라이언트와 서버가 서로 통신할 준비가 되었는지 확인하고, 데이터를 주고받을 상태를 맞추는 과정에 가깝다.

UDP는 일단 데이터를 던져놓고 시작하는 느낌이라면, TCP는 먼저 상대에게 말을 건다.

“너 받을 준비 됐어?”
“응, 나 준비됐고 너도 준비됐지?”
“응, 그럼 이제 보내자.”

이렇게 서로 확인한 뒤에야 본격적인 데이터 전송이 시작된다.

TCP는 왜 연결을 먼저 만들까

TCP는 신뢰성을 중요하게 생각하는 전송 방식이다.

데이터가 중간에 사라질 수도 있고, 순서가 뒤섞일 수도 있고, 상대가 아직 받을 준비가 안 됐을 수도 있다.
그래서 TCP는 데이터를 보내기 전에 최소한의 약속을 먼저 맞춘다.

비유하자면 전화를 거는 상황과 비슷하다.

내가 상대에게 전화를 건다.
상대가 받는다.
서로 목소리가 들리는지 확인한다.
그제야 본격적인 대화를 시작한다.

TCP 연결도 비슷하다.
데이터를 보내기 전에 양쪽이 서로의 존재와 준비 상태를 확인한다.

이 과정을 3-way handshake라고 한다.

3-way handshake

3-way handshake는 TCP 연결을 시작하기 위한 3단계 확인 과정이다.

이름 그대로 세 번의 메시지가 오간다.

첫 번째, 클라이언트가 서버에게 SYN을 보낸다.

SYN은 “연결을 시작하고 싶다”는 의미다.

클라이언트 입장에서는 이런 느낌이다.

“서버야, 나 너랑 TCP 연결하고 싶어.”

두 번째, 서버는 SYN/ACK를 보낸다.

SYN/ACK는 두 가지 의미를 함께 담고 있다.

“네 요청을 받았어.”
“나도 연결할 준비가 됐어.”

여기서 ACK는 확인 응답이다.
상대가 보낸 메시지를 잘 받았다고 알려주는 표시다.

세 번째, 클라이언트가 다시 ACK를 보낸다.

“나도 네 응답을 잘 받았어. 이제 연결하자.”

이 세 단계가 끝나면 TCP 연결이 수립된다.

정리하면 흐름은 이렇게 볼 수 있다.

클라이언트 → 서버 : SYN
클라이언트 ← 서버 : SYN/ACK
클라이언트 → 서버 : ACK

여기서 중요한 건, TCP 연결은 한쪽이 일방적으로 “연결됐다”고 선언하는 구조가 아니라는 점이다.
클라이언트와 서버가 서로 메시지를 주고받으면서 양쪽 모두 준비됐다는 사실을 확인한다.

장비가 판단하듯이 보면

클라이언트가 서버에 접속하려고 한다.

예를 들어 브라우저가 웹 서버의 443번 포트에 TCP 연결을 맺으려고 한다고 생각해보자.

클라이언트는 먼저 SYN을 보낸다.

“나는 이 서버의 443번 포트와 연결하고 싶다.”

서버는 이 요청을 받는다.
그리고 자신이 해당 포트에서 요청을 받을 수 있는 상태라면 SYN/ACK를 돌려준다.

“요청 받았다. 나도 연결 가능하다.”

클라이언트는 마지막으로 ACK를 보낸다.

“확인했다. 이제 데이터를 보내겠다.”

이후에야 HTTP 요청 같은 실제 애플리케이션 데이터가 TCP 연결 위에서 오갈 수 있다.

즉, 우리가 브라우저에서 웹사이트에 접속할 때 바로 HTTP 요청부터 날아가는 것이 아니다.
그 전에 TCP 연결을 위한 준비 과정이 먼저 있다.

TCP 상태를 흐름으로 이해하기

TCP에는 여러 상태가 있다.

CLOSED, LISTEN, SYN-SENT, SYN-RECEIVED, ESTABLISHED 같은 상태들이 나오는데, 처음 보면 외울 게 많아 보인다.
하지만 지금 단계에서는 상태 이름을 전부 암기하기보다 연결의 흐름을 이해하는 게 더 중요하다.

서버는 보통 연결 요청을 기다리는 상태에 있다.

이 상태를 LISTEN이라고 볼 수 있다.

클라이언트가 SYN을 보내면 클라이언트는 연결 요청을 보낸 상태가 된다.
서버는 SYN을 받고 SYN/ACK를 보낸 상태가 된다.
마지막 ACK까지 오가면 양쪽은 ESTABLISHED 상태가 된다.

ESTABLISHED는 TCP 연결이 실제로 맺어진 상태다.

이제 이 연결을 통해 데이터를 주고받을 수 있다.

흐름으로 보면 이렇게 이해할 수 있다.

서버: 연결 요청을 기다림
클라이언트: SYN 전송
서버: SYN/ACK 응답
클라이언트: ACK 응답
양쪽: 연결 성립

여기서 중요한 건 TCP 상태가 단순한 이름표가 아니라는 점이다.
각 상태는 “지금 연결 과정에서 어디까지 왔는지”를 나타낸다.

연결이 끝날 때도 절차가 있다

TCP는 연결을 시작할 때만 절차가 있는 것이 아니다.
연결을 끝낼 때도 절차가 있다.

데이터를 다 주고받았다고 해서 그냥 아무 말 없이 끊어버리면 문제가 생길 수 있다.

상대는 아직 보낼 데이터가 남아 있을 수도 있고, 마지막 데이터가 도착하지 않았을 수도 있다.
그래서 TCP는 연결 종료도 서로 확인하면서 진행한다.

대표적으로 FIN과 ACK가 사용된다.

FIN은 “이제 보낼 데이터가 없다. 연결을 종료하고 싶다”는 의미다.
ACK는 “그 종료 요청을 확인했다”는 의미다.

보통 한쪽이 FIN을 보내면, 상대가 ACK로 확인한다.
그리고 상대도 더 보낼 데이터가 없으면 FIN을 보낸다.
처음 FIN을 보냈던 쪽은 다시 ACK를 보내면서 종료를 마무리한다.

흐름만 보면 이런 느낌이다.

A → B : FIN
A ← B : ACK
A ← B : FIN
A → B : ACK

연결을 시작할 때 서로 준비됐는지 확인했다면,
연결을 끝낼 때는 서로 더 이상 보낼 데이터가 없는지 확인하는 셈이다.

왜 종료는 더 복잡해 보일까

연결 시작은 3단계인데, 종료는 보통 4단계처럼 보인다.

이유는 TCP 연결이 양방향 통신이기 때문이다.

A가 B에게 “나는 이제 보낼 데이터가 없어”라고 말해도,
B는 아직 A에게 보낼 데이터가 남아 있을 수 있다.

그래서 한쪽 방향의 종료와 반대쪽 방향의 종료를 따로 확인해야 한다.

전화를 끊는 상황으로 비유하면 이렇다.

내가 “나는 할 말 다 했어”라고 말해도,
상대가 “잠깐, 나는 아직 할 말이 있어”라고 할 수 있다.

상대도 할 말을 다 끝낸 뒤에야 둘 다 통화를 종료할 수 있다.

TCP 연결 종료도 비슷하다.
각 방향의 데이터 전송이 끝났는지 확인하면서 연결을 닫는다.

개발할 때 이 흐름이 왜 중요할까

웹 개발을 하다 보면 “서버는 떠 있는데 접속이 안 된다”는 상황을 만날 수 있다.

이때 단순히 서버 프로세스만 볼 것이 아니라, TCP 연결이 실제로 맺어지는지도 생각해야 한다.

클라이언트가 SYN을 보냈는데 서버가 SYN/ACK를 주지 않는다면, 중간에 방화벽이 막고 있을 수도 있다.
서버 포트가 열려 있지 않을 수도 있다.
로드밸런서나 보안 그룹 설정이 잘못됐을 수도 있다.

반대로 TCP 연결은 잘 맺어졌는데 HTTP 응답이 이상하다면, 문제는 그보다 위쪽인 애플리케이션 계층에 있을 가능성이 커진다.

여기서 중요한 건 TCP 연결 수립은 웹 요청이 실제로 오가기 전의 관문이라는 점이다.

브라우저에서 서버로 요청을 보낸다고 할 때, 그 아래에서는 먼저 TCP 연결이 만들어진다.
그리고 그 연결 위에서 HTTP 요청과 응답이 오간다.

흐름으로 다시 보기

TCP 연결은 시작부터 종료까지 하나의 대화처럼 볼 수 있다.

처음에는 연결하고 싶은 쪽이 SYN을 보낸다.
상대는 SYN/ACK로 준비됐다고 답한다.
다시 ACK가 오가면 연결이 성립된다.

연결이 성립된 뒤에는 데이터를 주고받는다.

데이터 전송이 끝나면 FIN과 ACK를 주고받으며 연결을 종료한다.

TCP 상태는 이 과정에서 현재 연결이 어디에 있는지를 나타내는 표시다.
처음부터 상태 이름을 전부 외우려고 하기보다, 연결이 생기고 유지되고 끝나는 흐름을 먼저 잡는 게 더 이해하기 좋다.

TCP는 그냥 데이터를 보내는 방식이 아니다.
보내기 전에 확인하고, 주고받는 동안 관리하고, 끝낼 때도 확인하는 방식이다.

그래서 TCP는 UDP보다 느리고 복잡하지만, 그만큼 안정적인 통신을 만들 수 있다.

profile
밥 잘 먹고 잠 잘 잡니다

0개의 댓글