티스토리 뷰
강의 : (인프런)C++과 언리얼로 마드는 MMORPG 게임 개발 시리즈 - 게임서버
데이터를 저장하거나, 패킷으로 전달할 때 정수값이나 char 값은 그대로 저장할 수 있으나 동적으로 할당된 값은 그대로 저장할 수 없습니다. 예를 들어 아래의 구조체를 가진 데이터를 보내거나 저장하려고 할때, target이나 vector의 값을 저장해도 후에 복원시킬 때 제대로 된 값을 복원할 수 없습니다. 따라서 이러한 값을 byte나 배열로 만들어서 보내거나 저장할 수 있도록 하는 것을 직렬화라고 합니다.
class Player {
public:
int32 hp = 0;
int32 attack = 0;
Player* target = nullptr;
vector<int32> buffs;
};
XML vs JSON
javascript에서 따온 형식이며, XML은 가독성이 좋을 수 있지만, 속도나 나머지 부분은 JSON이 좋습니다.
XML 예시
<?xml version="1.0" encoding="utf-8"?>
<PDL>
<Pakcet name ="S_TEST" desc="테스트 용도">
<Field name ="id" type="uint64" desc=""/>
<Field name ="hp" type="uint32" desc=""/>
<Field name ="attack" type="uint16" desc=""/>
<List name="buffs" desc="">
<Field name ="buffId" type="uint64" desc=""/>
<Field name ="remainTime" type="float" desc=""/>
</List>
</Pakcet>
</PDL>
아래는 강의 예시이며, 고정길이데이터를 먼저 받은 후 가변길이 데이터를 처리하는 형태로 패킷을 만들어어 가고 있습니다.
#pragma pack(1)
// [ PKT_S_TEST ][ 가변 길이 데이터 ]
struct PKT_S_TEST
{
struct BuffListItem
{
uint64 buffId;
float remainTime;
};
uint16 packetSize; // 공용 헤더
uint16 pakcetID; // 공용 헤더
uint64 id; // 8
uint32 hp; // 4
uint16 attack; // 2
uint16 buffOffset; // 가변데이터 시작 위치
uint16 buffCount; // 가변데이터 크기
bool Validate()
{
uint32 size = 0;
size += sizeof(PKT_S_TEST);
size += buffCount * sizeof(BuffListItem);
if (size != packetSize)
return false;
if (buffOffset + buffCount * sizeof(BuffListItem) > packetSize)
return false;
return true;
}
//vector<BuffData> buffs;
//wstring name;
};
#pragma pack()
클라 패킷 직렬화 .h
더보기
#pragma once
enum {
S_TEST = 1
};
class ClientPacketHandler
{
public:
static void HandlePacket(BYTE* buffer, int32 len);
static void Handle_S_TEST(BYTE* buffer, int32 len);
};
template<typename T, typename C>
class PacketIterator
{
public:
PacketIterator(C& container, uint16 index) : _container(container), _index(index) { }
bool operator!=(const PacketIterator& other) const { return _index != other._index; }
const T& operator*() const { return _container[_index]; }
T& operator*() { return _container[_index]; }
T* operator->() { return &_container[_index]; }
PacketIterator& operator++() { _index++; return *this; }
PacketIterator& operator++(int32) { PacketIterator ret = *this; ++_index; return ret; }
private:
C& _container;
uint16 _index;
};
template<typename T>
class PacketList
{
public:
PacketList() : _data(nullptr), _count(0) { }
PacketList(T* data, uint16 count) : _data(data), _count(count) { }
T& operator[](uint16 index)
{
ASSERT_CRASH(index < _count);
return _data[index];
}
uint16 Count() { return _count; }
// ranged-base for 지원
PacketIterator<T, PacketList<T>> begin() { return PacketIterator<T, PacketList<T>>((*this), 0); }
PacketIterator<T, PacketList<T>> end() { return PacketIterator<T, PacketList<T>>((*this), _count); }
private:
T* _data;
uint16 _count;
};
클라 패킷 직렬화 .cpp
더보기
#include "pch.h"
#include "ClientPacketHandler.h"
#include <BufferReader.h>
void ClientPacketHandler::HandlePacket(BYTE* buffer, int32 len)
{
BufferReader br(buffer, len);
//Echo
PacketHeader header;
br >> header;
switch (header.id)
{
case S_TEST:
Handle_S_TEST(buffer, len);
break;
}
}
#pragma pack(1)
// [ PKT_S_TEST ][ 가변 길이 데이터 ]
struct PKT_S_TEST
{
struct BuffListItem
{
uint64 buffId;
float remainTime;
};
uint16 packetSize; // 공용 헤더
uint16 pakcetID; // 공용 헤더
uint64 id; // 8
uint32 hp; // 4
uint16 attack; // 2
uint16 buffOffset; // 가변데이터 시작 위치
uint16 buffCount; // 가변데이터 크기
bool Validate()
{
uint32 size = 0;
size += sizeof(PKT_S_TEST);
if (packetSize < size)
return false;
size += buffCount * sizeof(BuffListItem);
if (size != packetSize)
return false;
if (buffOffset + buffCount * sizeof(BuffListItem) > packetSize)
return false;
return true;
}
using BuffsList = PacketList<PKT_S_TEST::BuffListItem>;
BuffsList GetBuffList() {
BYTE* data = reinterpret_cast<BYTE*>(this);
data += buffOffset;
return BuffsList(reinterpret_cast<PKT_S_TEST::BuffListItem*>(data), buffCount);
}
//vector<BuffData> buffs;
//wstring name;
};
#pragma pack()
void ClientPacketHandler::Handle_S_TEST(BYTE* buffer, int32 len)
{
BufferReader br(buffer, len);
if (len < sizeof(PKT_S_TEST))
return;
PKT_S_TEST* pkt = reinterpret_cast<PKT_S_TEST*>(buffer);
//PKT_S_TEST pkt;
//br >> pkt;
if (pkt->Validate() == false)
return;
//cout << "ID : " << id << " HP : " << hp << " ATT : " << attack << endl;
//vector<PKT_S_TEST::BuffListItem> buffs;
//buffs.resize(pkt->buffCount);
PKT_S_TEST::BuffsList buffs = pkt->GetBuffList();
cout << "BuffCount : " << buffs.Count() << endl;
for (int32 i = 0; i < buffs.Count(); i++) {
cout << "BuffInfo : " << buffs[i].buffId << " " << buffs[i].remainTime << endl;
}
for(auto it = buffs.begin(); it != buffs.end(); ++ it)
cout << "BuffInfo : " << it->buffId << " " << it->remainTime << endl;
for(auto& buff : buffs)
cout << "BuffInfo : " << buff.buffId << " " << buff.remainTime << endl;
}
저도 공부하면서 와... 하면서 공부했네요 ㅎㅎ
'서버 공부 > 네트워크' 카테고리의 다른 글
비동기 입출력 함수 Ex 시리즈(connect, disconnect, accept) (0) | 2023.05.16 |
---|---|
소켓 상태 지정 함수 - SetSockOpt (0) | 2023.05.15 |
TCP/IP) 채팅, 그림 공유 프로그램 (0) | 2022.07.13 |
TCP/IP) 각 모델의 장단점 (0) | 2022.07.12 |
TCP/IP) Completion Port 모델 (0) | 2022.07.12 |
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday
링크
TAG
- 인제대학교
- 시스템보안
- 지뢰찾기
- 야경
- 레지스터
- 학교
- Dreamhack
- 백준
- 워셜알고리즘
- 알고리즘
- 고양이
- 자료구조
- BFS
- STL
- Select모델
- 개발
- 정보보안
- 드림핵
- 컨퍼런스
- 더블버퍼링
- queue
- 멀티쓰레드
- 보안
- 스레드풀
- c++
- 링크드 리스트
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 | 31 |
글 보관함