Protobuf의 핵심 특징

  1. 데이터 직렬화: 데이터를 바이트 배열로 변환해서 전송하거나 저장할 수 있습니다.
  2. 언어 및 OS 독립적: 다양한 프로그래밍 언어(C++, Python, Java 등)와 OS에서 사용 가능하며 상호 호환됩니다.
  3. 성능 최적화: 가벼운 포맷으로, JSON이나 XML보다 빠르고 용량이 작습니다.
  4. 명확한 인터페이스: .proto 파일을 이용해 데이터를 정의하고 코드 생성기로 해당 언어의 소스 파일을 자동으로 생성합니다.

Protobuf를 프로젝트에 적용하는 과정

  1. Protobuf 설치 및 설정

    • protoc.exe 도구를 다운로드합니다.
    • .proto 파일을 만들고 이를 바탕으로 C++ 코드로 변환합니다.
  2. Protocol.proto 파일 작성

    • 데이터를 구조화된 형태로 정의합니다.
    • syntax = "proto3";를 사용해 Protobuf 버전을 지정합니다.
  3. 코드 생성

    • protoc.exe를 사용해 .proto 파일을 바탕으로 C++ 코드(헤더와 소스 파일)를 자동 생성합니다.
  4. 라이브러리 세팅

    • 생성된 파일을 프로젝트에 추가하고 Protobuf 라이브러리를 연결합니다.
  5. 데이터 직렬화/역직렬화

    • SerializeToArrayParseFromArray를 사용해 데이터를 직렬화하거나 역직렬화합니다.

1. Protocol.proto 파일

Protocol.proto 파일은 데이터의 구조를 정의합니다.

syntax = "proto3";
package Protocol;

message BuffData
{
    uint64 buffId = 1;           // 버프 ID
    float remainTime = 2;        // 남은 시간
    repeated uint64 victims = 3; // 가변 데이터: 희생자 목록
}

message S_TEST
{
    uint64 id = 1;               // ID
    uint32 hp = 2;               // 체력
    uint32 attack = 3;           // 공격력
    repeated BuffData buffs = 4; // 버프 리스트 (가변 데이터)
}

분석

  1. syntax = "proto3": Protobuf 버전 3을 사용함을 명시합니다.
  2. package Protocol: 코드 생성 시, 네임스페이스 역할을 합니다.
  3. message: 데이터를 정의하는 블록입니다.
    • Scalar Types: uint64, float, uint32 등 기본 데이터 타입을 사용합니다.
    • repeated: 리스트나 배열 같은 가변 데이터를 정의합니다.

2. 코드 생성

GenPackets.bat

protoc.exe -I=./ --cpp_out=./ ./Protocol.proto
IF ERRORLEVEL 1 PAUSE

설명

  • protoc.exe: Protobuf 컴파일러입니다.
  • -I=./: Protocol.proto 파일이 있는 경로를 지정합니다.
  • --cpp_out=./: C++ 소스 코드를 출력할 경로를 지정합니다.
  • 실행 결과:
    • Protocol.pb.h: 헤더 파일
    • Protocol.pb.cc: 소스 파일

주의: uint16 타입은 Protobuf에서 지원하지 않으므로, uint32로 대체해야 합니다.


3. Protobuf 라이브러리 빌드

빌드 과정

  1. CMake를 사용해 Protobuf 라이브러리 소스를 빌드합니다.
  2. Debug/Release 버전의 lib 및 dll 파일을 프로젝트에 복사합니다.

4. Protobuf 라이브러리 연결

pch.h 수정:

#ifdef _DEBUG
#pragma comment(lib, "Protobuf\\Debug\\libprotobufd.lib")
#else
#pragma comment(lib, "Protobuf\\Release\\libprotobuf.lib")
#endif

이제 Protobuf 라이브러리가 프로젝트에 연결됩니다.


5. 서버에서 Protobuf 활용하기

데이터 생성 예시 (Server)

#include "Protocol.pb.h" // 생성된 Protobuf 헤더 파일

while (true)
{
    Protocol::S_TEST pkt;
    pkt.set_id(1000);
    pkt.set_hp(100);
    pkt.set_attack(10);

    // BuffData 추가
    {
        Protocol::BuffData* data = pkt.add_buffs();
        data->set_buffid(100);
        data->set_remaintime(1.2f);
        data->add_victims(4000);
    }

    SendBufferRef sendBuffer = ServerPacketHandler::MakeSendBuffer(pkt);
    GSessionManager.Broadcast(sendBuffer);

    this_thread::sleep_for(250ms);
}

분석

  1. set_ 함수: 필드에 값을 설정합니다.
  2. add_ 함수: 리스트에 데이터를 추가합니다.
    • add_buffs()BuffData 객체를 추가합니다.
  3. SerializeToArray: 객체를 바이트 배열로 직렬화합니다.

6. 클라이언트에서 Protobuf 데이터 읽기

ClientPacketHandler::Handle_S_TEST

