Backlog Queue와 Accept Queue
TCP 연결은 3-way handshake 과정을 통해 이뤄진다. 이 과정에서 서버 측 커널은 다음 두 가지 큐를 유지한다.
- Backlog Queue(SYN Queue): 클라이언트 측이 보낸 SYN 요청에 대해 half-open 요청을 해당 큐에 저장한다.
- Accept Queue: 3-way handshake가 완료되어 클라이언트로부터 ACK 패킷을 수신하면, 완전히 연결된 요청을 해당 큐에 저장한다.
3-way handshake 과정을 통해 두 큐의 쓰임을 살펴보자.
- 클라이언트 측은 연결을 시작하기 위해 서버 측에 SYN을 보낸다. 클라이언트 측은 SYN_SENT 상태가 된다.
- 서버 측이 클라이언트 측으로부터 SYN을 수신하면, 서버 측은 SYN_RECV 상태가 된다. 이때 커널은 backlog queue에 연결(half-open connection)을 저장하고 SYN+ACK을 클라이언트 측에 보낸다.
- 클라이언트 측이 SYN+ACK을 수신하면, 클라이언트가 ACK을 보내고 ESTABLISHED 상태가 된다.
- 서버 측이 ACK을 수신하면, 커널은 SYN 큐에서 연결을 제거하고 accept queue에 연결(fully established connection)을 저장한다. 그리고 서버 측도 ESTABLISHED 상태가 된다.
- 서버 측 애플리케이션이 accept 함수를 호출하면, accept queue에서 연결을 가져와 새로운 소켓을 생성하고 이를 애플리케이션에 반환한다. 애플리케이션은 이 소켓을 사용하여 클라이언트와의 통신을 수행한다.
Backlog Queue 사이즈
backlog queue의 사이즈는 서버 애플리케이션이 소켓을 열고 클라이언트의 연결 요청을 대기하기 위해 listen 함수를 호출할 때 지정할 수 있다. listen 함수의 두 번째 인자는 backlog 큐의 크기를 지정하는 파라미터이다. sockfd는 소켓 파일 디스크립터, backlog는 backlog 큐의 크기이다.
int listen(int sockfd, int backlog);
실제 큐의 크기는 listen 호출 시 지정한 값과 운영체제의 내부 구현 값 중 더 작은 값으로 설정된다. 스프링 부트를 사용하면 톰캣 서버를 사용하게 되는데 톰캣의 기본 backlog queue 사이즈 설정 값은 100이다. 그리고 server.tomcet.accept-count 속성을 통해 큐 사이즈를 변경할 수 있다.
톰캣의 설정은 다음과 같다.
- accept-count: backlog queue 사이즈
- 기본 값 = 100
- 너무 크게 설정하면, 메모리 자원을 낭비할 수 있다.
- 너무 작게 설정하면, 요청이 몰렸을 때 들어오는 요청들을 거절할 수 있다. 그리고 DDOS 공격에 취약해진다. SYN Flood Attack은 간략히 말하면 클라이언트가 SYN만 무수히 많이 보내고 ACK를 보내지 않아서 SYN Backlog Queue를 가득차게 만들어서 다른 사람이 연결을 맺지 못하게 만드는 공격이다.
- 설정 값 이상의 SYN에 대해서는 연결 요청을 거절하거나 패킷을 드롭한다.
- max-connections: 서버가 동시에 열 수 있는 클라이언트와의 연결 소켓의 개수
- 기본 값 = 8192
- 서버 측 애플리케이션이 accept 함수를 호출하면 accept queue에서 요청을 가져와, 새로운 소켓을 생성한다. 이 소켓의 수를 의미한다.
Reference