Introduction to Protobuf-c (C language ⇔ Python)

First, what I wanted to do

Python <-> Interprocess communication between C languages

Due to various circumstances, I decided to write a program in C language, but I thought it would be very difficult to write the interface part in the same C, so I made the interface in Python and connected the programs with IPC etc. I thought about it. At first, I thought it would be okay to pack / unpack the C language structure on my own, but since there are some that my predecessors made, can I use ** Protocol Buffer **? So I decided to research and try it.

Protocol Buffer(protobuf) Official Protocol buffer support is as follows: (As of 11/11/2018)

proto2

proto3 (+ above)

proto2 and proto3 are not compatible. Qiita also has an article that explains in detail the outline of proto2, proto3, protobuf, etc., so please read it.

「Proto2 vs Proto3」:https://qiita.com/ksato9700/items/0eb025b1e2521c1cab79

Protobuf sample code

Generate proto file

sample.proto


message DataStructs{
  optional uint32 id = 1;
  optional uint32 ip_address = 2;
  optional uint32 port_num = 3;
}

message IpcMessage {
  enum Errors {
    SUCCESS = 200;
    ERROR_BAD_REQUEST = 400;
    ERROR_NOT_FOUND = 404;
    ERROR_SERVER_ERROR = 500;
    ERROR_SERVICE_UNAVAILABLE = 503;
  }
  // error_code refers to the above Errors.
  optional Errors error_code = 1;
  //You can nest messages.
  optional DataStructs data = 2;
}

The above is the description of proto2. I think that even people who see it for the first time can understand it somehow. It is convenient to add comments in the .proto file. proto3 is not compatible due to different syntax.

protobuf-c

So, this time, I wanted to run protobuf in C language. There is no official support, but when I look it up, it seems that there is a third party project, so I will use it. protobuf-c : https://github.com/protobuf-c/protobuf-c

It seems that protobuf-c does not support proto3, so I will try it with proto2 after that.

Install protobuf-c

If you think you have to compile, it seems that modern distributions have packages. It can be installed from standard repositories on both CentOS and Ubuntu.

CentOS-7.5-yum


$ yum search protobuf-c  | grep x86_64
protobuf-c.x86_64 : C bindings for Google's Protocol Buffers
protobuf-c-compiler.x86_64 : Protocol Buffers C compiler
protobuf-c-devel.x86_64 : Protocol Buffers C headers and libraries
...

Ubuntu-18.04-apt


$ apt-cache search protobuf-c | grep '(protobuf-c)'
libprotobuf-c1 - Protocol Buffers C shared library (protobuf-c)
libprotobuf-c-dev - Protocol Buffers C static library and headers (protobuf-c)
libprotobuf-c1-dbg - Protocol Buffers C shared library debug symbols (protobuf-c)
protobuf-c-compiler - Protocol Buffers C compiler (protobuf-c)

This time, it will be carried out in the CentOS environment.

$ cat /etc/redhat-release && uname -r && rpm -aq | egrep '(protobuf)|(gcc)' && python -V
CentOS Linux release 7.5.1804 (Core) 
3.10.0-862.14.4.el7.x86_64
gcc-4.8.5-28.el7_5.1.x86_64
protobuf-2.5.0-8.el7.x86_64
protobuf-c-devel-1.0.2-3.el7.x86_64
protobuf-c-1.0.2-3.el7.x86_64
protobuf-compiler-2.5.0-8.el7.x86_64
libgcc-4.8.5-28.el7_5.1.x86_64
protobuf-python-2.5.0-8.el7.x86_64
protobuf-c-compiler-1.0.2-3.el7.x86_64
Python 2.7.5

Compiling proto files with protoc -c

Compile the .proto file with protoc-c, which is a protobuf compiler for protobuf-c, generate .pb-ch and .pb-cc, I will use it from now on.

$ protoc-c sample.proto --c_out=./ && ls sample.*
sample.pb-c.c  sample.pb-c.h  sample.proto

Python-> C language

It is an example of reading protobuf in C language

Python Serialize script

Before writing a program to deserialize in C language, prepare a script to serialize to standard output in Python.

First, create a Proto file for Python.

$ protoc sample.proto --python_out=. ; ls *.py
sample_pb2.py

A script that displays python serialized protobuf on standard output. This time we are talking about protobuf-c, so I will omit the details.

serialize_sample.py


#!/usr/bin/python
# -*- encoding:utf-8 -*-

import sample_pb2
import sys

message = sample_pb2.IpcMessage()
message.error_code = sample_pb2.IpcMessage.ERROR_NOT_FOUND
message.data.id=123
message.data.ip_address=(192<<24)+(168<<16)+(0<<8)+5
message.data.port_num=5060

