개요
이번장에서는 이동 동기화, 서버 성능 개선 방안등의 서버 네트워킹에 필요한 다양한 주제를 다룬다.
이동 동기화
보통 서버와 클라이언트간 이동은 다음과 같이 처리된다.
1. 클라이언트에서 일정간격으로 서버에 자신의 이동정보를 보낸다.
2. 서버에서는 각 클라이언트들의 이동정보를 받을때마다 서버 메모리의 위치 정보들을 갱신한다.
3. 서버에서는 자체적인 타이머로 일정간격마다 모든 클라이언트들에 각각 필요한 다른 클라이언트들의 위치정보를 보내준다.
p2p에서는 다음과 같이 처리한다.
- 각 클라이언트는 일정간격으로 자신의 이동정보를 다른 클라이언트들에 보낸다.
- 각 클라이언트는 다른 클라이언트의 이동정보를 받으면 위치 정보를 갱신한다.
두 방법 중 뭐가 됬든 어떠한 객체들의 실제 위치를 가지고 있는 한 엔트리가 있으며 그 엔트리는 해당 정보를 다른 엔트리에 보내 동기화 한다. 전자 방식의 경우 해당 시점에 실제 위치에 해당하는 정보를 가지고 있는 서버에서 주기적으로 위치 동기화를 각 클라이언트들에 보내며 p2p는 각 클라이언트들마다 일어난다. 여기서 엔트리 a에서 어떠한 객체의 실제 위치(판정 기준)을 가지고 있고 이를 엔트리 b에 동기화 시키려 할 때의 처리를 알아보자. 지금은 엔트리 a를 서버로, 엔트리 b를 어떠한 한 클라이언트로 가정하여 설명하겠다.
서버에서 0.1초마다 객체 a의 위치를 클라이언트에게 전송한다고 하자. 그러면 클라이언트는 0.1초마다 해당 객체의 위치를 갱신할것이다. 여기에는 두가지 문제가 발생한다.
- 객체가 0.1초마다 순간이동한다.
- 메세지를 받아 위치가 갱신된 시점이 가장 동기화가 잘된 시점임에도 레이턴시만큼 늦은 정보로 갱신된 위치이다.
우선 첫번째문제는 다음과 같이 해결한다. 우선 최초 위치를 받으면 해당 위치로 객체를 순간이동시킨다. 그 후 다음 위치정보가 오면 그 위치로 순간이동하는 것이 아닌 기존 위치에서 새로온 위치까지 메세지 간격의 시간동안 이동시킨다. 이러면 매번 메시지가 도착할 때마다 해당 위치로 자연스래 이동하게 되어 자연스러운 움직임이 나온다. 하지만 해당 방식대로면 클라이언트에서 시점 t의 객체의 위치는 서버 기준에서는 메세지 간격 + 레이턴시만큼 이전의 위치에 객체가 존재하게 된다.(위의 2번 문제점과 결합되었다.) 이는 추측항법으로 개선할 수 있다. 추측항법은 다음과 같이 구현한다.
- 위치 정보 대신 위치 + 속도 정보를 보내준다.
- 클라이언트에서 해당 정보를 받으면 해당 위치에서 예상 딜레이시간 (여기서는 메세지 간격 + 레이턴시)만큼 속도로 이동한 위치를 계산한다.
- 해당 위치를 목표점으로하여 기존 방식대로 이동을 구현한다.
이런식으로 이동을 구현하면 순간적인 움직임 변화에 왜곡이 살짝 생길순있지만 직선적인 움직임이 있는 동안에는 효과적으로 동기화가 가능하다. 비슷한 원리로 애니메이션등에도 동기화를 적용시킬 수 있다.
레이턴시 마스킹
이동 동기화 외에도 이동이나 기타 행동의 딜레이를 숨길수있는 여러 기법이 있다. 첫째는 클라이언트에서 계산할 수 있는건 클라이언트에서 계산해서 보여주는 것이다. 대표적인 것이 자신의 캐릭터의 이동은 서버에 이동 메세지를 보내며 클라이언트에서 바로 계산하여 이동 및 렌더링을 하는 행위등이 있다. 이동 구현 방식에 따라 다르겠지만 만약 클라이언트에서 속력등을 이용하여 계산한 뒤의 결과를 서버에 보내주는 꼴이라면 서버에서 반드시 정상 범주의 메세지인지 한번더 확인할 필요가 있다.
다음으로는 우선 조작이 들어갔다는 느낌을 주고 이후 서버의 응답을 보여주는 방식이다. 대표적으로 스타크래프트의 플레이어가 이동 클릭을 하면 우선 캐릭터 더빙이 즉시 나오고 서버의 응답에 따라 이동이 시작하는 방식이 있다. 또 디아블로에서 공격 클릭 즉시 캐릭터 모션이 나오고 서버의 응답에 따라 이펙트가 나오는 방식등도 있다.
많은 캐릭터 처리
mmorpg의 넓은 맵처럼 한 방에 많은 클라이언트가 있다고 해보자. 만약 모든 클라이언트의 정보를 각 클라이언트들에 전부 보내주어야한다면 서버의 부담감이 너무 커질 것이다. 이는 가시 영역 필터링으로 극복 할 수 있다. 각 클라이언트들에게 다른 클라이언트의 정보를 보낼 때 해당 클라이언트에게 보이는 (또는 필요한) 클라이언트들의 정보만 걸러내어 전달하면 된다. 만약 fps게임에서 시야적인 부분만을 고려한다면 절두체 선별같은 느낌으로 보내줘야하는 클라이언트들을 필터링 할 수 있을 것이다. 다만 이러한 필터링 연산량과 이로인한 이득 연산량 사이를 잘 저울질 해야할 것이다. 보통 테이블들로 묶어 최대한 효율적으로 처리하기위한 기법들을 사용한다.
실시간 전략 게임의 동기화
스타크래프트같은 게임을 생각해보자. 스타에는 한 게임에 너무 많은 병력들이 존재한다. 만약 이러한 병력 하나하나의 이동정보를 기존 방식대로 처리한다고하면 연산량이 너무 커질 것이다. 따라서 락스텝 동기화 방식이라는 것을 사용한다. 랍스텝 동기화 방식은 p2p로 각 클라이언트들이 서로 어떤 입력을 했는지에 대한 정보만을 주고받는 방식이다. 이때 각 클라이언트들은 1/60초 수준으로 서로에게 메세지를 보내며 모든 클라이언트들에게 각자의 메세지가 전부 전달되야지 서로의 게임 루프가 진행된다. 즉 각 클라이언트들의 게임 루프자체가 동기화 되는 것이다. 이렇게 하면 시작 상태가 같은 게임 씬에서 매번 각자의 입력에 따라 동일한 처리가 루프로 진행될 것이며 결과적으로 동기화가 이루어진다. 실제 병력의 생산이나 이동등은 각자의 클라이언트들에서 처리하게 되는 것이다. 이때 주의 할것은 이러한 로직에는 부동소수점을 사용하면 안된다. 각 클라이언트의 하드웨어에 따라 부동소수점 연산에 차이가 있을 수 있어 조그마한 왜곡이 계속 발생하고 이것이 중첩되면 동기화가 깨지게 될 수 있기 때문이다.
실제 레이턴시 줄이기
TCP대신 UDP를 사용하면 레이턴시를 높일 수 있다. 유실시 재전송이 아닌 유실을 선택하기 때문이다. 따라서 중간 데이터가 유실되도 비교적 괜찮은 이동 메세지등에는 UDP를 사용하기도 한다. 또한 같은 양의 데이터를 보내더라도 적은 패킷으로 보내는 것이 메세지 총 크기를 줄이는데 도움이 된다.
보안
게임 서버에서는 다음 두가지 관점에서의 보안을 지켜야한다.
1. 일반적인 네트워크 보안
2. 게임 치트 핵 방어 보안
일반적인 네트워크 보안
1번의 경우 서버와 클라이언트가 주고 받는 패킷을 훔쳐보는 것을 막기위한 암호화가 대표적이다. 여기서 비대칭 암호화를 사용한다. 비대칭 암호화는 특정 데이터를 암호화하는데 쓰이는 키와 복호화하는데 쓰이는 키가 다른 암호화를 이야기한다. 즉
데이터 + 암호 키1 => 암호 문
암호문 + 암호키2 => 데이터
이다.
이와 반대로 대칭 암호키는 암호키 1과 2가 같은 암호화이다. 이를 이용하여 다음과 같이 보안을 수행한다. 목표는 클라이언트에서 서버로 데이터를 안전하게 전송하는 것이다.
1. 클라이언트에서 서버에게 데이터 전송을 할것이라고 요청한다
2. 서버는 암호키1, 2를 생성한다.
3. 클라이언트에게 암호키1만 보낸다.
4. 클라이언트는 사용할 대칭 암호키를 만들고 암호키 1로 비대칭 암호화하여 서버에 보낸다.
5. 서버는 이를 받아 암호키 2로 해석한다. 클라이언트에서 생성한 대칭 암호키를 습득했다.
6. 클라이언트는 대칭 암호키로 데이터를 암호화 하여 서버에 보낸다.
7. 서버는 얻은 대칭 암호키로 데이터를 해석한다.
이렇게 하면 중간에 해커가 패킷을 봐도 암호키2는 전혀 주고받지 않았기에 해석을 할 수 없다.
이때 대칭 암호키는 대칭 키, 세션 키
암호키 1은 공개 키
암호키 2는 개인 키
라고 한다.
이외의 다른 일반적인 네트워크 공격에는 DDOS등이 있다. 이는 하드웨어 방화벽등으로 극복한다.
추가로 클라이언트 컴퓨터나 서버 하드웨어 자체를 감염시키려는 시도도 있다. 클라이언트 컴퓨터 자체가 해킹 당했다면 클라이언트에 입력되는 데이터를 메모리에서 직접 볼수 있기 때문에 치명적이다. 서버 하드웨어 자체가 감염 당했다면 모든 클라이언트 정보를 볼 수 있기에 굉장히 위험한 상황이다. 두 상황 모두 하드웨어 자체가 감염되지않도록 수상한 프로그램을 다운받아 실행하거나 하는 행위는 해서는 안된다.
게임 치트 핵 방어 보안
게임에서 발생하는 보안 문제이다. 보통 성능상의 이유로 특정 판정을 클라이언트에게 맡겨 놓고 이에 대한 검사를 서버에서 수행하지 않는 빈틈을 공략하여 정상적이지 않은 판정으로 치트플레이를 하는 악성 클라이언트가 대표적이다. 이를 막기 위해서는 서버에서 받는 메세지에 대해 빈틈없는 유효검사를 만드는 것이 최선이다. 추가로 클라이언트 프로그램에 관여하는 다른 프로세스를 감지하거나 조작을 검사하는 검사 프로세스를 추가적으로 운영한는 것도 한가지 방법이다.
참고서적
게임 서버 프로그래밍 교과서
'네트워크 > 게임 서버 프로그래밍' 카테고리의 다른 글
[게임 서버][7장]데이터베이스 (0) | 2023.03.21 |
---|---|
[게임 서버][6장]프라우드넷 (0) | 2023.03.21 |
[게임 서버][4장]게임 서버와 클라이언트 (0) | 2023.03.21 |
[게임 서버][3장]소켓 프로그래밍 (0) | 2023.03.21 |
[게임 서버][2장]컴퓨터 네트워크 (0) | 2023.03.21 |