말감로그

24.09.04 Unity_C# - 데이터의 직렬화, 클라이언트와 서버 간 동기화 본문

TIL

24.09.04 Unity_C# - 데이터의 직렬화, 클라이언트와 서버 간 동기화

habbn 2024. 9. 4. 11:51
728x90

 

데이터의 직렬화

메모리를 디스크에 저장하거나 네트워크 통신에 사용하기 위한 형식으로 변환하는 것을 말한다.

역직렬화

반대로 디스크에 저장한 데이터를 읽거나, 네트워크 통신으로 받은 데이터를 메모리에 쓸 수 있도록 다시 변환하는 것을 말한다.

 

직렬화는 왜 필요한가? 

값 형식 데이터(Value Type) 은 우리가 흔히 선언해서 사용하는 int, float, char 등 값 형식 데이터들은 스택에 메모리가 쌓이고 직접 접근이 가능하다.

참조 형식 데이터(Reference Type)은 C#에선 Object 타입 혹은 C++에서 포인터 변수들이 여기에 해당한다. 해당 형식의 변수를 선언하면 힙에 메모리가 할당되고 스택에서는 이 힙 메모리를 참조하는(힙에 메모리 주소를 가지고 있음) 구조로되어있다.

 

이 두 가지 데이터 중에서 디스크에 저장하거나 통신에는 값 형식 데이터만 가능하다.

참조 형식 데이터는 실제 데이터 값이 아닌 힙에 할당되어있는 메모리 번지 주소를 가지고 있기 때문에 저장, 통신에 사용할 수 없다.

 

직렬화를 하게 되면 각 주소 값이 가지는 데이터들을 전부 끌어모아서 값 형식 데이터로 변환해준다.

직렬화가 된 데이터들은 언어에 따라서 텍스트 또는 바이너리 등의 형태가 되는데, 이러한 형태로 되었을 때 저장하거나 통신 시 파싱이 가능한 유의미한 데이터가 되는 것이다.


동기화

 

사용자가 키보드를 이용한 방향키 입력으로 이동 패킷을 서버에 보내주고, 그것을 서버에서는 브로드캐스트로 뿌려주는 가장 기본적이고 간단한 방법이 있지만, 이 방법은 엄청넌 서버 부하를 가져오게 된다.

 

60프레임으로 돌아가는 게임이라고 한다면 1초에 60번이나 이동에 대한 패킷을 보내는 것이 된다.

 

그렇다면 유저의 상태가 기존 상태에서 변화되었을 때만 알리게 된다면 어떨까?

 

기존 상태를 계속 유지하고 있다면 일정한 규칙을 가진 행동을 반복하고 있을테니 패킷을 보내지 않고 같은 처리를 계속 하도록 하고, 상태의 변화가 있을 때만 패킷을 보내여 상태 변화를 알리게 되면 서버는 이를 브로드캐스트하여 다른 클라이언트들도 해당 유저의 상태를 갱신해주는 것이다. 이렇게 된다면 송수신 횟수를 엄청나게 줄일 수 있게 된다.

 

예를 들어 오른쪽 방향키를 입력하여 캐릭터를 이동시키고 있다고 가정합시다.

일정한 이동속도로 캐릭터를 계속 이동시키면서 이를 1초에 60번씩 계속 서버에 알리고 서버는 이를 똑같이 1초에 60번씩 브로드캐스트 하게 된다.

이동 정지에 대한 패킷은 따로 없고 패킷이 가지 않는다면 그게 바로 정지한 것이 될 것이다.

 

다른 방법으로는 오른쪽 방향키를 처음 입력하여 캐릭터를 이동시키기 시작했을 때만 패킷을 보낸다.

서버는 이를 브로드캐스트하여 해당 유저가 이동을 시작하였다고 알린다. 그러면 각 클라이언트는 자체적으로 정해진 이동속도로 캐릭터를 이동시키며 1초에 60번씩 화면을 갱신한다.

 

해당 유저가 방향키에서 손을 떼어서 키가 올라갔다면 캐릭터 이동 정지 패킷을 보낸다.

서버는 이를 브로드캐스트하여 해당 유저가 정지했다고 알린다. 그러면 또 각 클라이언트는 계산을 멈추고 해당 유저를 정지시킬 것이다.

 

이것이 데드레커닝이다.

계속해서 패킷을 받아 위치를 갱신하여야 되지만 처음 한번만 이동 정보를 받아서 패킷이 오지 않는 동안에는 처음에 받은 이동 정보를 이용하여 다음 위치를 추측하여 이동시키는 것이다.

 

여기서 상태 변화가 올 때만 패킷을 다시 보내냐 아니면 일정 주기마다 패킷을 다시 보내냐, 일정주기마다 보내는 것이면 10프레임마다 보내냐 아니면 1초마다 보내냐 이런 것들은 정답이 없기 때문에 각자의 서버 상태나 혹은 게임 종류에 따라 알맞게 정하면 된다.

 

데드레커닝이 송수신 효율에 있어서 좋다는 것은 확실하지만 송수신에 있어서 지연시간이 발생할 수 밖에 없다. 

이동을 요청한 유저가 보낸 패킷이 서버에 도착하기까지 걸리는 시간과 서버가 다른 유저에게 다시 뿌려주는데 걸리는 시간, 그리고 다른 유저가 그 패킷을 받아서 처리하기까지의 지연시간이 있다.

그 지연 시간동안 클라이언트는 기존 계산하던 방식으로 계속 해당 유저의 위치나 상태를 갱신시키고 있기 때문에 도착한 정보와의 오차가 발생하고 이 오차만큼 캐릭터가 워프되거나 하는 문제가 생긴다.

 

이런 경우 최대한 어색하지 않고 자연스럽게 새로운 방향으로 이동시켜야 한다.

 

보간 처리란?

게임에서 보간 처리(interpolation)는 주로 네트워크 지연을 해결하거나 더 매끄러운 애니메이션을 구현하기 위해 사용된다.

 

상태 동기화(State Synchronization)

게임의 전체 상태를 주기적으로 클라이언트와 서버 간에 동기화하는 방식이다.

서버는 주기적으로 각 클라이언트에게 게임 오브젝트의 현재 상태(위치, 회전, 애니메이션 상태 등)를 전송한다. 

 

장점

1. 모든 클라이언트가 항상 동일한 게임 상태를 공유한다.

2. 새로운 클라이언트가 게임에 참여하더라도 현재 상태를 받아오므로 빠르게 동기화할 수 있다.

 

단점

1. 네트워크 트래픽이 많아질 수 있다.

2. 업데이트 주기가 길어질수록 클라이언트 간의 지연(latency)이 발생할 수 있다.

 

데드레커닝 기법

클라이언트에서 마지막으로 알려진 위치와 속도, 방향을 바탕으로 현재 위치를 예측하기 위해 사용하는 방법이다.

데드레커닝은 네트워크 지연(latency)이나 패킷 손실이 발생할 때도 클라이언트에서 객체의 움직임을 부드럽게 보이도록 하기 위해 사용된다.

 

새 위치 = 이전 위치 + (속도 x 시간)

 

interpolation(보간법, 내삽)

 

 

https://hub1234.tistory.com/26

 

 

728x90