Introduction à Protobuf-c (langage C ⇔ Python)

Tout d'abord, ce que je voulais faire

Python <-> Communication interprocessus entre les langages C

En raison de diverses circonstances, j'ai décidé d'écrire un programme en langage C, mais je pensais qu'il serait très difficile d'écrire la partie interface dans le même C, j'ai donc créé l'interface en Python et connecté les programmes avec IPC, etc. J'y ai pensé. Au début, je pensais que ce serait bien d'emballer / décompresser la structure du langage C moi-même, mais comme mes prédécesseurs en ont fait certains, puis-je utiliser ** Protocol Buffer **? J'ai donc décidé de faire des recherches et de l'essayer.

Protocol Buffer(protobuf) La prise en charge officielle du tampon de protocole est la suivante: (Au 11/11/2018)

proto2

proto3 (+ ci-dessus)

proto2 et proto3 ne sont pas compatibles. Qiita a également un article qui explique en détail les grandes lignes de proto2, proto3, protobuf, etc., veuillez donc le lire.

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

Exemple de code Protobuf

Générer un fichier proto

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 fait référence aux erreurs ci-dessus.
  optional Errors error_code = 1;
  //Vous pouvez imbriquer des messages.
  optional DataStructs data = 2;
}

Ce qui précède est la description de proto2. Je pense que même les gens qui le voient pour la première fois peuvent le comprendre d'une manière ou d'une autre. C'est pratique car vous pouvez ajouter des commentaires dans le fichier .proto. proto3 n'est pas compatible en raison de la syntaxe différente.

protobuf-c

Donc, cette fois, je voulais exécuter protobuf en langage C. Il n'y a pas de support officiel, mais quand je le recherche, il semble qu'il y ait un projet tiers, donc je vais l'utiliser. protobuf-c : https://github.com/protobuf-c/protobuf-c

Il semble que protobuf-c ne supporte pas proto3, je vais donc l'essayer avec proto2 après cela.

Installation de protobuf-c

Si vous pensez devoir compiler, il semble que les distributions modernes aient des packages. Il peut être installé à partir d'un référentiel standard sur CentOS et 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)

Cette fois, il sera réalisé dans l'environnement CentOS.

$ 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

Compilation de fichiers proto avec protoc -c

Compilez le fichier .proto avec protoc-c, qui est un compilateur protobuf pour protobuf-c, et générez .pb-ch '' et .pb-cc ''. Je vais l'utiliser à partir de maintenant.

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

Langage Python-> C

Ceci est un exemple de lecture de protobuf en langage C

Script de sérialisation Python

Avant d'écrire un programme pour désérialiser en langage C, préparez un script à sérialiser en sortie standard en Python.

Tout d'abord, créez un fichier Proto pour Python.

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

Un script qui affiche le protobuf sérialisé python sur la sortie standard. Cette fois, nous parlons de protobuf-c, je vais donc omettre les détails.

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

#Sortie sans code de saut de ligne
sys.stdout.write(data)

Désérialisation en langage C

En langage C, essayez de désérialiser à partir de l'entrée standard.

deserialize_sample.c


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

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

	//Entrée en mode binaire à partir de l'entrée standard
	fp=freopen(NULL, "rb", stdin);
	len=fread(buffer, sizeof(char), sizeof(buffer), fp);

	//Suivez les définitions définies dans le fichier Proto
	IpcMessage *message;
	//La longueur exacte des données sérialisées est requise lors du déballage. NG s'il est trop court ou trop long
	message=ipc_message__unpack(NULL, len, buffer);
	// has_*Vérifiez si l'élément facultatif a une valeur dans.
	if(message->has_error_code)
		printf("error_code      : %d\n", message->error_code);
	//Les messages imbriqués sont également générés dynamiquement lorsqu'ils sont compressés.
	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);

	//Libérer les objets déballés
	//Il semble que les messages imbriqués soient également publiés
	ipc_message__free_unpacked(message, NULL);
	close(fp);

	return 0;
}

Compiler

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

Courir

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

J'ai pu récupérer avec succès le message protobuf sérialisé en Python en langage C.

Langage C-> Python

Ensuite, un exemple de lecture de Protobuf généré en langage C en Python

Sérialisation en langage C

serialize_sample.c


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

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

	//Entrée en mode binaire à partir de l'entrée standard.
	fp=freopen(NULL, "wb", stdout);

	//Il existe un exemple d'utilisation de la macro INIT, mais ici, elle est allouée dynamiquement par malloc.
	//Après malloc__Doit être initialisé avec init
	IpcMessage *message;
	message=(IpcMessage *)malloc(sizeof(IpcMessage));
	ipc_message__init(message);
	//Contrairement à la compression, init ne sécurise pas la zone de message imbriquée.
	//Réservez de l'espace pour les messages imbriqués séparément et init. Nécessite une initialisation
	message->data=(DataStructs *)malloc(sizeof(DataStructs));
	data_structs__init(message->data);

	// .L'élément spécifié comme facultatif dans le fichier proto est a_*Drapeau à vrai.
	//S'il n'est pas défini sur true, il sera ignoré lors de la sérialisation
	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;

	//Sérialiser le processus, obtenir la taille et le malloc&Processus de sérialisation
	len=ipc_message__get_packed_size(message);
	buffer=malloc(len);
	ipc_message__pack(message, buffer);

	//Sortie en binaire vers sortie standard
	fwrite(buffer, sizeof(void), len, fp);

	//Libérer la zone mallocée
	free(buffer);
	free(message->data);
	free(message);
	close(fp);

	return 0;
}

