루틴이 시작되면 먼저 리스닝 소켓을 만들고 그것을 읽기용으로 체크할 목록에 넣는다. 다음 게임이 끝날 때까지 루프를 도는데, 먼저 Select()를 호출해 readBlockSockets의 소켓 중 하나에 패킷이 수신될 때까지 블로킹 상태에 빠진다. Select() 결과 readableSockets에는 실제 데이터가 수신된 소켓만 남게 된다. 이후 이들 읽을거리가 있는 소켓을 순회하여 데이터를 읽어 들인다.
만일 그중 하나가 최초의 리스닝 소켓이라면, 데이터를 수신했다는 뜻이 아니라 신규 클라이언트가 접속했다는 뜻이다.10 그러면 접속을 Accept()로 받아들이고 새 소켓을 readBlockSockets에 추가한 다음, ProcessNewClient()로 새 클라이언트가 접속하였음을 게임에 통지한다. 소켓이 다른 일반 소켓이라면 수신한 데이터를 Receive()로 읽어 들이고 ProcessDataFromClient()로 게임에 알려준다.
Note ≣
여러 소켓으로 들어오는 데이터를 처리하는 다른 방법도 있지만, 플랫폼마다 구현이 제각각이라 위 방법처럼 범용적으로 사용하기는 어려운 것도 있다. 예를 들면 윈도의 IOCP(I/O Completion ports)가 그것인데, 동시 연결이 수천 개라도 거뜬히 처리할 수 있다. IOCP에 관해선 3.10 더 읽을거리 절을 참고하자.11
10 역주 리스닝 소켓에서 직접 데이터를 읽지는 않지만 select( )에 리스닝 중인 소켓을 읽기용으로 넘기면, 신규 접속이 새로 들어올 때 ‘읽을 것이 있음’으로 취급한다. 그러면 recv( ) 대신 accept( )를 해 주면 되는 것이다.
11 역주 앞서 살펴본 세 방식은 모두 결국엔 폴링(polling) 방식인데, 위의 select( ) 예제에서 수천 개의 연결을 처리한다고 하면, 매 프레임마다 벡터에 수천 개씩 넣어서 폴링하고, 그 결과를 리턴하기 위해 또 수천 개씩 첨삭하는 작업을 반복해야 한다. 이에 반해 IOCP는 비동기식 이벤트 주도형이라는 것이 주된 차이점이다. IOCP는 어떤 소켓에 데이터가 오면 콜백으로 알려주므로, 애당초 데이터가 왔는지 매번 들여다볼 필요가 없다. IOCP와 유사한 것으로 macOS엔 kqueue나 GCD, 리눅스엔 e-poll 등이 있는데, 이렇게 운영체제마다 서로 다른 비동기식 I/O를 플랫폼 독립적으로 감싸주는 라이브러리도 있으니 참고하자.