목록Categories (1096)
KoreanFoodie's Study
[C++ 게임 서버] 5-7. Protobuf 핵심 : 1. protobuf 는 구조화된 데이터를 직렬화하는 방식으로, 구글에서 제공하는 툴을 의미하기도 한다. 2. protobuf 를 사용하면 임시 객체에 대한 복사는 발생할 수도 있으나, 직렬화와 역직렬화 과정이 매우 편리해진다는 장점이 있다. 3. protobuf 는 인터페이스가 간단하고 명확하며, 다른 언어 및 OS 를 사용하는 타 서버와의 연동이 쉬워 협업에도 매우 유리하다. 이번 시간에는 구글에서 만든 데이터 직렬화 툴을 지금까지 만든 샘플 프로젝트에 적용해 볼 것이다. 이번에는 결과만 보여주는 것이 아니라, 설치 및 세팅하는 과정을 자세히 기록할 것이니, 추후 세팅에 어려움을 겪을 때 이 글이 도움이 되었으면 좋겠다. 🤗 먼저, protobu..
[C++ 게임 서버] 5-6. 패킷 직렬화 #3 핵심 : 1. 가변 데이터 안에 가변 데이터를 넣는 경우도, 결국 Offset 과 Count 를 이용해 버퍼에 데이터를 기록하는 방식으로 처리한다. 2. 추후에 소개할 ProtoBuf 를 이용하면, 패킷을 만들 때 가변 데이터를 일일히 코드로 넣는 귀찮음을 줄일 수 있다(다만 불필요한 복사가 있을 수는 있음). 저번에는 가변 데이터를 패킷에 넣어 보았는데... 만약 가변 데이터 안에 또 가변 데이터가 있다면 어떻게 해야 할까? 예를 들어, BuffListItem 이라는 구조체에 해당 버프의 대상들 정보(Victims 라고 하자)를 담는다면 어떻게 해야 할까? struct BuffsListItem { uint64 buffId; float remainTime;..
[C++ 게임 서버] 5-5. 패킷 직렬화 #2 핵심 : 1. 가변 데이터를 처리할 수 있는 PacketList 를 만들면, 버퍼를 읽을 때 임시 객체를 생성하지 않고 캐스팅을 통해 데이터를 효과적으로 읽어들일 수 있다. 자, 일단 저번 시간에는 가변 데이터 형식인 buffs 에 대해 패킷 직렬화를 수행했었다. 그런데 코드를 잘 보면, 조금 마음에 들지 않는 부분이 있다. vector buffs; buffs.resize(pkt.buffsCount); for (int32 i = 0; i > buffs[i]; 바로 buffs 데이터를 읽을 때 임시 객체가 생성되는 것인데... 생각해보면 굳이 그럴 필요가 있나 싶다. 🤔 이 부분을 최적화하기 위해, 이번 글에서..
[C++ 게임 서버] 5-4. 패킷 직렬화 #1 핵심 : 1. 패킷 직렬화란, 포인터 및 가변 데이터 정보를 패킷으로 주고 받을 때 이를 안전하게 저장하고 불러올 수 있도록 파싱하는 기법이다. 2. 가변 데이터의 경우, Offset 과 Count 를 이용해 직렬화를 쉽게 진행할 수 있다. 3. 직렬화된 데이터는 xml 또는 json 포맷으로 세이브 & 로드를 하며 관리하게 된다. xml 은 가독성이 좋을 수 있으나 복잡하며, json 은 형태가 간단하며 파싱 속도가 빠르다. 이번 글에서는 패킷 직렬화에 대해 알아보자. 영어로는 사실 Serialization 인데, 직렬화라고 하니 살짝 어색하게 느껴지기는 한다. 😅 언리얼에서도 Serialization 이라는 용어를 사용하는데, 그 때는 데이터를 안전하고..
[C++ 게임 서버] 5-3. Unicode 핵심 : 1. 문자열을 핸들링에는 2가지 요소가 있다. 하나는 Character Set 이고, 다른 하나는 Encoding 이다. 2. Unicode 는 다양한 문자열에 대응할 수 있는 Set 으로, 인코딩 방식에 따라 UTF-8 이나 UTF-16 등으로 나뉘게 된다. 3. MBCS 는 문자의 특징마다 사용하는 바이트가 다른 경우를 의미하며, WBCS 은 모든 문자열이 동일한 바이트를 사용하는 방식이다. 이번 글에서는 누구나 익숙하지만 물어보면 항상 헷갈리는 내용, 유니코드에 대해 다시 짚고 넘어가도록 하겠다. 일단 Unicode 에 대해 제대로 이해하려면 문자열 인코딩의 두 요소, 'Character Set' 과 'Encoding' 을 구분해야 한다. 먼저,..
[C++ 게임 서버] 5-2. PacketHandler 핵심 : 1. 다양한 패킷에 대한 처리를 대응하기 위해서, 패킷별로 enum 을 만들고, 각 enum 별로 패킷을 핸들링하는 함수를 만들어 주면 switch 문으로 한번에 처리할 수 있을 것이다. 2. 가변 데이터를 넣는 경우도 Primitive Type 을 읽는 경우와 거의 비슷하다. 벡터의 크기나 갯수 정보를 넣어주기만 하면 해결된다! 이전 시간에는 Buffer Helper 클래스를 만들어 버퍼를 조금 더 용이하게 읽고 쓸 수 있도록 했다. 하지만 아직 부족하다! 일일히 임의의 값으로 버퍼의 내용을 채워 패킷을 조립하고 분해하는 것보다, 좀 더 정형화된 형태로 패킷을 주고 받을 수 있는 것이 좋다. 그래서 이번 글에서는 PacketHandler ..
[C++ 게임 서버] 5-1. BufferHelper 핵심 : 1. Buffer 를 쉽게 읽고 쓸 수 있게 해 주는 Buffer Helper 클래스를 만들어 보자. 2. '>>' , '>' 를 오버로딩한 부분이다. 현재 버퍼에서 읽을 부분을 적절한 타입으로 캐스팅해 읽어낸 후, _pos 를 이동시키면, 연속해서 '>>' 연산을 수행할 수 있다. 실제 예시는 아래 부분에 DummyClient 클래스를 통해 확인할 수 있을 것이다 😄 BufferReader.cpp BufferReader::BufferReader() { } BufferReader::BufferReader(BYTE* buffer, uint32 size, uint32 pos) : _buffer(buffer), _size(size), _pos(po..
[C++ 게임 서버] 4-10. PacketSession 핵심 : 1. 간단하게 패킷을 만들어 보자. 사실 데이터에 size 와 id(프로토콜) 을 헤더로 붙이면, 그게 기본적인 패킷의 형태다. 2. Session 을 제거하고 넣을 때는 기존 컨테이너의 정합성이 깨지지 않도록 주의하자. 이를 회피하기 위해 클라가 종료될 때, 바로 세션을 제거하지 않고, 대신 Disconnect 이벤트를 등록만 한 다음 다른 쓰레드가 Dispatch 될 때 세션을 제거하게 만들 수도 있다. 저번까지는 버퍼에 우리가 보내려고 하는 문자열을 그냥 담아서 보냈다. 그런데 실제 게임에서는 이 데이터가 무슨 데이터인지에 대한 정보가 필요한데, 흔히 우리는 이것을 패킷이라고 한다. 즉, 해당 정보에 대한 메타 정보(흔히 헤더 부분에..
[C++ 게임 서버] 4-9. SendBuffer Pooling 핵심 : 1. SendBuffer 를 보낼 때마다 메모리 할당을 하지 말고, Memory Pooling 기법을 이용해서 큰 초기에 1 번만 할당을 하는 기법을 사용해 보자. 2. 실제 Session 에서는 SendBufferManager 를 이용해 버퍼를 사용할 준비(Open 함수)를 한다. SendBufferManager -> SendBufferChunk -> SendBuffer 순으로 계층이 잡혀 있다. 3. SendBufferChunkRef 타입의 인자를 각 쓰레드별로 TLS(Thread-Local Space)에 잡아 두면, 각 쓰레드 끼리 Chunk 에 접근하는 데 있어 경합을 벌일 필요가 없어 효율적이다. 또한 Lock-Free 설..
[C++ 게임 서버] 4-8. SendBuffer 핵심 : 1. Send 는 Recv 보다 살짝 복잡할 수 있다. Lock 등으로 동기화 이슈를 잘 해결해 주어야 한다. 2. Session 의 갯수가 많아질 경우, SessionManager 를 이용해 Session 을 관리해 보자. 이번 글에서는 SendBuffer 를 만들어 보자. 이전에 Send 는 Recv 보다 조금 더 복잡할 수도 있다고 얘기한 바 있는데... 그래서 그런지 Send 는 Recv 보다 아주 살짝(?) 복잡할 것이다 😅 먼저, 이전에 Session 에서 만들었던 Send 의 구현을 한 번 보자. void Session::Send(BYTE* buffer, int32 len) { // 생각할 문제 // 1) 버퍼 관리? // 2) se..