Compiler

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

Script de désérialisation Python

Il s'agit d'un script qui désérialise et affiche l'entrée sérialisée par le protobuf d'entrée standard.

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

Courir

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

J'ai réussi à récupérer le message protobuf sérialisé en langage C avec Python.

Post-scriptum et impression

C'est facile car je n'avais pas beaucoup d'informations sur `` protobuf-c '' en japonais, mais j'ai résumé la méthode de sérialisation et la méthode de désérialisation. C'est un exemple de code qui n'est pas très conscient de la gestion des erreurs, etc., et en réalité ce n'est pas une simple entrée / sortie standard lorsqu'il est implémenté avec IPC, il est donc nécessaire de réassembler avec socket etc., mais je pense que vous pouvez comprendre l'essence. pense···.

Jusqu'à présent, je ne comprenais pas vraiment la valeur du `` tampon de protocole '', et pour être honnête, je pensais seulement que je devrais le vider avec json. Certes, s'il est nécessaire de définir sous une forme facile à comprendre, comme REST / API, il est bon de le définir en json ou yaml, mais qu'en est-il du format des données entre différents processus, différents langages de programmation, etc. Je l'ai trouvé très utile comme solution au problème. De plus, s'il s'agit d'un langage de bas niveau (par rapport aux langages récents) comme le langage C, json et yaml ne sont pas pris en charge en standard, et la vitesse de calcul n'est pas gaspillée autant que possible. Je comprends bien.

Recommended Posts

Introduction à Protobuf-c (langage C ⇔ Python)
Introduction au langage Python
Une introduction à Python pour les programmeurs en langage C
Introduction à OpenCV (python) - (2)
Écriture de journaux dans un fichier CSV (Python, langage C)
Introduction à Python Django (2) Win
Introduction à la communication série [Python]
[Introduction à Python] <liste> [modifier le 22/02/2020]
Introduction à Python (version Python APG4b)
Une introduction à la programmation Python
Introduction à Python pour, pendant
Essayez de créer un module Python en langage C
[Chapitre 5] Introduction à Python avec 100 coups de traitement du langage
[Introduction à python] Introduction rapide à Python pour les programmeurs C ++ occupés
[Chapitre 3] Introduction à Python avec 100 coups de traitement du langage
[Chapitre 2] Introduction à Python avec 100 coups de traitement du langage
[Chapitre 4] Introduction à Python avec 100 coups de traitement du langage
Appeler des fonctions du langage C depuis Python pour échanger des tableaux multidimensionnels
[Présentation de l'application Udemy Python3 +] 58. Lambda
[Présentation de l'application Udemy Python3 +] 31. Commentaire
Entraine toi! !! Introduction au type Python (conseils de type)
[Introduction à Python3 Jour 1] Programmation et Python
[Introduction à Python] <numpy ndarray> [modifier le 22/02/2020]
[Présentation de l'application Udemy Python3 +] 57. Décorateur
Introduction à Python Hands On Partie 1
[Introduction à Python3 Jour 13] Chapitre 7 Chaînes de caractères (7.1-7.1.1.1)
[Introduction à Python] Comment analyser JSON
[Présentation de l'application Udemy Python3 +] 56. Clôture
[Introduction à Python3 Jour 14] Chapitre 7 Chaînes de caractères (7.1.1.1 à 7.1.1.4)
[Présentation de l'application Udemy Python3 +] 59. Générateur
[Introduction à Python3 Jour 15] Chapitre 7 Chaînes de caractères (7.1.2-7.1.2.2)
[Introduction à Python] Utilisons les pandas
Comment envelopper C en Python
Python pour passer d'une autre langue
[Introduction à Python] Utilisons les pandas
[Introduction à l'application Udemy Python3 +] Résumé
Introduction à l'analyse d'image opencv python
[Introduction à Python] Utilisons les pandas
Premiers pas avec Python pour les non-ingénieurs
Introduction à Python Django (2) Édition Mac
Utilisez un langage de script pour une vie C ++ confortable-OpenCV-Port Python vers C ++ -
[AWS SAM] Présentation de la version Python
[Introduction à Python3 Day 21] Chapitre 10 Système (10.1 à 10.5)
Appeler le langage C depuis Python (python.h)
[Tutoriel Python] Une introduction facile à Python
[Introduction à Python] Qu'est-ce que Python, le langage de programmation le plus puissant actuellement?
Introduction à l'API Socket apprise en langage C, partie 1, édition serveur
[Introduction à Python] J'ai comparé les conventions de nommage de C # et Python.
[Introduction à Udemy Python3 + Application] 18. Méthode List
[Introduction à Udemy Python3 + Application] 63. Notation d'inclusion du générateur
[Introduction à l'application Udemy Python3 +] 28. Type collectif
[Introduction à Python] Comment utiliser la classe en Python?
[Introduction à Udemy Python3 + Application] 25. Méthode de type dictionnaire
[Introduction à l'application Udemy Python3 +] 33. instruction if
Introduction à la simulation d'événements discrets à l'aide de Python # 1
4 langage de comparaison de fermeture (Python, JavaScript, Java, C ++)
[Introduction à Udemy Python3 + Application] 13. Méthode de caractères
[Introduction à Python3, jour 17] Chapitre 8 Destinations de données (8.1-8.2.5)
[Introduction à l'application Udemy Python3 +] 55. Fonctions intégrées
[Introduction à l'application Udemy Python3 +] 48. Définition des fonctions
[Introduction à Python3, jour 17] Chapitre 8 Destinations de données (8.3-8.3.6.1)