커넥션 — TCP 의 3 웨이 핸드셰이크와 연결 종료
커넥션의 정체는 양 끝이 합의한 번호의 세기 시작. SYN → SYN/ACK → ACK 가 왜 3 회 필요한지, FIN 과 TIME_WAIT 로 무엇을 지키고 있는지를 따라간다.
「커넥션을 맺는다」의 정체는 무엇인가
connect() 가 돌아오면 연결됐다, 정도의 감각으로 평소에는 곤란하지 않습니다. 하지만 장애 때에는 「연결되지 않는다」의 내용 — 거부당했는가, 응답이 없는가, 확립 후에 끊겼는가 — 를 구별하지 못하면 앞으로 나아갈 수 없습니다. 그러기 위해서는 「커넥션을 맺는다」가 물리적으로 무엇인지를 알 필요가 있습니다.
답은 의외로 수수합니다. 경로 상에 전용선이 확보되는 것이 아니고, 도중의 라우터는 아무것도 기억하지 않습니다. 커넥션이란 양 끝의 2 대가 각각 메모리 상에 갖는 「이 4 요소 조합의 상대와, 어느 번호까지 주고받았는가」라는 장부의 쌍에 지나지 않습니다. 그래서 커넥션을 만드는 의식에서 해야 할 일도 하나뿐 — 서로의 「세기 시작하는 번호」를 알리고, 확실히 들었다고 서로 확인하는 것입니다.
이 세기 시작하는 번호를 초기 시퀀스 번호(ISN)라고 부릅니다. 1 부터 세기 시작하지 않는 이유는 제 5 문의 TIME_WAIT 와도 통합니다 — 직전까지 같은 4 요소 조합으로 통신하던 오래된 패킷과 섞이지 않도록, 매번 예측하기 어려운 값부터 시작하는 것입니다.
3 웨이 핸드셰이크. ISN 은 클라이언트 5000, 서버 9000 의 예입니다. ack 번호가 항상 「상대의 번호 + 1 = 다음에 원하는 번호」가 되어 있는지 확인해 주세요.
3 개의 패킷을 하나씩 읽는다
클라이언트의 ISN 을 5000, 서버의 ISN 을 9000 으로 한 예로 따라갑니다.
seq = 5000. 「접속하고 싶다. 나는 5000 부터 세기 시작한다」. SYN 은 데이터를 운반하지 않지만, 번호를 1 개 소비한다seq = 9000, ack = 5001. 「5001 까지 들었다(= 당신의 SYN 을 받았다). 나는 9000 부터 세기 시작한다」 — 확인과 선언의 합승seq = 5001, ack = 9001. 「당신의 세기 시작도 들었다」. 여기서 양쪽 모두 ESTABLISHED 가 되어, 데이터를 보낼 수 있다왜 3 회인가. TCP 는 양방향으로 독립된 2 개의 바이트 스트림을 가지므로, 방향마다 「세기 시작의 선언(SYN)」과 「들었다는 확인(ACK)」이 필요합니다. 역할은 합계 4 개이지만, 가운데 2 개(서버의 ACK 와 서버의 SYN)는 1 패킷에 합승할 수 있으므로, 최소 3 패킷. 2 회로는 「서버의 선언이 도착했다」는 것을 아무도 확인하지 않았고, 4 회는 낭비가 하나 있다, 는 것입니다.
소문제 2-1 — 3 웨이 핸드셰이크를 1 패킷씩
3 개 패킷 각각이 「말하고 있는 것」을 확인합니다. 번호 문제는 종이에 써서 계산해 주세요.
Q1. 3 웨이 핸드셰이크의 2 번째 패킷(서버가 돌려주는 SYN/ACK)이 말하고 있는 것으로 올바른 것은 어느 것입니까.
SYN/ACK 는 2 가지 역할의 합승입니다. ACK 부분이 클라이언트의 SYN 에 대한 확인, SYN 부분이 서버 자신의 세기 시작(서버 측 ISN)의 선언. TCP 는 양방향으로 각각 독립된 바이트 스트림을 가지므로, 양방향 각각에 SYN 과 ACK 가 1 회씩 필요합니다. 그것을 3 개 패킷에 눌러 담은 것이 이 형태입니다.
Q2. 클라이언트가 초기 시퀀스 번호 ISN = 5000 으로 SYN 을 보냈습니다. 서버가 돌려주는 SYN/ACK 의 ack 번호는 얼마입니까. (SYN 은 데이터를 운반하지 않지만, 시퀀스 번호를 1 개 소비한다)
ack 번호는 「다음에 원하는 번호」입니다. SYN 은 시퀀스 번호를 1 개 소비하는 약속이므로, seq = 5000 의 SYN 을 받은 서버가 다음에 원하는 것은 5001. 따라서 ack = 5001 입니다. 이 「다음에 원하는 번호를 돌려준다」는 규칙은 제 3 장의 데이터 전송에서도 완전히 같은 형태로 사용합니다.
Q3. 핸드셰이크가 2 회(SYN → SYN/ACK 로 끝)로는 성립하지 않는 이유로 올바른 것은 어느 것입니까.
2 회로 끝나면, 서버는 자신의 SYN/ACK 가 도착했는지 모른 채 「확립되었다」고 간주할 수밖에 없습니다. 늦게 도착한 오래된 SYN 에도 즉시 커넥션을 만들어 버려, 상대가 없는 유령 커넥션을 떠안게 됩니다. 3 번째의 ACK 는 「서버의 세기 시작도 확실히 들었다」는 확인입니다. 양방향 2 개 스트림 각각의 SYN + ACK(합계 4 역할)를 합승으로 3 패킷에 압축한, 이것이 최소 횟수입니다. 암호 키의 교환은 TLS 의 일이며 TCP 에는 포함되지 않습니다.
연결 종료는 4 역할 — 한 방향씩 가게를 닫는다
종료가 확립보다 한 수 많은 것은, 합승할 수 있다고는 할 수 없기 때문입니다. 「더 이상 보낼 것이 없다」는 선언 FIN 은 자신에게서 상대로의 방향만을 닫습니다. 상대에게는 아직 보낼 것이 남아 있을지 모르므로, FIN 을 보낸 후에도 수신은 계속할 수 있습니다(하프 클로즈). 양방향 각각에 FIN 과 ACK 가 필요하므로, 기본형은 4 패킷입니다.
그리고 먼저 FIN 을 보낸 쪽은 모두 끝난 후에도 TIME_WAIT 상태로 한동안(많은 구현에서 30 초〜몇 분) 남습니다. 이유는 2 가지입니다.
또 하나, 예의 없는 끝맺음이 RST(리셋)입니다. 「그런 커넥션은 모른다/지금 당장 부순다」는 일방적인 통고로, 리슨하는 곳이 없는 포트로의 SYN, 크래시한 프로세스 앞으로의 데이터 등에 대해 돌아옵니다. FIN 은 합의에 의한 폐점, RST 는 다짜고짜 파기 — 캡처에서 이 2 가지를 구별할 수 있는 것만으로도, 장애의 풍경은 크게 달라집니다.
소문제 2-2 — 연결 종료와 TIME_WAIT, 그리고 RST
시작보다 끝이 더 심오한 것이 TCP 입니다. 실무에서 만나는 빈도도 종료 쪽이 더 높습니다.
Q4. TCP 의 연결 종료에서 FIN 이 양방향으로 1 회씩(합계 2 회, 각각에 ACK) 필요한 이유는 어느 것입니까.
FIN 은 「나로부터 당신으로의 방향은, 더 이상 보낼 것이 없다」는 한 방향의 선언입니다. 상대에게는 아직 보낼 것이 남아 있을지 모르므로, 자신이 FIN 을 보낸 후에도 상대의 데이터를 계속 수신할 수 있습니다(하프 클로즈). 양방향 각각의 FIN + ACK 로 합계 4 패킷(합승으로 3 이 되기도 함) — 확립이 3 회로 끝나는데 종료가 4 회인 것은, 이 「한 방향씩 닫는」 설계 때문입니다.
Q5. 먼저 FIN 을 보내 종료한 쪽은 곧바로 사라지지 않고 TIME_WAIT 상태로 한동안 기다립니다. 이 대기 시간의 목적으로 올바른 것은 어느 것입니까.
TIME_WAIT 의 역할은 2 가지. (1) 자신이 돌려준 마지막 ACK 가 소실되면 상대는 FIN 을 재전송해 오므로, 응답할 수 있도록 남는다. (2) 네트워크 상을 떠도는 오래된 세그먼트가 수명이 다해 사라질 때까지 기다려, 같은 (송신원 IP, 송신원 포트, 목적지 IP, 목적지 포트) 를 재이용한 새로운 커넥션에 옛날 데이터가 섞여 들어가는 사고를 막는다. 즉 고장이 아니라 약속을 끝까지 지키기 위한 정상적인 상태입니다. 제 7 장에서 「TIME_WAIT 대량 발생」 케이스를 다룹니다.
Q6. 아무도 리슨하고 있지 않은 포트로 SYN 을 보낸 경우와, 방화벽이 패킷을 말없이 버리는 경우의, 클라이언트 측에서 본 차이로 올바른 것은 어느 것입니까.
리슨하는 곳이 없는 포트에 SYN 이 도착하면, 호스트의 OS 는 RST(리셋)를 즉시 돌려주고, 클라이언트는 Connection refused 로 곧바로 실패합니다. 한편, 말없이 버리는(drop 하는) 방화벽에서는 응답이 아무것도 없으므로, 클라이언트는 SYN 을 재전송하면서 계속 기다리다가 마지막에 타임아웃됩니다. 「즉시 거부 = 상대 호스트까지는 도착하고 있다」「타임아웃 = 응답이 돌아오지 않는다」 — 이 구분은 제 7 장의 케이스 연습에서 주역이 됩니다.
이 장에서 가져갈 것
- 커넥션의 정체는 양 끝이 갖는 장부의 쌍. 확립이란 「세기 시작하는 번호(ISN)」의 합의
- 핸드셰이크는 양방향분의 SYN + ACK 4 역할을 3 패킷에 합승시킨 최소형. ack 는 항상 「다음에 원하는 번호」
- 종료는 FIN 으로 한 방향씩. TIME_WAIT 는 고장이 아니라, 마지막 ACK 의 보험과 오래된 세그먼트의 독 빼기
- RST 즉시 반환 = 도착한 뒤의 거부, 무응답 타임아웃 = 애초에 응답이 돌아오지 않는다. 구분의 첫걸음
다음 장에서는 확립한 뒤의 주역 — 시퀀스 번호와 ACK 번호가 데이터 전송에서 어떻게 움직이는가 — 를 손계산으로 따라갑니다.