728x90
4장. 게임 서버와 클라이언트
4.1 패키지 게임에서 게임서버
- 데디케이티드(decidated server) 렌더링과 사용자 입력 처리를 전혀 받지 않고, 순전히 클라이언트의 연결을 받는 세션을 처리만 하는 프로그램이 따로 들어있는 경우
4.2 서버의 역할
싱글 플레이 게임의 게임루프(game loop)
입력받기 → 게임 로직 처리하기 → 렌더링
4.3 게임 클라이언트와 서버의 상호작용
- 게임 클라이언트가 서버에 데이터, 즉 메시지를 전달하면 서버는 이를 수신하여 메시지 내용에 따라 특정한 행동을 한다. 반대로 서버에서 클라이언트에 메시지를 전달하면 클라이언트는 수신한 메시지 내용에 따라 특정한 행동을 한다. 이렇게 메시지가 오가는 과정을 상호작용(interaction)이라고 한다.
- 게임 클라이언트와 서버의 상호작용은 크게 네 가지로 구별된다.
- 연결
- 요청 - 응답
- 능동적 통보
- 연결 해제
- 연결
- 연결이란 최초로 클라이언트가 서버와 데이터를 주고 받을 준비를 하는 것이다. 클라이언트에서 서버에 연결을 요청하면 서버는 이를 수락하여 클라이언트와 연결을 맺는 것이다.
- 요청 - 응답
- 연결을 마쳤으면 클라이언트는 서버에 메시지를 보내고, 서버는 이를 처리한 후 결과를 응답해준다. 모든 메시징이 요청 - 응답의 형식을 취할 필요는 없다. 클라이언트는 서버에 어떤 상황을 통보하고, 그 통보에 대한 서버 반응을 굳이 받지 않아도 될 때가 있다. 반대로 클라이언트에서 요청을 보낸적도 없는데 서버에서 능동적으로 통보해야 할 때도 있다. 이를 능동적 통보라고 한다.
- 능동적 통보
- 게임 서버는 세션을 하나 이상 가지고 있는 상태 기계(state machine)이다. 그리고 이 세션의 상태는 1인용 게임에서 컴퓨터 안의 세션처럼 시간이 지나면서 변화한다. 이 변화를 클라이언트에 일정시간마다 통보해야 할 때도 있다. 능동적 통보를 쓰는 대표적인 예이다.
4.4 게임 서버가 하는 일
- 여러 사용자와 상호작용
- 클라이언트에서 해킹당하면 안되는 처리
- 플레이어의 상태 보관
- 모든 게임 로직처리를 클라이언트에서 할 수는 없다. 기본적으로 서버에서 모든 게임 플레이 판정을 할 수 있게 만들되, 쾌적한 품질과 타협하기 위해 일부 처리를 게임 클라이언트 쪽에 맡기는 것이 현실적이다.
4.5 게임 서버의 품질
- 게임 서버의 품질을 위해 서버를 개발할 때 목표로 두어야 하는 것
- 안정성(stability)
- 확장성(scalability)
- 성능(performance)
- 관리 편의성
안정성
- 게임 서버가 얼마나 죽지 않는가를 의미한다. 또 게임 서버가 얼마나 오작동을 하지 않는가도 포함된다.
- 게임 안정성을 위해 노력할 것
- 치밀한 개발과 유닛 테스트
- 프로그래밍 결과를 한줄 한줄을 검수하고 코딩 가이드라인을 따르는 것이다. 모든 소스는 알기 쉽게 작성하라는 규정, 모든 함수 호출의 반환값을 반드시 체크하는 루틴이다.
- 그리고 개발 된 프로그램의 각 부분은 반드시 자동화된 자가검증, 즉 유닛 테스트를 만들어야 한다는 의무 규정을 정하고 시행하는 것이다.
- 80 : 20 법칙
- 모든 프로그램의 성능의 80%는 20% 소스 코드에서 나타난다는 파레토 법칙이다. 성능에 지대한 영향을 주는 일부분의 소스코드에서만 프로그램 구조가 복잡해지더라도 성능을 최적화해서 개발하고, 나머지 대부분은 성능보다 유지보수하기 쉬운 단순한 구조로 개발하는 것이다.
- 1인 이상의 코드 리뷰
- 가정하지 말고 검증하라
- 봇 테스트, 더미 클라이언트 테스트
- 서버를 띄우고 대량의 더미 클라이언트를 실행시킨다. 더미 클라이언트는 입력처리와 렌더링 과정이 생략되어 있으며, 미리 프로그래밍된 행동을 반복한다. 이것을 대량으로 서버에 접속 시킨다.
- 치밀한 개발과 유닛 테스트
- 서버가 불안정해지면 다음과 같은 방법으로 극복할 수 있다.
- 서버가 죽더라도 최대한 빨리 다시 살아나게 한다.
- 서버는 죽더라도 최대한 적은 서비스만 죽게한다.
- 서버 오작동에 대해서 기록(로그)을 남길 수 있게 한다.
확장성
- 서버를 얼마나 많이 설치할 수 있느냐, 게임 사용자 측면에서 사용자 수가 늘어나더라도 서비스 품질이 떨어지지 않고 유지되느냐가 곧 확장성을 의미한다.
- 서버 확장성을 올리는 방법은 수직적 확장(scale - up) 과 수평적 확장(scale - out)이 있다.
- 수직적 확장
- 서버 머신의 부품을 업그레이드 혹은 서버 머신안의 CPU, RAM 증설한다.
- 서버 소프트웨어 설계 비용이 낮다.
- 확장 비용이 높다.(기하급수적으로 상승)
- 과부하 지점이 서버 컴퓨터이다.
- 오류 가능성이 낮다.(로컬 프로그래밍 방식으로 작동하므로)
- 로컬 컴퓨터의 CPU와 RAM만 사용하므로 단위 처리 속도가 높다.
- 서버 컴퓨터 한 대의 성능만 사용하므로 처리 가능 총량이 낮다.
- 수평적 확장
- 서버 머신의 개수를 증설한다.
- 서버 소프트웨어 설계 비용이 높다.
- 확장 비용이 낮다(선형적으로 상승)
- 네트워크 장치에서 과부하가 일어난다.
- 여러 머신에 걸쳐 비동기 프로그래밍 방식으로 작동하므로 오류 가능성이 높다.
- 여러 서버 컴퓨터간의 메시징이 오가면서 처리하므로 단위 처리 속도가 낮다.
- 여러 서버 컴퓨터로 부하가 분산되므로 처리 가능 총량이 높다.
성능
- 기본적으로 얼마나 빨리 처리하는지
- 온라인 게임의 처리 성능은 서버뿐만 아니라 네트워크 환경에 따라 결정된다. 서버가 아무리 빠르게 처리하더라도 클라이언트와 서버 사이의 네트워크가 느리면 소용없다. 서버 성능을 높이려면 서버의 단위처리 속도를 높이는 것이 기본이다. 서버는 클라이언트의 요청 메시지를 받으면 최대한 이를 빨리 처리해서 응답 메시지를 전송해 줄 수 있어야 한다.
- 게임 서버의 성능을 높이는 기본적인 원칙은 단위 처리 속도를 높이는 것이다. 서버의 단위 처리 속도를 높이려면, 당연하겠지만 프로그램이 더 빠르게 실행할 수 있게 코드 최적화나 알고리즘 최적화를 하는 것이다.
- 더 빠른 속도로 실행되는 프로그래밍 언어를 쓰는 것도 좋은 방법이다.
- 서버 성능을 높이는 또 다른 방법은 서버의 과부하 영역을 분산하는 것이다. 게임 서버 뿐만 아니라 일반적인 프로그램의 처리 속도를 높이고자 먼저 해보는 방법 중 하나는 코드 프로필링(code profiling)을 이용하는 것이다. 어떤 함수가 처리 시간을 많이 차지하는지 발견한 후 그것에 집중해서 성능을 개선하는 것이다. 함수 A가 처리 시간을 가장 많이 차지하지만 함수 A의 처리 속도를 더 높일 수 있는 방법이 더이상 없고, 그렇다고 함수 A를 실행할 빈도를 낮출 수 있는 방법도 없다면 분산을 할 때이다.
- 플레이어가 느끼는 처리 성능을 높이는 또 다른 방법은 네트워크 프로토콜을 최적화 하는 것이다.
- 네트워크 프로토콜 최적화의 첫 번쨰 방법은 메시지의 양을 줄이는 것이다. 서버는 수신과 송신을 해야하는 메시지 수가 많거나 메시지가 차지하는 총량(바이트 수)이 많으면 처리 부담이 증가한다. 따라서 메시지의 양 자체를 줄여야만 서버 부담이 줄어든다.
- 메시지를 압축하는 방법으로 메시지의 양을 줄일 수도 있다. 일반적으로 메시지를 압축하고 풀기 위해 CPU가 연산하는데 걸리는 시간은 메시지의 양에 비례하므로, 서버가 처리하는데 걸리는 시간은 여타 시간보다 짧다. 큰 데이터에서는 데이터 ZLib를 이용한 압축과 같은 무손실 알고리즘이 잘 동작한다. 작은 데이터 같은 경우 다른 방법의 압축 기법을 써야한다. 그 중 하나가 양자화(Quantization)이다.
- 네트워크 프로토콜 최적화의 또 다른 방법은 메시지 교환 횟수를 줄이는 것이다.
- 서비스 성능을 개선하려면 서버뿐만 아니라 네트워크 전송 시간을 줄이는 것도 매우 효과적이다. 클라이언트와 서버간 데이터를 줄이는 비싸지만 빠른 방법은 고품질 네트워크 회선을 가진 데이터센터에 서버를 설치하는 것이다.
- 서버를 거치지 않고 클라이언트끼리 직접 통신하게 하는 것도 방법 중 하나이다. 클라이언트끼리 직접 메시지를 주고 받는 것을 P2P(Peer - to - Peer) 네트워킹이라고 한다. 클라이언트끼리는 레이턴시가 짧지만, 클라이언트와 서버간 레이턴시가 긴 경우 P2P 네트워킹이 효과적이다.
4.6 플레이어 정보의 저장
- 보통 싱글 플레이 게임의 플레이어 정보는 게임을 구동하는 컴퓨터 자체의 디스크에 저장된다.
- 온라인 게임에서 플레이어 정보는 게임을 구동하는 컴퓨터, 즉 클라이언트에 잘 저장하지 않는다.
- 두 가지의 이유가 있다.
- 해킹에 취약하다. 클라이언트에 저장된 데이터는 사용자가 얼마든지 건드릴 수 있다.
- 같은 사용자가 다른 기기를 사용할 경우 문제가 된다.
- 플레이어 데이터를 디스크 같은 통상적인 파일 시스템에 저장하는 방법도 있지만, 데이터 베이스 소프트웨어를 이용해서 저장할 때가 더 많다. 그 이유는 다음과 같다.
- 데이터 관리와 분석을 빠르게 할 수 있다.
- 강력한 데이터 복원 기능이 있다.
- ‘전부 아니면 전무’로 데이터를 변경할 수 있다. 데이터 베이스는 트랜젝션이라는 기능을 제공하는 데 문제가 생기는 경우 원상복구해서 없던 일로 만들 수 있다.
- 데이터 일관성을 유지시켜준다.
- 데이터 베이스는 처리가 2개 이상 동시에 실행될 때 한 데이터가 동시에 여러 데이터를 액세스 하면서 이상한 결과가 나오는 문제를 막아주는 기능이 있다.
- 장애에 내성이 강하다. 데이터 베이스는 데이터를 기록하기 전에 로그 버퍼라는 별도 파일에 할 일을 미리 기록한 후 이를 실제 데이터 베이스에 적용하면서 로그 버퍼를 지워나간다. 데이터 베이스가 중간에 죽어버린 후 재시작을 하면 로그 버퍼에서 아직 안 한 일을 마저 찾아서 복원한다.
728x90
'책 > 게임 서버 프로그래밍 교과서' 카테고리의 다른 글
게임 서버 프로그래밍 교과서 7장. 데이터베이스 기초 (0) | 2024.04.12 |
---|---|
게임 서버 프로그래밍 교과서 5장. 게임 네트워킹 (0) | 2024.04.10 |
게임 서버 프로그래밍 교과서 3장. 소켓 프로그래밍 (0) | 2024.04.10 |
게임 서버 프로그래밍 2장. 컴퓨터 네트워크 (0) | 2024.04.06 |
게임 서버 프로그래밍 1장. 멀티스레딩 (1) | 2024.04.06 |