void ClientPacketHandler::Handle_S_TEST(BYTE* buffer, int32 len)
{
    Protocol::S_TEST pkt;

    // 직렬화된 데이터를 객체로 변환
    ASSERT_CRASH(pkt.ParseFromArray(buffer + sizeof(PacketHeader), len - sizeof(PacketHeader)));

    cout << pkt.id() << " " << pkt.hp() << " " << pkt.attack() << endl;

    cout << "BUFSIZE : " << pkt.buffs_size() << endl;

    for (auto& buf : pkt.buffs())
    {
        cout << "BUFINFO : " << buf.buffid() << " " << buf.remaintime() << endl;
        cout << "VICTIMS : " << buf.victims_size() << endl;
        for (auto& vic : buf.victims())
            cout << vic << " ";
        cout << endl;
    }
}

1. Protobuf 설치 및 다운로드

다운로드한 파일 구성:

  • bin: protoc.exe (Protobuf 컴파일러)
  • src: 소스 코드 파일들
  • cmake: CMake를 사용한 빌드 설정 파일

2. CMake를 사용해 Protobuf 빌드

CMake 설정:

  1. CMake 실행 → 설정:

    • Where is the source code: Protobuf의 cmake 폴더 경로를 지정합니다.
    • Where to build the binaries: 빌드된 솔루션과 파일들을 저장할 경로를 지정합니다.
  2. Configure 버튼 클릭 → Visual Studio 버전 선택.

  3. Generate 버튼 클릭 → 솔루션 파일 생성.


Visual Studio에서 빌드:

  1. CMake 빌드 디렉터리에 생성된 protobuf.sln 파일을 엽니다.
  2. 솔루션 탐색기에서 ALL_BUILD 프로젝트를 선택 → 빌드 수행.
  3. Debug/Release 모드에 따라 각각 빌드합니다.

빌드 결과:

  • protoc.exe: Protobuf 컴파일러 실행 파일
  • Debug: libprotobufd.lib (디버그용 라이브러리)
  • Release: libprotobuf.lib (릴리즈용 라이브러리)

3. Protobuf 컴파일러 사용

.proto 파일 작성:

syntax = "proto3";
package Example;

message SampleMessage {
    uint64 id = 1;
    string name = 2;
    repeated uint32 values = 3;
}

protoc 실행:

protoc.exe를 사용해 .proto 파일을 C++ 코드로 변환합니다.

protoc --cpp_out=. [대상 proto 파일]
  • --cpp_out: C++ 소스 코드 생성.
  • 결과: SampleMessage.pb.hSampleMessage.pb.cc 파일이 생성됩니다.

4. 프로젝트 설정 (라이브러리 및 인클루드 추가)

1. 프로젝트에 라이브러리 추가:

  • 라이브러리 디렉터리 설정:

    • libprotobufd.lib (Debug 모드)
    • libprotobuf.lib (Release 모드)
  • 링커 설정:

    • 링커 → 입력 → 추가 종속성:
      libprotobufd.lib 또는 libprotobuf.lib 추가.

2. Include 디렉터리 설정:

  • protobuf src 경로를 추가합니다.
    • 예: C:\protobuf\src

5. 코드에서 Protobuf 사용하기

C++ 예제 코드:

SampleMessage 생성 및 직렬화:

#include "SampleMessage.pb.h"
#include <iostream>
#include <fstream>

int main() {
    Example::SampleMessage msg;

    // 데이터 설정
    msg.set_id(12345);
    msg.set_name("Test Name");
    msg.add_values(10);
    msg.add_values(20);

    // 직렬화
    std::ofstream output("output.bin", std::ios::binary);
    if (msg.SerializeToOstream(&output)) {
        std::cout << "Data serialized successfully!" << std::endl;
    }

    // 역직렬화
    std::ifstream input("output.bin", std::ios::binary);
    Example::SampleMessage newMsg;
    if (newMsg.ParseFromIstream(&input)) {
        std::cout << "ID: " << newMsg.id() << std::endl;
        std::cout << "Name: " << newMsg.name() << std::endl;
        for (int i = 0; i < newMsg.values_size(); ++i) {
            std::cout << "Value: " << newMsg.values(i) << std::endl;
        }
    }

    return 0;
}

6. 주요 Protobuf 함수

기능함수
데이터 설정set_<필드이름>(값)
리스트 데이터 추가add_<필드이름>(값)
직렬화 (Serialize)SerializeToArray, SerializeToOstream
역직렬화 (Deserialize)ParseFromArray, ParseFromIstream
리스트 크기 반환<필드이름>_size()
리스트 데이터 접근<필드이름>(인덱스)

7. 오류 해결

  1. 런타임 라이브러리 미스매치:

    • C/C++ → 코드 생성 → 런타임 라이브러리를 일치시킵니다.
      • Debug 모드: 다중 스레드 디버그(/MDd)
      • Release 모드: 다중 스레드(/MD)
  2. DLL 파일 누락:

    • libprotobufd.dll 또는 libprotobuf.dll 파일을 실행 파일과 같은 디렉터리에 배치합니다.

profile
李家네_공부방

0개의 댓글