한국어
오픈소스포럼
 이곳은 다양한 오픈소스 프로젝트를 소개하고 리뷰, 활용 방법을 공유합니다.
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

번호 제목 글쓴이 날짜 조회 수
35 openFrameworks 한글 폰트 설정 및 출력하기 file makersweb 2023.02.19 198
34 Flutter 위젯의 상태관리에 대해서 file makersweb 2023.04.06 450
33 OTA 오픈소스 프로젝트 makersweb 2022.08.03 471
32 도커(docker)설치 및 기본 명령어 makersweb 2019.12.02 482
31 CopperSpice 에 대해서 (C++ Gui 라이브러리) file makersweb 2022.01.02 537
30 라즈베리파이4에서 openFrameworks 예제 실행 file makersweb 2020.12.13 559
29 Windows에서 Qt Creator + CMake + vcpkg 로 C++ 개발환경 구성 (POCO 라이브러리 DirectoryWatcher 예제) file makersweb 2023.01.14 665
28 라즈베리파이에서 Redis의 Pub/Sub 패턴을 사용하는 Electron 응용프로그램 file makersweb 2021.01.31 681
27 Chromium과 Ozone 층 file makersweb 2022.03.03 728
26 윈도우에서 안드로이드 flutter 프로그래밍 개발환경 구축(with Visual Studio Code) file makersweb 2020.09.16 794
25 ZeroMQ 를 이용한 Qt 응용프로그램 간 통신 file makersweb 2021.08.28 829
24 VSCode 와 Qbs 플러그인으로 C/C++ 개발환경 구성 file makersweb 2021.09.12 845
» Protocol Buffers 를 이용한 직렬화 with Conan Package Manager file makersweb 2021.02.24 860
22 NAppGUI, C언어용 크로스 플랫폼 GUI 라이브러리 file makersweb 2022.10.10 861
21 C++를 위한 Lottie 라이브러리 with SDL2 file makersweb 2021.08.15 962
20 LVGL 을 통해 GUI 구현 시 한글 폰트 추가 file makersweb 2023.02.07 1078
19 [NodeGui] JavaScript로 데스크탑 응용프로그램 작성 file makersweb 2023.02.21 1078
18 Qt와 GStreamer 로 작성한 flac 오디오 재생 예제 file makersweb 2020.09.05 1139
17 Flutter/Dart 와 Qt/QML 비교 file makersweb 2021.11.07 1400
16 CANdevStudio 를 사용하여 CAN 네트워크 시뮬레이션 file makersweb 2021.03.09 1694