목록Categories (1104)
KoreanFoodie's Study

[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..

[C++ 게임 서버] 4-7. RecvBuffer 핵심 : 1. RecvBuffer 를 만들 때는, ReadPos / WritePos 와 관련한 처리를 어떻게 할 것인지가 중요하다. 2. 보통 BufferSize 보다 크게 메모리를 할당해, Clean 을 효율적으로 구현하는 방법을 많이 사용한다. 이전에 Recv 는 Send 에 비해 비교적 구조가 간단하다고 말한 적이 있다. 왜냐면... Send 는 여러 군데에서 동시에 호출하고 난리를 치지만, Recv 는 결국 멀티 쓰레드 환경일지라도 동시에 받는 것이 아니라 순차적으로 받게 되어 있으므로(논리상), 상대적으로 얌전(?) 할 수 밖에 없는 것이다 😅 물론, Recv 의 경우에도 RecvBuffer 를 어떻게 만들어야 좋을지에 대해서는 고민이 약간 필요..

[C++ 게임 서버] 4-6. Session #3 핵심 : 1. 소켓을 세션이 닫힐 때마다 Close 하지 않고, 재사용할 수 있음을 알아 두자(DisconnectEx) 2. 세션 클래스를 통신의 얼개를 만들어 두면, 서버-클라이언트 단에서는 앞으로 사실상 로직과 관련된 구현만 집중해도 된다! 이번 글에서는 세션의 구현을 마무리 지으면서, 동시에 DummyClient 에서도 세션을 통한 구현을 적용해 보도록 하겠다. 일단 이전에 Session 에서 구현했던 Disconnect 함수를 다시 보면... void Session::Disconnect(const WCHAR* cause) { if (_connected.exchange(false) == false) return; // TEMP wcout

[C++ 게임 서버] 4-5. Session #2 핵심 : 1. Session 구현에 있어, Send 는 나름 복잡한 구석이 있다. 2. 송신 버퍼를 어떻게 만들 것인지, 전송 시 여러 쓰레드가 Pending 상태로 놓였을 때, 순서가 반드시 보장되어야 하는지 등등을 생각하면서 구현해 보자. 이번 글에서는 이전에 만들었던 Session 관련 클래스에 내용을 덧붙여 보겠다. 조금 더 구체적으로 이야기하자면, Send 하는 부분을 간단하게 구현해 볼 것이다. 😉 그래서 기존의 Session 코드에서 바뀐 부분만 살짝 보면... Session.h /*-------------- Session ---------------*/ class Session : public IocpObject { public: voidP..