data=message.SerializeToString()

#Output so as not to include line feed code
sys.stdout.write(data)

Deserialization in C language

In c language, try deserializing from standard input.

deserialize_sample.c


#include <stdio.h>
#include "sample.pb-c.h"

int main(){
	char buffer[1024];
	int len=0;
	FILE *fp;

	//Input in Binary mode from standard input
	fp=freopen(NULL, "rb", stdin);
	len=fread(buffer, sizeof(char), sizeof(buffer), fp);

	//Follow the definition defined in the Proto file
	IpcMessage *message;
	//The exact length of the serialized data is required when unpacking. NG if it is too short or too long
	message=ipc_message__unpack(NULL, len, buffer);
	// has_*Check if the optional item has a value in.
	if(message->has_error_code)
		printf("error_code      : %d\n", message->error_code);
	//Nested messages are also dynamically generated when packed.
	if(message->data->has_id)
		printf("data.id         : %d\n", message->data->id);
	if(message->data->has_ip_address)
		printf("data.ip_address : %d.%d.%d.%d\n", (message->data->ip_address)>>24 & 0xff,
							  (message->data->ip_address)>>16 & 0xff,
							  (message->data->ip_address)>>8  & 0xff,
							  (message->data->ip_address)     & 0xff);
	if(message->data->has_port_num)
		printf("data.port_num   : %d\n", message->data->port_num);

	//Release unpacked objects
	//It seems that nested messages are also released
	ipc_message__free_unpacked(message, NULL);
	close(fp);

	return 0;
}

Compile

$ gcc -l protobuf-c deserialize_sample.c sample.pb-c.c -o deserialize_sample

Run

$ ./serialize_sample.py | ./deserialize_sample
error_code      : 404
data.id         : 123
data.ip_address : 192.168.0.5
data.port_num   : 5060

I was able to successfully retrieve the protobuf message serialized in Python in C language.

C language-> Python

Next, an example of reading Protobuf generated in C language in Python

Serialization in C language

serialize_sample.c


#include <stdio.h>
#include <stdlib.h>
#include "sample.pb-c.h"

int main(){
	void *buffer;
	int len=0;
	FILE *fp;

	//Input in Binary mode from standard input.
	fp=freopen(NULL, "wb", stdout);

	//There is an example of using the INIT macro, but here it is dynamically allocated by malloc.
	//After malloc__Must be initialized using init
	IpcMessage *message;
	message=(IpcMessage *)malloc(sizeof(IpcMessage));
	ipc_message__init(message);
	//Unlike the time of pack, even if init is done, the nested message area is not secured.
	//Reserve space for separately nested messages and init. Needs initialization
	message->data=(DataStructs *)malloc(sizeof(DataStructs));
	data_structs__init(message->data);

	// .The element specified as optional in the proto file is has_*Flag to true.
	//If it is not set to true, it will be ignored when serializing.
	message->has_error_code=1;
	message->error_code=IPC_MESSAGE__ERRORS__ERROR_SERVICE_UNAVAILABLE;//503
	message->data->has_id=1;
	message->data->id=1192;
	message->data->has_ip_address=1;
	message->data->ip_address=(192<<24)+(168<<16)+(0<<8)+234;
	message->data->has_port_num=1;
	message->data->port_num=8080;

	//Serialize process, get size and malloc&Serialization process
	len=ipc_message__get_packed_size(message);
	buffer=malloc(len);
	ipc_message__pack(message, buffer);

	//Binary output to standard output
	fwrite(buffer, sizeof(void), len, fp);

	//Free malloc area
	free(buffer);
	free(message->data);
	free(message);
	close(fp);

	return 0;
}

Compile

$ gcc -l protobuf-c serialize_sample.c sample.pb-c.c -o serialize_sample

Python Deserialize script

A script that deserializes and displays the input serialized by the standard input protobuf.

deserialize_sample.py


#!/usr/bin/python
# -*- encoding:utf-8 -*-

import sample_pb2
import sys

data = sys.stdin.read()

message = sample_pb2.IpcMessage()
message.ParseFromString(data)

if message.HasField("error_code"):
    print("error_code      : {}".format(message.error_code))
if message.data.HasField("id"):
    print("data.id         : {}".format(message.data.id))
if message.data.HasField("ip_address"):
    print("data.ip_address : {}.{}.{}.{}".format((message.data.ip_address>>24)&0xff,
                                                 (message.data.ip_address>>16)&0xff,
                                                 (message.data.ip_address>> 8)&0xff,
                                                 (message.data.ip_address>> 0)&0xff))
if message.data.HasField("port_num"):
    print("data.port_num   : {}".format(message.data.port_num))

