Einführung in Protobuf-c (C-Sprache ⇔ Python)

Zuerst was ich machen wollte

Python <-> Kommunikation zwischen Prozessen zwischen C-Sprachen

Aufgrund verschiedener Umstände habe ich beschlossen, ein Programm in C-Sprache zu schreiben, aber ich dachte, es wäre sehr schwierig, den Schnittstellenteil in demselben C zu schreiben. Deshalb habe ich die Schnittstelle in Python erstellt und die Programme mit IPC usw. verbunden. Ich habe darüber nachgedacht. Zuerst dachte ich, es wäre in Ordnung, die C-Sprachstruktur selbst zu packen / zu entpacken, aber da es einige gibt, die meine Vorgänger gemacht haben, kann ich ** Protocol Buffer ** verwenden? Also habe ich beschlossen, zu recherchieren und es zu versuchen.

Protocol Buffer(protobuf) Die offizielle Protokollpufferunterstützung lautet wie folgt: (Stand 11/11/2018)

proto2

proto3 (+ oben)

proto2 und proto3 sind nicht kompatibel. Qiita hat auch einen Artikel, der die Umrisse von proto2, proto3, protobuf usw. ausführlich erklärt. Bitte lesen Sie ihn.

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

Protobuf-Beispielcode

Proto-Datei generieren

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 bezieht sich auf die oben genannten Fehler.
  optional Errors error_code = 1;
  //Sie können Nachrichten verschachteln.
  optional DataStructs data = 2;
}

Das Obige ist die Beschreibung von proto2. Ich denke, dass sogar Leute, die es zum ersten Mal sehen, es irgendwie verstehen können. Dies ist praktisch, da Sie der .proto-Datei Kommentare hinzufügen können. proto3 ist aufgrund der unterschiedlichen Syntax nicht kompatibel.

protobuf-c

Also wollte ich dieses Mal protobuf in C-Sprache ausführen. Es gibt keine offizielle Unterstützung, aber wenn ich nachschaue, scheint es, dass es ein Drittanbieterprojekt gibt, also werde ich es verwenden. protobuf-c : https://github.com/protobuf-c/protobuf-c

Es scheint, dass protobuf-c proto3 nicht unterstützt, also werde ich es danach mit proto2 versuchen.

Installation von protobuf-c

Wenn Sie denken, Sie müssen kompilieren, scheinen moderne Distributionen Pakete zu haben. Es kann von einem Standard-Repository unter CentOS und Ubuntu installiert werden.

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)

Dieses Mal wird es in der CentOS-Umgebung ausgeführt.

$ 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

Proto-Dateien mit protoc -c kompilieren

Kompilieren Sie die .proto-Datei mit protoc-c, einem Protobuf-Compiler für protobuf-c, und generieren Sie .pb-ch und .pb-cc. Ich werde es von nun an verwenden.

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

Python-> C-Sprache

Dies ist ein Beispiel für das Lesen von Protobuf in C-Sprache

Python Serialize-Skript

Bevor Sie ein Programm zum Deserialisieren in C-Sprache schreiben, bereiten Sie ein Skript für die Serialisierung auf die Standardausgabe in Python vor.

Erstellen Sie zunächst eine Proto-Datei für Python.

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

Ein Skript, das Python-serialisierten Protobuf in der Standardausgabe anzeigt. Dieses Mal sprechen wir über Protobuf-C, daher werde ich die Details weglassen.

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()

#Ausgabe ohne Zeilenvorschubcode
sys.stdout.write(data)

Deserialisierung in C-Sprache

Versuchen Sie in der Sprache c, von der Standardeingabe zu deserialisieren.

deserialize_sample.c


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

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

	//Eingabe im Binärmodus über die Standardeingabe
	fp=freopen(NULL, "rb", stdin);
	len=fread(buffer, sizeof(char), sizeof(buffer), fp);

	//Befolgen Sie die in der Proto-Datei definierten Definitionen
	IpcMessage *message;
	//Die genaue Länge der serialisierten Daten wird beim Auspacken benötigt. NG, wenn es zu kurz oder zu lang ist
	message=ipc_message__unpack(NULL, len, buffer);
	// has_*Überprüfen Sie, ob das optionale Element einen Wert in hat.
	if(message->has_error_code)
		printf("error_code      : %d\n", message->error_code);
	//Verschachtelte Nachrichten werden auch beim Packen dynamisch generiert.
	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);

	//Geben Sie entpackte Objekte frei
	//Es scheint, dass auch verschachtelte Nachrichten freigegeben werden
	ipc_message__free_unpacked(message, NULL);
	close(fp);

	return 0;
}

