poll로 한 걸음 더
select에는 1024개라는 상한과 매번 전부를 검사하는 부담이 있었습니다. poll은 그중 첫 번째 한계를 풀어 주는 select의 사촌입니다. 발상은 똑같고 사용법만 조금 다릅니다. 짧게 만나 보겠습니다.
poll의 차이
select는 소켓들을 리스트로 넘기고, 운영체제가 그것을 훑어 준비된 것을 골라 줍니다. 이때 select 내부의 구조 때문에 지켜볼 수 있는 소켓 수에 상한이 있었습니다.
poll은 이 상한을 없앱니다. 지켜볼 소켓들을 등록해 두면, 1024개를 훌쩍 넘겨도 문제없이 지켜봅니다. 그래서 적당히 많은 동시 접속을 다뤄야 할 때 select보다 poll이 낫습니다. 사용 방식도 한 번 등록해 두면 매번 소켓 리스트를 새로 넘기지 않아도 되어 조금 더 깔끔합니다.
poll 에코 서버
같은 에코 서버를 poll로 만듭니다.
# 새 파일: poll_server.pyimport socketimport selectserver = socket.socket(socket.AF_INET, socket.SOCK_STREAM)server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)server.bind(("127.0.0.1", 9003))server.listen(5)server.setblocking(False)poller = select.poll()poller.register(server, select.POLLIN)connections = {server.fileno(): server}print("poll 에코 서버 대기 중")while True: for fd, event in poller.poll(): s = connections[fd] if s is server: conn, addr = s.accept() conn.setblocking(False) poller.register(conn, select.POLLIN) connections[conn.fileno()] = conn else: data = s.recv(1024) if data: s.sendall(data) else: poller.unregister(fd) del connections[fd] s.close()
핵심 차이를 따라가 봅시다.
select.poll()로 poller라는 감시자를 하나 만듭니다. 그리고 poller.register(server, select.POLLIN)으로 듣기 소켓을 등록합니다. POLLIN은 읽을 데이터가 준비됐는지를 지켜보겠다는 뜻입니다. select에서는 매번 소켓 리스트를 통째로 넘겼지만, poll에서는 이렇게 한 번 등록해 두면 됩니다.
poller.poll()은 등록된 소켓 중 준비된 것들을 돌려줍니다. 다만 소켓 객체가 아니라 파일 디스크립터 번호와 어떤 일이 일어났는지를 알려 줍니다. PART 01에서 배운 그 파일 디스크립터입니다. 그래서 번호로 실제 소켓을 다시 찾을 수 있도록 connections라는 딕셔너리에 번호와 소켓을 짝지어 보관합니다.
나머지 흐름은 select 서버와 똑같습니다. 듣기 소켓이 준비됐으면 accept해서 새로 등록하고, 기존 소켓이 준비됐으면 recv로 받아 돌려줍니다. 클라이언트가 떠나면 poller.unregister로 감시 명단에서 빼고 딕셔너리에서도 지운 뒤 소켓을 닫습니다.
그래도 남는 한계
poll은 select의 소켓 수 상한을 풀어 주었습니다. 하지만 select의 두 번째 한계, 곧 매번 모든 소켓을 검사하는 부담은 그대로입니다. poll도 호출할 때마다 등록된 소켓 전부를 훑어 누가 준비됐는지 확인합니다. 소켓이 수만 개면 수만 개를 매번 훑습니다. 실제로 준비된 것이 몇 개뿐이어도 말입니다.
그래서 poll은 select보다는 낫지만, 수만 개의 동시 연결 앞에서는 여전히 한계를 보입니다. 이 마지막 벽을 넘는 것이 PART 06에서 만날 epoll입니다. epoll은 준비된 소켓만 콕 집어 알려 주어, 전부를 훑는 비용 자체를 없앱니다.
지금까지 동시성을 다루는 세 가지 도구, 스레드와 fork, select, poll을 배웠습니다. 이제 이것들을 모아 실전 프로그램을 만들 차례입니다. 다음 장에서 여러 사람이 동시에 떠드는 채팅 서버를 select로 완성합니다.