서버 메인 루프에 대해서는 서버에서 수신 및 이벤트 콜백과 16. 서버에서 타이머 루프, RMI, 이벤트 처리하기 에 소개하고 있습니다.
10.1서버에서 수신 및 이벤트 콜백
게임 서버는 모든 CPU를 활용하고, 데이터베이스 접근을 하는 동안에도 다른 클라이언트에 대한 수신 처리를 하기 위해 스레드 풀링(Thread pooling)을 활용합니다. ProudNet 또한 thread pooling 방식으로 작동합니다. 그래서 다음 특징들을 갖고 있습니다.
서버 시작시 별도의 스레드 풀(Thread pool)
을 생성합니다. 생성할 스레드의 갯수를 조절하는 방법은 Proud.CNetServer.Start()를 참고하십시오.
게임 클라이언트는 일정 시간마다 수신 처리를 하겠다는 함수를 호출해야만 그동안 쌓인 수신 메시지를 처리합니다. 하지만 서버는 그러한 호출이 불필요합니다.
서버에서 이벤트나 RMI 수신이 발생하면 Proud.CNetServer가 보유한 스레드 풀에서 콜백됩니다.
같은 클라이언트에 대한 이벤트나 RMI 수신은 동시에 2개 이상의 스레드에서 RMI 수신이 콜백되지 않습니다. 콜백됐다가는 처리 순서를 보장하지 못하는 사태가 벌어지니까요. 반드시 도착한 순서대로 RMI 수신이 콜백됨을 보장합니다. 물론, 서로 다른 클라이언트에 대한 RMI나 이벤트는 동시다발적 콜백이 발생합니다.
모든 스레드가 콜백 루틴이 실행되고 있는 동안 RMI 나 이벤트가 발생해야 하는 경우 당장 콜백이 일어나지 않습니다. 대신, 콜백 루틴이 완료되는 스레드가 있을 때까지 해당 콜백은 대기됩니다.
사용자가 구현한 콜백 루틴의 실행 시간이 긴 경우라 하더라도 네트웍 통신에 장애가 일어나지 않습니다. 따라서 서버 개발자는 이를 위해 별도의 스레드 풀을 자체 구현할 필요가 없습니다.
아래 그림은, 클라이언트 A,B,C가 서버에 수용되어 있으며 A,B,C 각각은 지금 막 RMI 또는 이벤트가 발생하여 Proud.CNetServer내의 queue에서 대기중인 예를 표현하고 있습니다. A1,A2,A3는 클라이언트 A에 대한 이벤트 또는 RMI입니다. B1,B2,B3은 클라이언트 B를 위한 것이고요. 스레드 풀의 스레드는 총 2개입니다.
이때 위 규칙에 따라 다음과 같이 실행됩니다.
A1,A2,A3가 동시에 실행되지는 않습니다. B1,B2,B3 도 마찬가지입니다. C1,C2,C3도요.
A1,A2,A3 중 하나와 B1,B2,B3 중 하나와 C1,C2,C3 중 하나는 동시에 실행될 수 있습니다.
하지만 스레드 풀의 스레드 갯수가 2개 뿐이므로 A,B,C 중 2개가 선별되어 콜백되되 먼저 콜백 루틴이 완료된 스레드가 선별되지 못한 클라이언트의 RMI 혹은 이벤트에 대한 콜백을 수행합니다.
그림 10-1스레드 풀 실행 예
10.2서버에서 타이머 콜백
게임 클라이언트처럼, 게임 서버 또한 일정 시간마다 뭔가를 처리하고자 할 수 있습니다. 이러한 경우 아래와 같은 단순한 루프를 가지는 방법이 있습니다.
while(1) { do_something(); // 월드 천이 연산을 시행 Sleep(1); // 일정 시간 대기 }
Proud.CTimerThread나 Proud.CTimerQueue를 이용해서 위 루프를 별도 스레드에서 하게 만드는 방법도 있습니다.
그러나, 서버의 thread pool에서 직접 사용자가 정의한 타이머 함수가 호출되게 하는 방법을 추천합니다. 예를 들어 서버가 1개의 thread만을 가질 경우 타이머 함수와 이벤트 콜백이 같은 스레드이면 critical section 접근 횟수를 절약할 수도 있기 때문입니다. ProudNet은 이를 위한 기능을 내장하고 있습니다.