한국어
오픈소스포럼
 이곳은 다양한 오픈소스 프로젝트를 소개하고 리뷰, 활용 방법을 공유합니다.
Protocol Buffers는 구조화 된 데이터의 직렬화를위한 언어 중립적, 플랫폼 중립적, 확장 가능한 메커니즘을 제공하며 Google에서 개발 한 BSD 3-Clause 라이선스 오픈 소스 프로젝트이다.
Protobuf에서 사용하는 중립 언어를 사용하면 .proto 파일을 통해 구조화 된 형식으로 메시지를 모델링 할 수 있다.
.proto 파일에 구조화된 데이터를 정의하고 프로토콜 버퍼 컴파일러(protoc)를 통해 .proto 파일을 다양한 언어(C++, Java등)들에서 사용할 수 있는 소스코드(클래스)를 사용하여 데이터 스트림의 자동 인코딩 및 구문 분석을 처리해 주므로 구조화된 데이터를 쉽게 읽고 쓸 수 있다.
생성 된 클래스는 프로토콜 버퍼를 구성하는 필드에 대해 getter 및 setter를 제공하고 프로토콜 버퍼를 하나의 단위로 읽고 쓰는 세부 사항을 처리한다.
 
이 글은 C++언어 및 python으로 conan package manager를 사용하여 protobuf를 사용해 본것을 정리한 것이다.

 

프로토콜 포맷 정의

프로토콜 버퍼 언어의 구문은 proto2 버전과 proto3 버전이 있다. 다음은 구조화된 센서 정보를 정의하는 sensor.proto 파일이다.
syntax = "proto2";
message Sensor {
  required string name = 1;
  required double temperature = 2;
  required int32 humidity = 3;
 
  enum SwitchLevel {
    CLOSED = 0;
    OPEN = 1;
  }
  required SwitchLevel door = 4;
}
 
.proto 파일은 message 정의가 있다. message는 유형이 지정된 필드 집합을 포함하는 구조체와 같다. bool, int32, float, double 및 string을 포함한 많은 표준 데이터 유형을 필드 유형으로 사용할 수 있다. 필드 중 하나에 미리 정의 된 열거형 유형을 정의 할 수도 있다. 예제 에서는 door가 SwitchLevel 유형을 사용하도록 지정한다.
각 요소의 "= 1", "= 2"마커는 필드가 이진 인코딩에서 사용하는 고유 한 "태그"로 사용된다. 태그 번호 1-15는 인코딩하는 데 더 적은 바이트가 필요하므로 최적화를 위해 일반적으로 사용되거나 반복되는 요소에 대해 부여할 수 있으며 자주 사용되지 않는 요소에는 태그 16 이상을 사용한다.
 
각 필드는 다음 예약어 중 하나로 정의해야 한다.
 
optional: 이 필드는 설정되거나 설정되지 않을 수 있다. 선택적 필드 값이 설정되지 않은 경우 기본 값이 사용된다. 간단한 유형의 경우 예제에서 전화 번호 유형에 대해 수행 한 것처럼 고유 한 기본 값을 지정할 수 있다. 그렇지 않으면 시스템 기본 값이 사용된다.(숫자 유형의 경우 0, 문자열의 경우 빈 문자열, 부울의 경우 false이다.) embedded messages 의 경우 기본 값은 항상 해당 필드가 설정되지 않은 메시지의 "기본 인스턴스"또는 "프로토 타입"이다. 접근자를 호출하여 명시 적으로 설정되지 않은 선택적 (또는 필수) 필드의 값을 가져 오면 항상 해당 필드의 기본 값이 반환 된다.
 
repeated: 필드는 여러 번 반복 될 수 있음을 나타낸다 (0 포함). 반복되는 값의 순서는 프로토콜 버퍼에 보존된다. 반복 필드를 동적 크기의 배열이라고 생각하자.
 
required: 필드 값을 제공해야 한다. 그렇지 않으면 메시지가 "초기화되지 않은"것으로 간주된다. libprotobuf 가 디버그 모드에서 컴파일 된 경우 초기화되지 않은 메시지를 직렬화하면 assertion 오류가 발생한다. 최적화 된 빌드에서는 검사를 건너 뛰고 메시지가 작성될 수 있지만 초기화되지 않은 메시지를 구문 분석하는 것은 항상 실패다 (parse 메서드에서 false를 반환함으로써). 이 외에 필수 필드는 선택 필드와 똑같이 작동한다.
 
required 필드의 경우 몇 가지 문제로 인하여 Google 에서는 권장하지 않으며 proto2 구문에 정의 된 대부분의 메시지는 optional 및 repeated 만 사용한다. (Proto3는 required 필드를 지원조차 하지 않는다.)

 

프로토콜 버퍼 컴파일

이제 .proto가 있으므로 다음으로 해야 할 일은 메시지를 읽고 쓰는 데 필요한 클래스를 생성하는 것이다. 프로토콜 버퍼 컴파일러 protoc을 실행해야 한다.
 
우선 conan을 통해 필요한 패키지를 설치하려면 conanfile.txt 을 작성해야한다. protobuf 패키지를 정의한다.
[build_requires]
protoc_installer/3.6.1@bincrafters/stable
 
[requires]
protobuf/3.6.1@bincrafters/stable
 
[generators]
cmake
 
conan 패키지 매니저와 cmake를 연동하여 빌드할 수 있으므로 CMakeLists.txt파일에 protoc 컴파일 명령을 실행할 수 있다. 다음 CMakeLists.txt 는 .proto 파일을 컴파일 하도록 하는 방법을 보여준다.
cmake_minimum_required(VERSION 3.1.2)
project(sensor CXX)
 
set(CMAKE_VERBOSE_MAKEFILE ON)
 
