들어가기 앞서
Release의 모든 것이라는 책을 읽다가 보니, 포트가 1024 ~ 65535까지 총 64511개가 있는데 많은 사용자가 서버에 접속할 수 있는 이유에 대해서 설명하는 부분이 있었다.
주된 이유는 운영체제가 가상 IP 주소를 동일한 네트워크 인터페이스에 부여하기 때문이라고 적혀있다.
내가 예상하는 바와는 다른 내용이라 분석을 시도했다.
Contents
우선 64511개 보다 더 많은 TCP 연결을 동시에 할 수 있는 이유는, 소켓을 구성하는 데 있어서, 포트 번호로만 연결이 식별되지 않기 때문이다.
보통 (Source IP, Source Port, Destination IP, Destination Port)을 기준으로 튜플 key로 연결이 구분된다.
Client가 Server에 접속한다고 가정하면 다음과 같이 구성된다.
| source ip | source port | destination ip | destination port |
| 1.1.1.1 | 40000 | 10.0.0.1 | 443 |
| 1.1.1.1 | 40001 | 10.0.0.1 | 443 |
| 1.1.1.2 | 50000 | 10.0.0.1 | 443 |
| 1.1.1.3 | 60000 | 10.0.0.1 | 443 |
즉, Source IP당 Source Port가 계속 달라짐으로 서버측 포트 번호만으로 연결 수가 제한되지는 않는다.
그러면 이런 생각을 할 수 있을 것이다.
서버 입장에서는 소켓제한이 없는거는 이해했는데, 그럼 client가 connection close를 하지 않고 64511개를 초과해서 TCP를 연결하면 더 이상 연결 못하는 거 아닐까?
docker compose 환경을 이용해서, connection을 close하지 않고, client가 계속해서 소켓 연결을 시도하도록 만들었다.
server.py
import socket
HOST = "0.0.0.0"
PORT = 5000
def main():
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind((HOST, PORT))
s.listen()
print(f"[SERVER] Listening on {HOST}:{PORT}", flush=True)
count = 0
while True:
conn, addr = s.accept()
count += 1
if count % 1000 == 0:
print(f"[SERVER] Accepted connections: {count}", flush=True)
# 연결을 유지하기 위해 close 하지 않음
if __name__ == "__main__":
main()
client.py
import socket
import time
HOST = "server"
PORT = 5000
def main():
sockets = []
success = 0
while True:
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
sockets.append(s)
success += 1
if success % 1000 == 0:
print(f"[CLIENT] Connected: {success}", flush=True)
except ConnectionRefusedError as e:
print(f"[CLIENT] Server not ready ({e})", flush=True)
time.sleep(1)
continue
except OSError as e:
print(f"[CLIENT] Failed after {success} connections: {e}", flush=True)
time.sleep(5)
break
if __name__ == "__main__":
main()
Dockerfile
FROM python:3.13-slim
COPY server.py client.py ./
CMD ["python", "server.py"]
docker-compose.yml
version: "3.9"
services:
server:
build: .
container_name: tcp-server
command: ["python", "server.py"]
ulimits:
nofile:
soft: 1000000
hard: 1000000
networks:
- testnet
client1:
build: .
container_name: tcp-client-1
command: ["python", "client.py"]
depends_on:
- server
ulimits:
nofile:
soft: 100000
hard: 100000
networks:
- testnet
client2:
build: .
container_name: tcp-client-2
command: ["python", "client.py"]
depends_on:
- server
ulimits:
nofile:
soft: 100000
hard: 100000
networks:
- testnet
networks:
testnet:
driver: bridge
위와 같이 구성 후 docker compose up --build로 실행 시켜 보면, 다음과 같이 나온다.

약 60000개는 커녕, 28232개까지만 connection을 생성한 것을 볼 수 있다.
이유는, 운영체제에 있는데, 기본적으로 linux의 ephemeral port range는 32768 60999로 설정되어 있다.
즉, 32768 ~ 60999 포트만 TCP 소켓으로 사용 가능하다.
cat /proc/sys/net/ipv4/ip_local_port_range
# 32768 60999
그래서, 아래와 같이 직접 1024 ~ 65535 까지 사용하라고 변경해 보겠다.
docker-compose.yml
version: "3.9"
services:
server:
build: .
container_name: tcp-server
command: ["python", "server.py"]
ulimits:
nofile:
soft: 1000000
hard: 1000000
networks:
- testnet
client1:
build: .
container_name: tcp-client-1
command: ["python", "client.py"]
sysctls:
net.ipv4.ip_local_port_range: "1024 65535"
depends_on:
- server
ulimits:
nofile:
soft: 100000
hard: 100000
networks:
- testnet
client2:
build: .
container_name: tcp-client-2
command: ["python", "client.py"]
sysctls:
net.ipv4.ip_local_port_range: "1024 65535"
depends_on:
- server
ulimits:
nofile:
soft: 100000
hard: 100000
networks:
- testnet
networks:
testnet:
driver: bridge

각 Client의 connection 수 상승했다는 것을 알 수 있다.
그 외에 오차를 보이거나, connection이 연결되지 않는 이유를 보면, 대부분 운영체제의 설정이나, FD 개수 문제 등이 있다.
즉, TCP 연결은 linux의 커널의 역할이 큰데, TCP 관리 정책에 의해서 조금의 오차가 있는 것으로 보인다.
중요한 점은, 원하는 통신을 이루고 나면, TCP close를 해야 한다는 것이고, 실시간 서비스라 한다고 하더라도, 수만 개의 연결을 단일 client에 하는 경우는 없어야 한다는 점이다.
'IT' 카테고리의 다른 글
| [백엔드] timestamp를 압축 시켜보자 (0) | 2025.12.18 |
|---|---|
| [백엔드] timestamp의 Z와 +00:00 이야기 (0) | 2025.12.17 |
| [DB] UTC로 구성 한다는 것 (0) | 2025.12.12 |
| [백엔드] output json을 신경써서 만들어야 하는 이유(안티패턴) (0) | 2025.11.12 |
| [Azure] 클라우드를 몰랐던 스타트업 개발자의 실수 (0) | 2025.10.31 |