Google Protocol Buffers 기본 사용법 Programming

Protocol Buffers는 구글에서 내놓은 오픈소스 직렬화 라이브러리 입니다. 메세지(혹은 구조체)를 연속된 비트로 만들고, 반대로 비트에서 원래의 메세지로 만들수있습니다. 게임에서는 패킷 전송시에 유용하게 사용할 수 있습니다. C++, java, python을 지원하는데, C++에서 사용하는 방법만을 소개하도록 하겠습니다. 2.3.0 버젼을 기준으로 합니다.

일단 다운로드 받아서 모두 컴파일 합니다.
http://code.google.com/p/protobuf/

메세지를 정의 합니다.
package tutorial;

message Person {
  required string name = 1;
  required int32 id = 2;
  optional string email = 3;

  enum PhoneType {
    MOBILE = 0;
    HOME = 1;
    WORK = 2;
  }

  message PhoneNumber {
    required string number = 1;
    optional PhoneType type = 2 [default = HOME];
  }

  repeated PhoneNumber phone = 4;
}
이것을 .proto 파일로 저장하고 protoc에 인자로 넣어주면 이것을 기반으로 C++코드를 생성해 줍니다.
protoc.exe 파일은 protobuf-2.3.0/vsprojects/Debug 혹은 Release에 들어있습니다.

아래는 배치파일 입니다.
set DST_DIR=D:\Work\ProtoBufExample
set SRC_DIR=D:\Work\ProtoBufExample
protoc -I=%SRC_DIR% --cpp_out=%DST_DIR% %SRC_DIR%/addressbook.proto
이렇게 하면 addressbook.pb.h/cc 파일이 생성됩니다.

다음은 생성된 코드를 이용하여 직렬화, 역직렬화를 수행하는 코드입니다.
#include <iostream>
#include <fstream>
#include <string>
#include "addressbook.pb.h"
#include <google/protobuf/io/zero_copy_stream_impl_lite.h>

using namespace std;
using namespace google;

int main(int argc, char* argv[])
{
// Message 객체에 값 세팅
tutorial::Person src_person;
src_person.set_id(41);
src_person.set_name("alice");
src_person.set_email("alice@anydomain.com");
tutorial::Person::PhoneNumber* phone0 = src_person.add_phone();
phone0->set_number("123-0101");
phone0->set_type(tutorial::Person_PhoneType_MOBILE);
tutorial::Person::PhoneNumber* phone1 = src_person.add_phone();
phone1->set_number("456-0202");
phone1->set_type(tutorial::Person_PhoneType_HOME);

// 미리 생성해야 하는 버퍼의 길이를 알아내어 버퍼할당
int bufSize = src_person.ByteSize();
char* outputBuf = new char[bufSize];

// 버퍼에 직렬화
protobuf::io::ArrayOutputStream os(outputBuf, bufSize);
src_person.SerializeToZeroCopyStream(&os);

// 버퍼에서 역직렬화
protobuf::io::ArrayInputStream is(outputBuf, bufSize);
tutorial::Person dst_person0;
dst_person0.ParseFromZeroCopyStream(&is);

// Message 객체에서 값 가져오기
string name = dst_person0.name();
int id = dst_person0.id();
for (int i=0;i<dst_person0.phone_size();++i)
{
const tutorial::Person_PhoneNumber& phone = dst_person0.phone(i);
tutorial::Person_PhoneType phone_type = phone.type();
string phone_number = phone.number();
}

// 파일에 직렬화
const char* test_filename = "person.txt";
fstream ofs(test_filename, ios::out | ios::trunc | ios::binary);
src_person.SerializeToOstream(&ofs);
ofs.close();

// 파일에서 역직렬화
fstream ifs(test_filename, ios::in | ios::binary);
tutorial::Person dst_person1;
dst_person1.ParseFromIstream(&ifs);
ifs.close();

// 메모리 해제
delete [] outputBuf;
outputBuf = NULL;
protobuf::ShutdownProtobufLibrary();
return 0;
}
문자열의 길이에 따라 메세지의 사이즈가 달라지는 것은 당연하고, int형 변수의 크기에 따라서도 사이즈가 달라집니다. 예를 들면 위의 예제에서 id값이 41이면 총 메세지 사이즈는 58, 65530 이면 60 입니다.