Run

$ ./serialize_sample | ./deserialize_sample.py 
error_code      : 503
data.id         : 1192
data.ip_address : 192.168.0.234
data.port_num   : 8080

I was able to successfully retrieve the protobuf message serialized in C language with Python.

Postscript and impression

It's easy because I didn't have much information about protobuf-c in Japanese, but I've summarized the serialization method and deserialization method. It is a sample code that is not very conscious of error handling etc., and in reality it is not a simple standard input / output when implemented with IPC, so it is necessary to reassemble with socket etc., but I think that you can understand the essence. think···.

Until now, I didn't really understand the value of protocol buffer, and to be honest, I only thought "I should dump it with json". Certainly, if it is necessary to define in a form that is easy for people to understand, such as REST / API, it is good to define it in json or yaml, but what to do with the data format between different processes, different programming languages, etc. I found it very useful as a solution to the problem. Furthermore, if it is a low-level language (compared to recent languages) such as C language, json or yaml is not supported as standard, and its calculation speed is not wasted as much as possible. I understand it.

Recommended Posts

Introduction to Protobuf-c (C language ⇔ Python)
Introduction to Python language
An introduction to Python for C programmers
Introduction to OpenCV (python)-(2)
Writing logs to CSV file (Python, C language)
Introduction to Python Django (2) Win
Introduction to serial communication [Python]
[Introduction to Python] <list> [edit: 2020/02/22]
Introduction to Python (Python version APG4b)
An introduction to Python Programming
Introduction to Python For, While
Try to make a Python module in C language
[Chapter 5] Introduction to Python with 100 knocks of language processing
[Introduction to python] A high-speed introduction to Python for busy C ++ programmers
[Chapter 3] Introduction to Python with 100 knocks of language processing
[Chapter 2] Introduction to Python with 100 knocks of language processing
[Chapter 4] Introduction to Python with 100 knocks of language processing
Call C language functions from Python to exchange multidimensional arrays
[Introduction to Udemy Python 3 + Application] 58. Lambda
[Introduction to Udemy Python 3 + Application] 31. Comments
Practice! !! Introduction to Python (Type Hints)
[Introduction to Python3 Day 1] Programming and Python
[Introduction to Python] <numpy ndarray> [edit: 2020/02/22]
[Introduction to Udemy Python 3 + Application] 57. Decorator
Introduction to Python Hands On Part 1
[Introduction to Python3 Day 13] Chapter 7 Strings (7.1-7.1.1.1)
[Introduction to Python] How to parse JSON
[Introduction to Udemy Python 3 + Application] 56. Closure
[Introduction to Python3 Day 14] Chapter 7 Strings (7.1.1.1 to 7.1.1.4)
[Introduction to Udemy Python3 + Application] 59. Generator
[Introduction to Python3 Day 15] Chapter 7 Strings (7.1.2-7.1.2.2)
[Introduction to Python] Let's use pandas
How to wrap C in Python
Python to switch from another language
[Introduction to Python] Let's use pandas
[Introduction to Udemy Python 3 + Application] Summary
Introduction to image analysis opencv python
[Introduction to Python] Let's use pandas
An introduction to Python for non-engineers
Introduction to Python Django (2) Mac Edition
Use a scripting language for a comfortable C ++ life-OpenCV-Port Python to C ++-
[AWS SAM] Introduction to Python version
[Introduction to Python3 Day 21] Chapter 10 System (10.1 to 10.5)
Call c language from python (python.h)
[Python Tutorial] An Easy Introduction to Python
[Introduction to Python] What is the most powerful programming language now?
Introduction to Socket API Learned in C Language Part 1 Server Edition
[Introduction to Python] I compared the naming conventions of C # and Python.
[Introduction to Udemy Python3 + Application] 18. List methods
[Introduction to Udemy Python3 + Application] 63. Generator comprehension
[Introduction to Udemy Python3 + Application] 28. Collective type
[Introduction to Python] How to use class in Python?
[Introduction to Udemy Python3 + Application] 25. Dictionary-type method
[Introduction to Udemy Python3 + Application] 33. if statement
Introduction to Discrete Event Simulation Using Python # 1
Closure 4 language comparison (Python, JavaScript, Java, C ++)
[Introduction to Udemy Python3 + Application] 13. Character method
[Introduction to Python3, Day 17] Chapter 8 Data Destinations (8.1-8.2.5)
[Introduction to Udemy Python3 + Application] 55. In-function functions
[Introduction to Udemy Python3 + Application] 48. Function definition
[Introduction to Python3, Day 17] Chapter 8 Data Destinations (8.3-8.3.6.1)