이슈 사항
실제 상용환경에서 발생한 상황이었는데 클라이언트 쪽 서버 주소로 소켓을 연결해서 정보를 보내주는 로직이 존재했었다.
근데 어느날 클라이언트 쪽에서 정보를 못받고 있다고 연락을 받아 급하게 찾아보니 로그에 Exception 요청된 주소로 배정할 수 없다.
로그가 주르륵 쏟아지는 상태였다. 태어나서 처음 보는 에러라 당황했던 기억을 되짚어보며 정리해보고자 한다.
소켓(Socket) 이란?
소켓(socket)은 컴퓨터 네트워크에서 프로세스 간 통신(IPC, Inter-Process Communication)을 수행하기 위한 endpoint
데이터를 작은 단위(패킷)로 나누어 전달하고, 수신된 패킷을 조립하여 원본 데이터를 복원하는 방식
주로, 채팅, 파일 전송, 게임 등과 같은 환경에서 활용된다.
endpoint : 소켓은 통신의 끝 지점(endpoint)으로, 하나의 소켓은 IP 주소와 포트 번호의 조합 정의 (ex : 127.0.0.1:8080) 클라이언트-서버 모델 : 서버는 소켓을 열고 클라이언트 요청을 기다리고, 클라이언트는 소켓을 열어 서버에 요청을 보냄 |
소켓의 종류
- 스트림 소켓 (TCP 소켓):
- 연결지향형이며 신뢰성 있는 데이터 전송 보장.
- 데이터가 순서대로 전달되고 손실되지 않음.
- 예: HTTP, FTP, Telnet 등.
- 데이터그램 소켓 (UDP 소켓):
- 비연결형이며 빠른 데이터 전송을 위해 신뢰성을 희생.
- 데이터가 순서 없이 도착하거나 손실될 수 있음.
- 예: VoIP, 스트리밍 서비스, 게임 등.
- 로컬 소켓:
- 동일한 시스템 내의 프로세스 간 통신(IPC)을 위해 사용.
- 네트워크를 거치지 않고 빠르게 통신 가능.
소켓 통신 과정

소켓은 이런 기본적인 통신 구조를 가지고 있는데 소켓은 무한정으로 열 수 있는 게 아니다.
소켓은 서버의 자원에 따라 열릴 수 있는 한계가 존재한다.
만약, 서버 OS 가 리눅스이면 OS 란 결국 하드웨어를 보다 잘 활용하기 위해서 사용되는 소프트웨어다.
그 OS 안에는 핵심으로 사용되는 부분이 있는데 커널이다.
아래는 리눅스의 구조이다.

이 커널을 활용하여 자원을 할당 받고 소켓을 만드는데 아래와 같은 일련의 과정을 거친다.
- 파일 디스크립터(FD): 소켓도 일반 파일처럼 FD를 할당받음. (/proc/<pid>/fd/에서 확인 가능)
- 네트워크 버퍼: 송수신 데이터를 저장하는 공간 할당 (/proc/sys/net/ipv4/tcp_mem)
- 포트 할당: 클라이언트/서버 통신 시 포트가 사용됨 (/proc/net/tcp에서 확인 가능)
- 소켓 테이블: 커널이 소켓을 관리하는 구조 (ss -s로 확인 가능)
TCP 소켓을 연결과정에서 3 -Way Handshake & 4 -Way Handshake 과정을 겪는데 쉽게 말하면 연결 & 연결 끊기 다.
해당 내용은 아래의 블로그에서 보다 잘 설명하고 있다.
[네트워크] TCP/UDP와 3 -Way Handshake & 4 -Way Handshake
TCP / UDP / 3-Way Handshake / 4-Way Handshake
velog.io
그럼 제목에서 알렸다 싶이 TIME_WAIT 가 왜 발생했는지 설명하자면 소켓 연결 통신 과정을 보면 연결 후 할 일이 끝나면 연결 끊는(close)가 진행된다.
연결을 끊을 때 상태가 TIME_WAIT 으로 변하는데 그 시간동안 잠깐의 시간이 소비된다. (CentOS의 경우 60초)
TIME_WAIT 상태란 무엇인가 · The Missing Papers
TIME_WAIT 상태란 무엇인가 14 Jun 2015 TIME_WAIT 상태가 늘어나면 서버의 소켓이 고갈되어 커넥션 타임아웃이 발생한다는 얘기를 한다. 이 말이 올바른 얘기인지, TIME_WAIT은 어떠한 경우에 발생하고 어
docs.likejazz.com
서버 리소스에서 소켓을 연결할 때 일시적인 포트를 할당하는데 포트는 한정되어 있다.
로컬 포트의 경우 최대 65,535개 할당할 수 있지만 range 가 정해져 있다.
아래의 명령어는 local port range를 확인할 수 있는 명령어이다.
sysctl net.ipv4.ip_local_port_range
EC2 환경에서 해당 명령어 사용 시 나오는 화면이다.

이 길이 처럼 사용할 수 있는 포트는 28,231 개 정도이다.
보통 28,231 개도 많아서 다 사용할 수 없지만 만약, 소켓이 1초당 1000개가 열리고 이게 3분간 지속된다면 어떻게 될까?
소켓은 무한정으로 열리고 포트도 게속 할당하게 되지만 TIME_WAIT 상태로 기존에 쓰던 소켓 포트를 사용할 수 없게 되고 결국에는 연결에 필요한 포트가 부족하게 될 것이다. 이 현상이 포트 고갈 현상이다.
소켓이 연결할 때는 IP와 PORT를 활용하게 되는데 할당할 수 없게 되면서 요청된 주소가 배정할 수 없게 된다.
해결방법
해결할 수 있는 방법은 여러가지이지만, 아래의 세 가지만 이야기 해보고자 한다.
1. 소켓 연결 실패 시 재시도 로직
만약 소스 상에서 소켓을 연결하는 구조가 있다면, 소켓을 연결하고 실패 될 때 재시도하는 로직을 추가하면 된다.
이렇게 되면 굳이 서버 옵션을 변경하지 않아도 소프트웨어 상에서 해결할 수 있다.
2. tcp_tw_reuse 활성화
서버에는 tcp_tw_reuse 라는 옵션이 존재하는데 해당 옵션은 TIME_WAIT 소켓을 재활용해서 사용하게 할 수 있는 방법이다.
tcp_tw_reuse와 tcp_tw_recycle
Linux Network Internal | 시스템 엔지니어에게 가장 골치 아픈 커널 파라미터가 뭐냐고 묻는다면 아마 위 두 값이라고 하시는 분들이 많으실 겁니다. 비슷하게 보이지만 차이점을 알기가 힘든 두 값
brunch.co.kr
3. tcp_local_port_range 늘리기
로컬 포트의 길이를 늘리는 방법인데, 권장된 방법은 아님.
아직은 해당 현상을 해결하지 못했지만... 만약하게 되면 2번 방법으로 진행하지 않을까 싶다.