Kompilieren

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

Lauf

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

Ich konnte die in Python serialisierte Protobuf-Nachricht in C-Sprache erfolgreich abrufen.

C Sprache-> Python

Als nächstes ein Beispiel für das Lesen von Protobuf, das in Python in C-Sprache generiert wurde

Serialisierung in C-Sprache

serialize_sample.c


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

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

	//Eingabe im Binärmodus über die Standardeingabe.
	fp=freopen(NULL, "wb", stdout);

	//Es gibt ein Beispiel für die Verwendung des INIT-Makros, das hier jedoch von malloc dynamisch zugewiesen wird.
	//Nach Malloc__Muss mit init initialisiert werden
	IpcMessage *message;
	message=(IpcMessage *)malloc(sizeof(IpcMessage));
	ipc_message__init(message);
	//Anders als beim Packen sichert init den verschachtelten Nachrichtenbereich nicht.
	//Reservieren Sie Platz für separat verschachtelte Nachrichten und init. Muss initialisiert werden
	message->data=(DataStructs *)malloc(sizeof(DataStructs));
	data_structs__init(message->data);

	// .Das in der Protodatei als optional angegebene Element hat_*Flag to true.
	//Wenn es nicht auf true gesetzt ist, wird es bei der Serialisierung ignoriert
	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;

	//Prozess serialisieren, Größe und Malloc ermitteln&Serialisierungsprozess
	len=ipc_message__get_packed_size(message);
	buffer=malloc(len);
	ipc_message__pack(message, buffer);

	//Ausgabe in Binär- zu Standardausgabe
	fwrite(buffer, sizeof(void), len, fp);

	//Malloced-Bereich freigeben
	free(buffer);
	free(message->data);
	free(message);
	close(fp);

	return 0;
}

Kompilieren

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

Python Deserialize-Skript

Ein Skript, das die vom Standardeingabeprotobuf serialisierte Eingabe deserialisiert und anzeigt.

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))

Lauf

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

Ich konnte die in C serialisierte Protobuf-Nachricht mit Python erfolgreich abrufen.

Nachtrag und Eindruck

Es ist einfach, weil ich nicht viele Informationen über "protobuf-c" auf Japanisch hatte, aber ich habe die Serialisierungsmethode und die Deserialisierungsmethode zusammengefasst. Es ist ein Beispielcode, der sich der Fehlerbehandlung usw. nicht sehr bewusst ist, und in Wirklichkeit handelt es sich bei der Implementierung mit IPC nicht um eine einfache Standardeingabe / -ausgabe. Daher ist es erforderlich, sie mit Socket usw. wieder zusammenzusetzen, aber ich denke, dass Sie das Wesentliche verstehen können. Überlegen···.

Bis jetzt habe ich den Wert von "Protokollpuffer" nicht wirklich verstanden, und um ehrlich zu sein, dachte ich nur, dass ich ihn mit json ausgeben sollte. Wenn es notwendig ist, in einer Form zu definieren, die für Menschen leicht verständlich ist, wie z. B. REST / API, ist es sicher gut, sie in json oder yaml zu definieren, aber was ist mit Datenformaten zwischen verschiedenen Prozessen, verschiedenen Programmiersprachen usw. Ich fand es sehr nützlich als Lösung für das Problem. Wenn es sich um eine einfache Sprache (im Vergleich zu neueren Sprachen) wie C handelt, werden json und yaml nicht standardmäßig unterstützt und die Berechnungsgeschwindigkeit wird nicht so weit wie möglich verschwendet. Ich verstehe es.

Recommended Posts

