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
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.
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
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
Dies ist ein Beispiel für das Lesen von Protobuf in C-Sprache
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)
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
$ ./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.
Als nächstes ein Beispiel für das Lesen von Protobuf, das in Python in C-Sprache generiert wurde
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
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))
$ ./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.
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