- 접속자 목록 관리2026년 06월 23일
- 소쩍소쩍이
- 작성자
- 2026.06.23.:46
접속자 목록 관리하기
지금까지 서버는 현재 접속 중인 클라이언트들을 따로 기억하지 못했다.
이 문제를 해결하기 위해 서버 안에 clients 리스트를 만들고, 클라이언트가 접속하면 목록에 추가하고 종료하면 목록에서 제거하는 구조를 만든다.
서버 - clients 리스트와 print_clients() 만들기
먼저 현재 접속 중인 클라이언트를 저장할 clients 리스트를 만든다.
import socket import threading HOST = "127.0.0.1" PORT = 5000 clients = [] # 현재 접속 중인 클라이언트 소켓 목록서버가 처음 실행될 때는 아무도 접속하지 않았기 때문에 빈 리스트 상태이다. 이후 클라이언트가 접속하거나 종료할 때마다 이 리스트가 바뀐다.
clients를 함수 안에 정의하면 스레드마다 별도의 리스트가 생겨 공유되지 않는다. 따라서 clients 리스트는 함수 바깥, 파일 상단에 전역으로 정의해야 한다.
주의
clients 리스트는 여러 스레드에서 함께 사용해야 하므로 함수 안이 아니라 파일 상단에 정의한다.print_clients() 함수 추가
접속자 수가 제대로 변하는지 확인하기 위해 print_clients() 함수를 만든다.
def print_clients(): print(f"현재 접속자 수: {len(clients)}")코드 의미 clients 현재 접속 중인 클라이언트 소켓 목록 len(clients) 현재 접속자 수 print_clients() 접속자 수를 화면에 출력하는 함수. 접속·종료가 일어나는 곳마다 호출해 목록이 의도대로 바뀌는지 확인하는 역할 접속 시 추가, 종료 시 제거 연결하기
handle_client()에 종료 시 제거 코드 추가
클라이언트 처리가 끝나면 해당 클라이언트 소켓을 clients 목록에서 제거해야 한다.
handle_client() 전체 코드 보기
import socket import threading HOST = "127.0.0.1" PORT = 5000 clients = [] def print_clients(): print(f"현재 접속자 수: {len(clients)}") def handle_client(client_socket, client_address): print(f"클라이언트 처리 시작: {client_address}") with client_socket: while True: data = client_socket.recv(1024) if not data: print(f"클라이언트 연결 끊김: {client_address}") break message = data.decode("utf-8") print(f"{client_address} 메시지: {message}") if message == "exit": print(f"클라이언트 종료 요청: {client_address}") break response = f"서버가 받은 메시지: {message}" client_socket.send(response.encode("utf-8")) # with 블록이 끝난 뒤 — 소켓은 이미 닫혔고, 목록에서 제거 if client_socket in clients: # 이미 제거된 소켓을 다시 제거하려 할 때 ValueError 방지 clients.remove(client_socket) # 종료된 클라이언트를 목록에서 제거 print(f"접속자 목록에서 제거: {client_address}") print_clients() print(f"클라이언트 처리 종료: {client_address}")주의
clients.remove() 코드는 with 블록 안이 아니라 바깥에 두는 것이 좋다.
with 블록 안에서 제거하면 소켓이 아직 열려 있는 상태에서 목록이 줄어들 수 있고, 나중에 브로드캐스팅할 때 문제가 생길 수 있다.핵심
exit 입력과 연결 끊김이 거의 동시에 일어나는 드문 상황에서 같은 소켓을 두 번 제거하려 하면 ValueError가 발생할 수 있다.
그래서 if client_socket in clients: 조건을 먼저 확인하고 제거한다.메인 루프에 접속 시 추가 코드 연결
클라이언트가 새로 접속하면 clients 리스트에 해당 소켓을 추가한다.
메인 루프 코드 보기
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as server_socket: server_socket.bind((HOST, PORT)) server_socket.listen() print("접속자 목록 관리 서버를 시작합니다.") print("클라이언트 접속 대기 중...") while True: client_socket, client_address = server_socket.accept() print(f"클라이언트 접속: {client_address}") clients.append(client_socket) # 접속 시 목록에 추가 — start() 전에 실행 print(f"접속자 목록에 추가: {client_address}") print_clients() client_thread = threading.Thread( target=handle_client, args=(client_socket, client_address) ) client_thread.start()주의
clients.append(client_socket)은 스레드를 시작하기 전에 실행하는 것이 좋다.
스레드가 먼저 시작되면 아주 드물게 handle_client()가 종료 처리하려는 순간, 아직 append가 실행되지 않은 상태가 될 수 있다.흔한 오류와 해결 방법
접속자 목록을 관리할 때는 추가와 제거 위치가 중요하다. 특히 스레드가 여러 개 동시에 실행되기 때문에 같은 소켓을 두 번 제거하지 않도록 주의해야 한다.
오류 상황 원인 및 해결 방법 ValueError: list.remove(x): x not in list 이미 제거된 소켓을 다시 제거하려 했음. if client_socket in clients: 조건을 먼저 확인한다 접속자 수가 줄어들지 않음 clients.remove()가 실행되지 않았음. handle_client() 반복문이 끝난 뒤 with 블록 밖에 제거 코드가 있는지 확인한다 접속자 수가 계속 늘기만 함 exit 종료와 연결 끊김 상황 중 하나에서 제거가 누락됨. if not data: break와 if message == "exit": break 두 경로 모두 with 블록을 빠져나오는지 확인한다 NameError: name 'clients' is not defined clients 리스트가 함수 안에 정의되어 있음. clients = []를 함수 바깥, 파일 상단에 정의한다 ConnectionRefusedError 서버가 실행되지 않은 상태에서 클라이언트를 실행했음. python chat_server/server.py를 먼저 실행한다 여러 클라이언트 출력 순서가 섞임 여러 스레드가 동시에 print()를 호출하는 것. 정상 동작이다. 접속자 수 숫자 변화가 맞는지를 기준으로 판단한다 정리
여러 클라이언트를 관리하려면 서버가 현재 접속 중인 클라이언트 소켓을 기억해야 한다.
이를 위해 clients 리스트를 파일 상단에 만들고, 클라이언트가 접속하면 append()로 추가하고 종료되면 remove()로 제거한다.
스레드 환경에서는 같은 소켓을 두 번 제거하지 않도록 if client_socket in clients: 조건을 확인하는 것이 안전하다.'Server - Client' 카테고리의 다른 글
왜 메시지 규칙이 필요한가 (0) 2026.06.23 전체 채팅 브로드캐스팅 (0) 2026.06.23 스레드로 클라이언트 처리 (0) 2026.06.23 스레드 개념과 기초 사용법 (0) 2026.06.23 여러 클라이언트 접속 구조 (0) 2026.06.23 다음글이전글이전 글이 없습니다.댓글
스킨 업데이트 안내
현재 이용하고 계신 스킨의 버전보다 더 높은 최신 버전이 감지 되었습니다. 최신버전 스킨 파일을 다운로드 받을 수 있는 페이지로 이동하시겠습니까?
("아니오" 를 선택할 시 30일 동안 최신 버전이 감지되어도 모달 창이 표시되지 않습니다.)