include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
conan_basic_setup(TARGETS)
 
find_package(Protobuf REQUIRED)
 
protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS sensor.proto)
protobuf_generate_python(PROTO_PYS sensor.proto)
 
add_executable(${PROJECT_NAME} main.cc ${PROTO_SRCS} ${PROTO_HDRS})
target_link_libraries(${PROJECT_NAME} PUBLIC CONAN_PKG::protobuf)
target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_BINARY_DIR})
set_target_properties(${PROJECT_NAME} PROPERTIES CXX_STANDARD 11)
 
add_custom_target(proto_python ALL DEPENDS ${PROTO_PYS})
 
protobuf_generate_cpp 와 protobuf_generate_python 는 C++와 Python에서 사용할 수 있는 파일을 생성하도록 하는 것이다.

 

직렬화 및 파싱

각 프로토콜 버퍼 클래스에는 프로토콜 버퍼 바이너리 형식을 사용하여 선택한 유형의 메시지를 읽고 쓰는 메서드가 있다. 다음의 매소드들을 포함하고 있다.
bool SerializeToString(string* output) const; : 메시지를 직렬화하고 지정된 문자열에 바이트를 저장한다. 저장된 데이터들은 텍스트가 아니라 바이너리이다. string 클래스를 편리한 컨테이너 용도로 사용할 뿐이다.
bool ParseFromString(const string& data); : 주어진 string으로부터 메시지를 파싱한다.
bool SerializeToOstream(ostream* output) const; : 주어진 C++ ostream 에 메시지를 쓴다.
bool ParseFromIstream(istream* input); : 주어진 C++ istream에서 메시지를 파싱한다.
 
다음은 생성된 파일(sensor.pb.h)을 사용하는 C++ 예제 소스코드이다. SerializeToOstream 을 사용하여 데이터를 직렬화하여 파일에 쓰도록 되어있다.
#include <fstream>
#include "sensor.pb.h"

int main() {
    Sensor sensor;
    sensor.set_name("Laboratory");
    sensor.set_temperature(23.4);
    sensor.set_humidity(68);
    sensor.set_door(Sensor_SwitchLevel_OPEN);
    std::ofstream ofs("sensor.data", std::ios_base::out | std::ios_base::binary);
    sensor.SerializeToOstream(&ofs);
}
 
다음의 Python 코드는 직렬화 되어 저장된 파일(sensor.data)을 열어서 필드의 값을 출력하도록한다.
import os
from build.sensor_pb2 import Sensor

if __name__ == "__main__":
    with open("sensor.data", 'rb') as file:
        content = file.read()
        
    print("Retrieve Sensor object from sensor.data")
    sensor = Sensor()
    sensor.ParseFromString(content)
    door = "Open" if sensor.door else "Closed"
    print("Sensor name: {}".format(sensor.name))
    print("Sensor temperature: {}".format(sensor.temperature))
    print("Sensor humidity: {}".format(sensor.humidity))
    print("Sensor door: {}".format(door))
 

빌드 및 실행

mkdir build
cd build
pip install protobuf
conan install .. —build=missing
cmake .. -G"MinGW Makefiles"
cmake --build . --config Release
 
protobuf_example.png

 

예제 소스코드는 다음 위치에서 찾을 수 있다:

https://github.com/conan-io/examples/tree/master/libraries/protobuf/serialization

번호 제목 글쓴이 날짜 조회 수
36 Wayland IVI Extension 간단 리뷰 file makersweb 2019.05.12 2988
35 GDBus 튜토리얼(GDBus tutorial) file makersweb 2019.06.30 11667
34 텔레그램(Telegram) Bot 개발 file makersweb 2019.07.21 6556
33 리눅스에서 SDL2 최신버전 컴파일과 Qt Creator로 개발환경 구성 file makersweb 2019.10.06 3771
32 webOS소개 및 Raspberry Pi 3 에서 실행 file makersweb 2019.10.13 4634
31 도커(docker)설치 및 기본 명령어 makersweb 2019.12.02 1249
30 Pluma(C++ Plug-in Management Framework) 튜토리얼 file makersweb 2019.12.07 14698
29 [SDL2 와 OpenGL]윈도우 생성과 2D그래픽 file makersweb 2020.04.11 3841
28 ZeroMQ의 기본 메세지 패턴들 file makersweb 2020.07.31 9632
27 ZeroMQ 비동기 클라이언트/서버 패턴 file makersweb 2020.08.13 2632
26 Qt와 GStreamer 로 작성한 flac 오디오 재생 예제 file makersweb 2020.09.05 1864
25 가볍고 쉬운 임베디드용 그래픽 라이브러리 - LVGL file makersweb 2020.09.16 4375
24 윈도우에서 안드로이드 flutter 프로그래밍 개발환경 구축(with Visual Studio Code) file makersweb 2020.09.16 1608
23 GENIVI DLT(Diagnostic Log and Trace) 활용 file makersweb 2020.11.19 10631
22 Dear ImGui, 경량의 C++ 용 GUI 및 Widget 라이브러리 file makersweb 2020.11.28 10239
21 라즈베리파이4에서 openFrameworks 예제 실행 file makersweb 2020.12.13 1386
20 Nana, C++용 크로스플랫폼 GUI 라이브러리 file makersweb 2021.01.06 2902
19 라즈베리파이에서 Redis의 Pub/Sub 패턴을 사용하는 Electron 응용프로그램 file makersweb 2021.01.31 1462
» Protocol Buffers 를 이용한 직렬화 with Conan Package Manager file makersweb 2021.02.24 1589
17 CANdevStudio 를 사용하여 CAN 네트워크 시뮬레이션 file makersweb 2021.03.09 2595