직렬화된 비트에서 text format의 문자열을 추출해 낼수도 있습니다.
#include <google/protobuf/text_format.h>

...

// 텍스트 형식으로 출력
string textFormatStr;
protobuf::TextFormat::PrintToString(src_person, &textFormatStr);
printf("%s\n", textFormatStr.c_str());

// 문자열에서 parsing
tutorial::Person dst_person2;
protobuf::TextFormat::ParseFromString(textFormatStr, &dst_person2);


출력된 text format의 형태입니다.

유사해 보이지만 JSON도 아니고 YAML도 아닌 자체 포맷으로 출력해줍니다.

저는 직렬화된 비트에서 JSON형태로 뽑아내는 기능을 원했는데, 아무래도 직접 구현해야 할 것 같습니다.
아래 개발자 인터뷰를 보니 Reflection을 사용하면 가능할 것 같기도 합니다.
http://news.oreilly.com/2008/07/interview-google-open-sources.html

java예제도 다루고 있는 참고 링크
http://aploit.egloos.com/5233561

핑백

  • flexible gameserver : Google Protocol Buffers에서 Reflection 사용법 2010-12-03 18:42:20 #

    ... http://javawork.egloos.com/2720889</a> PB에서 Reflection을 사용하면 정의된 필드/값에 하나씩 접근할수 있습니다. 이런 방식으로 PB의 소스를 수정하지 않고 여러 기능을 추가할수 있습니다. 예를 들면 제가 하려고 하는, 직렬화된 PB 버퍼를 JSON 형식으로 변환하기, 같은 기능이죠. 아래 코드는 PB에서 제공하는 text format과 같은 형식의 문자열을 출력하는 예제입니다. .proto 파일은 < ... more

  • flexible gameserver : Protocol Buffers를 패킷으로 활용해 보자 2010-12-14 15:07:39 #

    ... 1; required float y = 2; } repeated Position track = 2; } 이전 포스트 참조해서 h/cc파일을 생성합니다. 아래는 생성된 파일을 이용한 소스 파일 입니다. #include &lt;string&gt;#include &lt;goog ... more

  • 수까락의 프로그래밍 이야기 : Google Protocol Buffer Documentation 2013-10-04 18:15:29 #

    ... C++ API3) Java 생성 코드4) Java API5) Python 생성 코드6) Python API 3. 국내 블로그 1) 프로토콜 버퍼 특징 소개 2) 프로토콜 버퍼 시작3) 프로토콜 버퍼 JSON via reflection4) 프로토콜 버퍼 스트리밍 처리 (header + payload) ... more

  • 박피디의 게임 개발 이야기 : Protocol Buffer 로 C++ 과 C# 에서 데이터 읽기 2014-03-09 11:46:09 #

    ... Google Protocol Buffers 기본 사용법 by 자바워크</a>Google Protocol Buffers에서 Reflection 사용법 by 자바워크Protocol Buffers를 패킷으로 활용해 보자 by 자바워크 cpp 에서 프로토콜 버퍼를 사용하려면 다음과 같이 한다.protobuf 프로젝트를 디버그, 릴리즈별로 따로 빌드한다. addressbook.proto 와 protoc.exe 파일이 들어있는 폴더에서 ProtocolGen.b ... more

덧글

  • kay 2011/04/11 16:33 # 삭제 답글

    안녕하세요! 저도 protocol buffers를 사용해보려고 하는 중입니다 ㅜㅜ ..
    아직 학생인지라 배움이 짧고 이해력이 떨어져서 이렇게 염치 불구하고 질문 드립니다 ㅜㅜ..

    현재 구굴 사이트에서 protobuf-2.4.0a.tar와 protoc-2.4.0-win32 이 두가지를 받았습니다 !

    그런데요 ! 전 여기부터 막혀버렸습니다 .. ㅜㅜ 메시지를 정의하라고 하셨는데요 ~ 그 메시지는 뭘로 작성하는 것인지요 ㅜㅜ ?? 뭐 visual에서 해야하는 건지.. 메모장으로 작성해서 확장자 명만 proto로 하면 되는건지 .. ㅜㅜ
    불쌍한 대학생 구제해준다 생각하시고 쉬운 설명 부탁드립니다 ㅜㅜ !!! 부탁드려요 .. ㅜ
  • 자바워크 2011/04/11 19:15 #

    protoc-2.4.0-win32.zip 안에 protoc.exe 이 있는데, 이게 .proto 파일을 해석해서 소스파일(.h와 .cc)로 만들어줍니다.

    아래 링크에 나오는 package tutorial;... 로 시작하는 예제를 addressbook.proto 로 저장하세요.
    http://code.google.com/intl/ko-KR/apis/protocolbuffers/docs/cpptutorial.html
    메모장에서 하셔도 되고 Visual Studio에서 저장하셔도 됩니다.

    protoc.exe 와 같은 폴더에 두시고 아래와 같은 내용의 배치파일(예를 들면 run.bat)을 작성하세요.
    protoc --cpp_out=./ addressbook.proto

    위 배치파일을 실행하면 addressbook.pb.h , addressbook.pb.cc 두개의 소스 파일이 생성됩니다.
    이 파일을 본인의 프로젝트에 포함하셔서 사용하시면 됩니다.
  • kay 2011/04/15 09:12 # 삭제 답글

    와 ㅜㅜ 정말 감사합니다 !!! ㅜㅜ 바로 해결이 됐네요 !! 진짜 너무너무 감사합니다 ^^
  • kay 2011/04/15 15:11 # 삭제 답글

    아저기요 ! 딱 한가지만 더 여쭤보겠습니다 ㅜㅜ !!
    protocol buffers를 sql을 대신해서 사용하려고 하는데용 ! 고럼 혹시 쿼리문 처럼 사용가능한가요 ~ ?

    그러니까 위에 예제로 보면요 ! Person을 배열이나 리스트로 만들었다고 가정하면요 ~
    Person의 id에 조건을 걸어 읽어온다거나 하는 것말이죠 ! id가 4이상인 것 받기 ! 이렇게요 !!

    알고 계시다면 답변 부탁드려용 ^0^
  • 자바워크 2011/04/17 01:03 #

    SQL처럼 조건을 주어서 딱 원하는 레코드만 읽어오는 기능은 없습니다.
    배열이나 벡터처럼 전체를 순회해서 찾아야 합니다(Reflection).
  • kay 2011/04/18 09:25 # 삭제 답글

    네넹 ! 답변 감사합니다 ^^
  • jakejang 2012/06/30 13:31 # 삭제 답글

    잘보고 갑니다!
  • 횬송이 2016/05/17 09:34 # 삭제 답글

    안녕하세요 제가 google protocol buffer를 배우면서 이 글을 읽게 되었습니다.
    일단 다운로드 받아서 모두 컴파일 하라는 주소로 들어가서 보았는데 어떤 것을 다운로드 받아야할지 막막해서
    댓글을 달아봅니다. 알려주세요. beta3 master 등등의 메뉴가 있는데 그것을 설정한후 zip파일을 받으면되는지요..?
    저는 c++을 사용하는데 어떤 것을 받아야할지도 모르겠습니다.
  • 자바워크 2016/05/17 10:17 #

    브랜치는 master로 설정한 후, Download Zip 하시면 소스 파일이 Zip 형식으로 받아집니다.
  • 횬송이 2016/05/17 10:39 # 삭제 답글

    빠른 답변 감사합니다!!
    다른 질문을 해도되는지요.
    제가 데이터를 주고 받는 프로그램을 google protocol buffer를 이용해서 작성해야하는데
    개념이 맞는지 모르겠습니다. 원래 protocol buffer를 이용하지 않으면 데이터 전송시 데이터 누락이나 겹침이 일어날수있는데
    그걸 방지하기위해 google이 만든 protocol buffer를 이용한다고 알고있습니다. 이게 맞는지요.
    그리고 말씀하신대로 zip파일을 받은 후
    컴파일을 하는데 컴파일 방법과 cpp코드를 얻어 내기위해 proto파일을 작성 후 protoc에 인자를 주라는 것이 무슨 뜻인지 모르겠습니다.
    마지막으로 생성된 코드를 이용해 데이터 송수신을 하는 코드를 짜야하는데 생성된 코드를 이용한다는 것이 데이터 송수신을 하는 코드 내에서 미리 생성한 코드를 불러내어 이용한다는 말씀인지요.

    아는 게 없어서 두서없이 여쭤봤는데 답변부탁드립니다...!
  • 자바워크 2016/05/17 10:59 #

    1. protobuf는 object serialization을 위해 사용합니다. https://en.wikipedia.org/wiki/Serialization
    2. 아래와 같이 커맨드 라인에서 입력하시면 addressbook.pb.h/.pb.cc가 생성됩니다. protoc.exe 이후 문자열들이 인자입니다.
    protoc.exe --cpp_out=./ addressbook.proto
    3. 생성된 addressbook.pb.h/.pb.cc 를 사용하면 버퍼를 생성할 수 있습니다. 그 버퍼를 네트워크로 send/recv하는 코드는 직접 작성하시거나, 다른 네트워크 라이브러리를 사용하셔야 한다는 의미 입니다.
  • 횬송이 2016/05/17 11:06 # 삭제 답글

    감사합니다. 제가 proto파일을 만들고 위치시켜야하는데 말씀해주신 zip을 압축을 풀어서 찾는데

    proto.exe 파일이 없는데 master로 받은후 소스만 받은 것이라면 따로 변환해주는 파일이 포함된 것들은 어디서 받아야하는 건가요.?

    그리고 cpp 폴더는 src폴더를 의미하는것인가요?
  • 자바워크 2016/05/17 11:24 #

    1. 소스 컴파일하면 protoc.exe가 생성됩니다. 혹은 https://developers.google.com/protocol-buffers/docs/downloads#release-packages 여기서 받으셔도 됩니다.
    2. --cpp_out=c:where_your_cpp_file_will_be_generated 작성하신 .proto 파일을 바탕으로 생성될 .pb.h / .pb.cc 파일이 생성될 폴더위치 입니다.
  • 횬송이 2016/05/17 11:13 # 삭제 답글

    정말 감사합니다... 많이 배우고있습니다.
  • 횬송이 2016/05/17 11:57 # 삭제 답글

    자바워크님이 알려주신 사이트에서 받으려 하는데

    Protocol Buffers 2.6.1 full source: protobuf-2.6.1.tar.gz (MD5: f3916ce13b7fcb3072a1fa8cf02b2423)
    Protocol Compiler 2.6.1 binary for windows: protoc-2.6.1-win32.zip (MD5: b057f86ef83835010bb227eb2d82de04)

    제가 리눅스라서 compiler를 받기 보단 소스 컴파일(addressbook.proto를 컴파일) 하는 명령어를 terminal에 입력하려는데 g++ addressbook.proto -o addressbook 이런 형식으로 컴파일 하는 것이 맞는지 알고싶습니다.
    윗 댓글 답변해 주신 것을 보고 protoc.exe --cpp_out=./ addressbook.proto 를 쓰면 protoc.exe가 없어서 그런지 오류가 납니다.

    처음이라 그런지 어려운점이 많네요.. 알려주시면 감사하겠습니다.
  • 횬송이 2016/05/17 13:59 # 삭제 답글

    아 해결했습니다. 리눅스의 경우 터미널에서 sudo apt-get install protobuf-compiler 명령으로 받을 수 있습니다.

    혹시나 저말고 궁금한 다른분이 계실까봐 올려드립니다.

    결국엔 자바워크님이 설명해주신 사이트에서 full source를 받고 compiler는 따로 받으시면됩니다.
  • 쌍쓰 2016/12/15 22:10 # 삭제 답글

    안녕하세요 자바워크님
    제가 addressbook.pb.h , addressbook.pb.cc 는 생성해서
    파일은 소스코드있는곳에 옮겨두고
    #include addressbook.pb.h
    인클루드 추가 한 후에
    tutorial::Person src_person;
    만 했는데 외부기호 참조 못하는 오류가 나네요 ㅠ
    혹시 제가 잘못한게 있을까요, 솔루션에 파일을 추가 하니
    .cc 쪽에서 에러가 많이 나오네요
댓글 입력 영역