티스토리 뷰
들어가며
예전에는 3-Way Handshake와 4-Way Handshake를 단순히 면접에 자주 나오는 질문이라고만 생각했다. 그래서 그냥 달달 외우기만 했는데 실제 일을 하다 보니 해당 레이어에서 발생할 수 있는 문제를 겪게 되었다.
'어라, 저희 서버에서는 응답을 줬는데요? 왜 받지를 못할까요?'
서로 통신하는 친한(?) 사이에 내가 응답을 줬다고 모른척하면 문제는 해결되지 않는다.
커넥션을 연결하고, 데이터를 통신하고, 연결을 종료하는 과정까지 다시 짚어보고 이 과정에서 발생할 수 있는 타임아웃 이슈에 대해 정리해본다.
커넥션 연결 과정


TCP 커넥션 연결 : 3-Way Handshake
통신 시작 전에 데이터를 안전하게 보내고 받을 수 있는지 확인하는 작업을 해야 한다. 3번의 패킷을 주고받으며 통신을 준비하기 때문에 3-Way Handshake라고 부른다.
SYN
- 클라이언트는 서버와 커넥션을 연결하기 위해
SYN
패킷을 보낸다.- 클라이언트는 임의의 시퀀스 번호를 선택해
SYN
플래그를 세팅한다. (seq=0
)
- 클라이언트는 임의의 시퀀스 번호를 선택해
- 클라이언트는 서버와 커넥션을 연결하기 위해
SYN
,ACK
- 서버는 클라이언트의
SYN
을 수신하고 응답으로SYN
,ACK
패킷을 보낸다.
- 서버의 시퀀스 번호와 함께 클라이언트 시퀀스 번호에 대한
ACK
가 포함된다.
- 서버의 시퀀스 번호와 함께 클라이언트 시퀀스 번호에 대한
- 서버는 클라이언트의
ACK
- 클라이언트는 서버의
SYN
,ACK
패킷을 받으면,ACK
를 서버에 전송한다.- 이 패킷에는 서버의 시퀀스 번호에 대한
ACK
가 포함된다.
- 이 패킷에는 서버의 시퀀스 번호에 대한
- 클라이언트는 서버의
이 3단계 과정을 통해 클라이언트와 서버는 커넥션을 맺고, 데이터 전송을 위한 통신 경로를 준비한다.
TCP 커넥션 종료 : 4-Way Handshake
FIN
- 클라이언트(혹은 서버)는 커넥션 종료를 요청하려면 FIN 패킷을 보낸다.
ACK
- 서버 (혹은 클라이언트)는
FIN
패킷을 확인하고 응답으로ACK
패킷을 보낸다.- 이 패킷은 커넥션 종료 요청을 수락하는 메시지다.
- 서버 (혹은 클라이언트)는
FIN
- 서버 (혹은 클라이언트)는 데이터를 모두 보낸 후
FIN
패킷을 클라이언트로 보낸다.
- 서버 (혹은 클라이언트)는 데이터를 모두 보낸 후
ACK
- 클라이언트는 서버의 FIN 패킷을 받은 후, ACK 패킷을 서버로 전송하여 연결 종료를 수락한다.
- 이 패킷이 마지막이 되어 커넥션이 종료된다.
- 클라이언트는 서버의 FIN 패킷을 받은 후, ACK 패킷을 서버로 전송하여 연결 종료를 수락한다.
참고로 커넥션 종료는 클라이언트가 먼저 FIN 패킷을 보낼 수도 있고, 서버가 먼저 FIN 패킷을 보낼 수도 있다.
실제 실행한 TCP 커넥션 종료 과정을 보면 서버(8080)가 클라이언트(63158)에게 먼저 커넥션 종료를 요청한다.

- 서버가 클라이언트에게
FIN
,ACK
를 보낸다.FIN
패킷을 보내 커넥션 종료를 요청한다.ACK
를 함께 보내는 이유는 클라이언트가 마지막으로 보낸 데이터를 정상적으로 받았다는 의미다.
- 클라이언트가 서버에게
ACK
를 보낸다.- 서버의 커넥션 종료 요청을 수락한다.
- 클라이언트가 서버에게
FIN
,ACK
를 보낸다.- 클라이언트도 더 이상 데이터를 보내지 않는다는 걸 FIN 패킷으로 알린다.(여기서 왜 ACK를 또 보내는지는 모르겠다.)
- 서버가 클라이언트에게
ACK
를 보낸다.
이렇게 4-Way Handshake 과정을 거쳐 커넥션이 종료된다.
타임아웃
Connection Timeout
클라이언트가 서버와 커넥션 연결을 시도할 때, 연결이 성공되도록 기다리는 최대 시간.
이 시간 내에 커넥션이 연결되지 않으면 Connection Timeout
이 발생한다.
시나리오
- 서버가 죽어서 커넥션을 연결할 수 없는 상황

실행 결과
// 클라이언트 로그
Caused by: java.net.ConnectException: Connection refused

클라이언트가 SYN
패킷을 보내, 커넥션 연결하기를 원하지만 서버는 이를 거절한다.
TCP 레이어에서 연결 재시도를 한 번 하기 때문에 기록이 남은 걸로 보인다.
Read Timeout
클라이언트와 서버 간 커넥션 연결은 성공했으나, 실제 데이터 전송 과정에서 일정 시간이 넘으면 Read Timeout
이 발생한다.
시나리오
- 클라이언트에서 Read Timeout을 3초로 설정
- 실제 서버 응답은 4초가 걸린다.

실행결과
// 클라이언트 로그
Caused by: io.netty.handler.timeout.ReadTimeoutException: null

커넥션을 연결하고 3초 후에도 응답이 오지 않아서 클라이언트는 서버에게 FIN
, ACK
패킷을 보내며 연결 종료를 알린다.
서버는 커넥션을 연결하고 4초가 지난 후 응답을 보내지만, 클라이언트는 RST
패킷을 보내 응답을 거절한다.
해결방법
- 클라이언트는 서버로 요청을 재전송해서 데이터를 받는다.
- 서버의 평균 응답시간을 고려해 클라이언트에게 권장 타임아웃 시간을 알린다. 서버의 응답시간을 개선한다(...)
가장 좋은 방법은 둘 다 하는 것이다. 클라이언트는 Read Timeout 시간을 임의로 설정하는 대신, 서버 측에 문의하여 적정 타임아웃 시간을 확인하는 것이 좋다.
'Web' 카테고리의 다른 글
OpenCSV를 사용해서 CSV 파일 읽기 (0) | 2023.11.17 |
---|---|
WebSocket, SockJS, STOMP에 대해 알아보자 (0) | 2021.10.27 |
HTTP Live Streaming(HLS) 개념과 적용 예제 (0) | 2021.08.02 |
문자열 인코딩(character encoding) : ASCII, EUC-KR, UTF-8 (0) | 2020.12.13 |
- Total
- Today
- Yesterday
- 쿼리 파라미터 바인딩
- online ddl
- TCP연결
- 도메인구성요소
- mysql 온라인 ddl
- spring boot3 쿼리 로그
- 코프링
- 4Way Handshake
- 이모지입력오류
- jpa 쿼리 로그
- spring retry
- csv to bean
- csv 라이브러리
- github actions 구성요소
- mysql 이모지
- hibernate 쿼리 로그
- file
- opencsv
- 엔티티와값객체
- 문자집합변경
- 콜레이션변경
- AOP
- github actions components
- CGLIB프록시
- http커넥션
- github actions 기초
- tcp커넥션
- utf8mb3
- 콜레이션
- read timeout
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |