Overlapped I/O는 논블록 소켓의 리소스 낭비와 데이터 블록의 복사 연산을 해야 하는 단점을 보완했다. 이제 단순하게 I/O 처리 중인지 완료되었는지만 확인하면 된다.
하지만 게임 서버 입장에서 보면, 아직 갈길은 멀다.
클라이언트가 10,000개가 있다고 가정한다면, 10,000개 이하의 I/O 상태를 확인하는 Overlapped 구조체에 I/O 상태를 조회할 것이다. 논블록 소켓보다 리소스가 절약되더라도, 결국 소켓의 개수에 비례하여 루프를 돌기 때문에 이는 리소스 소모로 이어진다. 게임 서버는 항상 수천 혹은 수만명의 사람들이 접속한다는 가정 하에 개발하기 때문에 이런 점을 고려하지 않을 수가 없다. 루프 없이 한 번에 끝내는 방법이 있는데, Epoll과 IOCP가 있다.
사실 이 파트의 꽃은 IOCP인데, 먼저 Epoll부터 다루겠다. Epoll은 소켓이 I/O 작업이 가능한 상태가 되면 사용자에게 알려준다. 거기에 더해 어떤 소켓이 I/O가 가능한지 알려준다. 이러면 앞서 소켓의 개수에 비례해 리소스 소모가 심해지는 문제를 해결할 수 있다!
이전에 논블록 소켓에서 Select
를 사용했었다. 소켓이 데이터를 읽거나 보낼 수 있는 상태를 확인할 수 있지만, 모든 소켓을 조회해야했다. 이제 epoll
을 사용하면 I/O가 가능한 소켓만 조회하므로 리소스 절약이 된다고 볼 수 있다. 이론적으로 보면. 하지만 실제로 소켓의 송신 버퍼가 가득 차있는 경우가 그렇게 많지 않다. 거의 상시로 송신이 가능한 상태를 유지한다.
이 친구는 조금 복잡한 사정이 있다. 위 특징때문에 불필요한 루프를 돌게 되서 여기서 리소스 소모가 발생한다(I/O 대기 상태에 빠지는 상황이 매우 짧은 순간인데 굳이 루프를 돌아야되냐는 것임). 그래서 이 상황을 해결하기 위해 레벨 트리거 대신 에지 트리거를 사용해야 한다고 한다.
레벨 트리거 : 소켓 I/O가 가능함
에지 트리거 : 소켓 I/O가 불가능했는데 가능해졌음!
즉, 에지 트리거는 소켓 I/O가 가능해진 순간에 반환된다. 그러면 이거만 쓰면 되는거 아닌가? 여기서 또 필요한 것이 있다. 소켓이 에지 트리거만 인식하게 등록을 해놨다고 가정해보자. 수신이 가능한 상태에서 데이터를 수신받았다면, 아무 반응이 없다. 왜냐하면 에지트리거는 I/O가 불가능한 상태에서 바뀐 것이 아니기 때문이다. 이러면 데이터를 꺼낼 수가 없는 것이다. 에지트리거를 사용할 때 주의해야 할 점이다.
1. I/O 호출을 한 번만 하지 말고 would block이 발생할 때까지 반복해야 합니다.
2. 소켓은 논블록으로 미리 설정되어 있어야 합니다.
출처 : 게임서버 프로그래밍 교과서(저 : 배현직) P216
게임서버 프로그래밍 교과서(저:배현직),
[C++과 언리얼로 만드는 MMORPG 게임개발 시리즈] Part4: 게임서버(강사 : 루키스)를 학습하고 정리한 내용입니다.