Einführung in Protobuf-c (C-Sprache ⇔ Python)
Einführung in die Python-Sprache
Eine Einführung in Python für C-Sprachprogrammierer
Einführung in OpenCV (Python) - (2)
Schreiben von Protokollen in eine CSV-Datei (Python, C-Sprache)
Einführung in Python Django (2) Win
Einführung in die serielle Kommunikation [Python]
[Einführung in Python] <Liste> [Bearbeiten: 22.02.2020]
Einführung in Python (Python-Version APG4b)
Eine Einführung in die Python-Programmierung
Einführung in Python For, While
Versuchen Sie, ein Python-Modul in C-Sprache zu erstellen
[Kapitel 5] Einführung in Python mit 100 Klopfen Sprachverarbeitung
[Einführung in Python] Hochgeschwindigkeits-Einführung in Python für vielbeschäftigte C ++ - Programmierer
[Kapitel 3] Einführung in Python mit 100 Klopfen Sprachverarbeitung
[Kapitel 2] Einführung in Python mit 100 Klopfen Sprachverarbeitung
[Kapitel 4] Einführung in Python mit 100 Klopfen Sprachverarbeitung
Rufen Sie C-Sprachfunktionen von Python auf, um mehrdimensionale Arrays auszutauschen
[Einführung in die Udemy Python3 + -Anwendung] 58. Lambda
[Einführung in die Udemy Python3 + -Anwendung] 31. Kommentar
Trainieren! !! Einführung in Python Type (Type Hints)
[Einführung in Python3 Tag 1] Programmierung und Python
[Einführung in Python] <numpy ndarray> [edit: 2020/02/22]
[Einführung in die Udemy Python3 + -Anwendung] 57. Decorator
Einführung in Python Hands On Teil 1
[Einführung in Python3 Tag 13] Kapitel 7 Zeichenfolgen (7.1-7.1.1.1)
[Einführung in Python] So analysieren Sie JSON
[Einführung in die Udemy Python3 + -Anwendung] 56. Abschluss
[Einführung in Python3 Tag 14] Kapitel 7 Zeichenfolgen (7.1.1.1 bis 7.1.1.4)
[Einführung in die Udemy Python3 + -Anwendung] 59. Generator
[Einführung in Python3 Tag 15] Kapitel 7 Zeichenfolgen (7.1.2-7.1.2.2)
[Einführung in Python] Verwenden wir Pandas
So verpacken Sie C in Python
Python, um von einer anderen Sprache zu wechseln
[Einführung in Python] Verwenden wir Pandas
[Einführung in die Udemy Python3 + -Anwendung] Zusammenfassung
Einführung in die Bildanalyse opencv python
[Einführung in Python] Verwenden wir Pandas
Erste Schritte mit Python für Nicht-Ingenieure
Einführung in Python Django (2) Mac Edition
Verwenden Sie eine Skriptsprache für ein komfortables C ++ - Leben - OpenCV-Port Python zu C ++ -
[AWS SAM] Einführung in die Python-Version
[Einführung in Python3 Tag 21] Kapitel 10 System (10.1 bis 10.5)
Rufen Sie die c-Sprache von Python aus auf (python.h)
[Python Tutorial] Eine einfache Einführung in Python
[Einführung in Python] Was ist Python, die derzeit leistungsstärkste Programmiersprache?
Einführung in die in C Language Part 1 Server Edition erlernte Socket-API
[Einführung in Python] Ich habe die Namenskonventionen von C # und Python verglichen.
[Einführung in die Udemy Python3 + -Anwendung] 18. Listenmethode
[Einführung in die Udemy Python3 + -Anwendung] 63. Notation zur Einbeziehung des Generators
[Einführung in die Udemy Python3 + -Anwendung] 28. Kollektiver Typ
[Einführung in Python] Wie verwende ich eine Klasse in Python?
[Einführung in die Udemy Python3 + -Anwendung] 25. Wörterbuchmethode
[Einführung in die Udemy Python3 + -Anwendung] 33. if-Anweisung
Einführung in die diskrete Ereignissimulation mit Python # 1
4-Sprachen-Vergleich des Abschlusses (Python, JavaScript, Java, C ++)
[Einführung in die Udemy Python3 + -Anwendung] 13. Zeichenmethode
[Einführung in Python3, Tag 17] Kapitel 8 Datenziele (8.1-8.2.5)
[Einführung in die Udemy Python3 + -Anwendung] 55. In-Function-Funktionen
[Einführung in die Udemy Python3 + -Anwendung] 48. Funktionsdefinition
[Einführung in Python3, Tag 17] Kapitel 8 Datenziele (8.3-8.3.6.1)