Bonjour,
J'ai un programme python qu utilise la fonction pack du module struct. Et j'aimerais avoir son équivalent en langage C.
Merci
Version imprimable
Bonjour,
J'ai un programme python qu utilise la fonction pack du module struct. Et j'aimerais avoir son équivalent en langage C.
Merci
Que fait cette fonction ? Quel est le contexte ? Peux-tu décrire le cas d'application ?
Bonjour,
Merci pour ta réponse.
Je ne connais pas trop python, mais elle à l'air d'être utilisé pour "formater" des chaines de caractères.
Par exemple :où l est un entier.Code:x += pack("B",l & 0x7F)
A chaque fois qu'elle est utilisé, c'est pour des chaines de caractères.
Si j'en crois la documentation (2.7 ou 3.6), struct permet la conversion string <-> octets.
Ca ressemble terriblement à sprintf(output_buffer, "format", v1, v2, ...).struct.pack(fmt, v1, v2, ...) Return a string containing the values v1, v2, ... packed according to the given format. The arguments must match the values required by the format exactly
Ok, cette fonction produit une séquence d'octets qui peut être affectée à un enregistrement C défini avec l'arrangement correspondant. Il n'y a pas lieu d'y avoir d'équivalent puisque cette fonction est justement destinée à l'interopérabilité vers C. L'équivalent c'est une struct compatible avec le format décrit.
Je pose donc à nouveau la question :
C'est pour envoyer des requêtes dans un certain protocole à un serveur
Certes mais dans quel cadre, comment ça se passe ? Que cherches-tu à faire en C qui nécessite cette opération ? Où est le programme C ? Où est le script Python ? Comment doivent-ils communiquer ?
Voilà le script python que j'aimerais traduire en C :
Et la fonction getLenOf :Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 TYPE_ENUM = 0 TYPE_STRING = 2 TYPE_BYTES = 2 protocolVersion = 0 source_id = "sender" destination_id = "receiver" server = "10.2.182.26" namespace = "urn:dial-multiscreen-org:service:dial:1" data = "{\"type\":\"GET CONNECTION\",\"origin\":{}}" lnData = getLenOf(data) socket = socket.socket() socket = ssl.wrap_socket(socket) socket.connect((server,1900)) msg = pack(">BBBB%dsBB%dsBB%dsBBB%ds%ds" % (len(source_id),len(destination_id),len(namespace),len(lnData),len(data)),getType(1,TYPE_ENUM),protocolVersion,getType(2,TYPE_STRING),len(source_id),source_id,getType(3,TYPE_STRING),len(destination_id),destination_id,getType(4,TYPE_STRING),len(namespace),namespace,getType(5,TYPE_ENUM),0,getType(6,TYPE_BYTES),lnData,data) msg = pack(">I%ds" % (len(msg)),len(msg),msg) socket.write(msg)
Code:
1
2
3
4
5
6
7
8 def getLenOf(s): x = "" l = len(s) while(l > 0x7F): x += pack("B",l & 0x7F | 0x80) l >>= 7 x += pack("B",l & 0x7F) return x
Il te suffit de mettre tous ces champs dans un enregistrement (struct) et de :
- l'envoyer tel quel si les chaînes sont stockées dans des buffers de taille fixe ;
- écrire une fonction qui le sérialise vers un flux si les chaînes sont de longueur variable.
Mais struct c'est juste un ensemble de variable, non ?
Oui.. et ? C'est justement ce que fait pack : concaténer une séquence de données telle qu'elle serait arrangée en C.
faut juste faire attention au padding, et en général pour ce genre de truc on l'inhibe pour avoir la main sur le formatage des données (pragma ou attributes en gcc/clang).
Je ne suis pas sur d'avoir compris
Le but est de remplir un buffer d'octets dans un format donné. On peut définir une fonction pour les 3 types de pack (B,%ds et I) et les utiliser pour remplir le bufferCode:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 unsigned pack_B( uint8_t* buf , unsigned offset , uint8_t v ) { buf[offset] = v; return offset+1; } unsigned pack_pct_ds( uint8_t* buf , unsigned offset, char const *v ) { size_t lg = strlen( v ); memcpy( buf + offset , v , lg ); return offset + lg; } unsigned pack_I( uint8_t* buf , unsigned offset , uint32_t v ) { buf[offset] = v >> 24; buf[offset + 1] = 0xff & (v >> 16); buf[offset + 2] = 0xff & (v >> 8); buf[offset + 3] = 0xff & v; return offset + 4; } void do_it() { uint8_t buffer[4096]; unsigned offset = 4; // reserver 4 octets pour le pack("I") fait en dernier offset = pack_B( buffer , offset , getType(1, TYPE_ENUM) ); offset = pack_B( buffer , offset , protocolVersion ); offset = pack_B( buffer , offset , getType(2, TYPE_STRING) ); offset = pack_B( buffer , offset , strlen(source_id) ); offset = pack_pct_ds( buffer , offset , source_id ); ... ... offset += pack_I( buffer , 0 , offset ); send( socket, buffer , offset, 0); }
Du coup, j'ai ça :
Bon, ben quand j'envoie au serveur, je ne reçoit rien.Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 uint8_t buffer[4096]; unsigned offset = 4; // reserver 4 octets pour le pack("I") fait en dernier offset = pack_B( buffer , offset , strlen(source_id) ); offset = pack_B( buffer , offset , strlen(destination_id) ); offset = pack_B( buffer , offset , strlen(namespace) ); offset = pack_B( buffer , offset , 1 ); offset = pack_B( buffer , offset , strlen(data) ); offset = pack_B( buffer , offset , getType(1, TYPE_ENUM) ); offset = pack_B( buffer , offset , protocolVersion ); offset = pack_B( buffer , offset , getType(2, TYPE_STRING) ); offset = pack_B( buffer , offset , strlen(source_id) ); offset = pack_pct_ds( buffer , offset , source_id ); offset = pack_B( buffer , offset , getType(3, TYPE_STRING) ); offset = pack_B( buffer , offset , strlen(destination_id) ); offset = pack_pct_ds( buffer , offset , destination_id ); offset = pack_B( buffer , offset , getType(4, TYPE_STRING) ); offset = pack_B( buffer , offset , strlen(namespace) ); offset = pack_pct_ds( buffer , offset , namespace ); offset = pack_B( buffer , offset , getType(5, TYPE_ENUM) ); offset = pack_B( buffer , offset , payloadType ); offset = pack_B( buffer , offset , getType(6, TYPE_ENUM) ); offset = pack_B( buffer , offset , getLenOf(data) ); offset = pack_pct_ds( buffer , offset , namespace ); offset += pack_I( buffer , 0 , offset ); send(sock, buffer , offset, 0);
Le script python avat aussi une socket sécurisé avec socket_wrap.
Est-ce qu'il est nécessaire en C que ma socket soit sécurisé ?
Ta séquence de pack n'est pas bonne, les données passées en % n'ont pas être traitées (elle fournissent la longueurs des %ds dans le format mais ne sont pas dans le buffer). Il doit y avoir exactement le même nombre d'appels que de "B" "%ds" et "I" que dans le format, j'ai indiqué les 5 premiers. Et attention au lnData qui doit être calculé et transmis sous la forme d'une chaîne.
Si le socket était sécurisé, il y a de forte chance que tu nécessites de faire de même.
Mais à quoi correspond %ds ?
Les B c'est pour unsigned char, I pour unsigned int...
Mais là on les copie dans le buffer les %ds, non ?
Code:memcpy( buf + offset , v , lg );
Si tu dois envoyer dans cet ordre un byte, un short (16bit), deux bytes, un int (32bits) alors tu fais une structure :
Tu la remplis, tu l'écris avec un write (attention à l'endianness) et basta.Code:
1
2
3
4
5
6
7 struct foo { unsigned char first_byte ; short first_short; unsigned char second_byte; unsigned char third_byte; int last_int; } __attribute__ (( packed ));
J'ai présenté la syntaxe d'attribut valide avec gcc/clang, mais on peut aussi un pragma.
Je suis pas obligé d'utiliser send pour envoyer un flux de données sur le réseau ?
Donc c'est un peu comme pour écrire dans un fichier en mode binaire, sauf qu'on utilise write au lieu de fwrite, comme le montre cet exemple ?
Code:
1
2
3
4
5
6
7
8
9
10
11
12 typedef struct { int age; char nom[30]; char prenom[30]; char adresse[60]; int nombreFreres; }SPersonne ; SPersonne personne; //Je déclare une variable de type SPersonne fwrite( &personne , sizeof(personne) , 1 , fichier);
Donc on doit bien faire appel à pack_B pour le len et pack_pct_ds pour le char*, non ?
Vous vous rendez compte du problème XY ?
On a au départ un truc certainement écrit en qui utilise des structures. Ensuite on passe en pyhton qui ne propose pas le même type natif mais passe par un module externe pour créer ces structures C. On revient en C ensuite et on essaye d'initer le comportement du module python … et du coup on fait un truc tout compliqué !
C'est pas un peu le serpent qui se tire une balle dans le pied ça ?
Bon, j'ai essayé de faire comme tu as dit piceodev.
Est-ce qu'on obtient ça du coup ?
Puis l'envoie :Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52 typedef struct { unsigned char lensourceid1; unsigned char lendestinationid1; unsigned char lennamespace1; unsigned char lenlndata; unsigned char lendata; unsigned char gettypeenum1; unsigned char protocolVersion; unsigned char gettypestring1; unsigned char len_sourceid2; char *sourceid; unsigned char gettypestring2; unsigned char lendestinationid2; char *destination; unsigned char gettypestring3; unsigned char lennamespace2; char *namespace; unsigned char gettypeenum2; unsigned char payloadtype; unsigned char gettypebytes; unsigned char lndata; char *data; } Request; Request req1; req1.lensourceid1 = strlen(source_id); req1.lendestinationid1 = strlen(destination_id); req1.lennamespace1 = strlen(namespace); req1.lenlndata = 1; req1.lendata = strlen(data); req1.gettypeenum1 = getType(1, TYPE_ENUM); req1.protocolVersion = protocolVersion; req1.gettypestring1 = getType(2, TYPE_STRING); req1.len_sourceid2 = strlen(source_id); req1.sourceid = malloc(req1.len_sourceid2); strcpy(req1.sourceid, source_id); req1.gettypestring2 = getType(3, TYPE_STRING); req1.lendestinationid2 = strlen(destination_id); req1.destination = malloc(req1.lendestinationid2); strcpy(req1.destination, destination_id); req1.gettypestring3 = getType(4, TYPE_STRING); req1.lennamespace2 = strlen(namespace); req1.namespace = malloc(req1.lennamespace2); strcpy(req1.namespace, namespace); req1.gettypeenum2 = getType(5, TYPE_ENUM); req1.payloadtype = payloadtype; req1.gettypebytes = getType(6, TYPE_BYTES); req1.lndata = strlen(data); req1.data = malloc(req1.lndata); strcpy(req1.data, data);
Code:write(sock, &req1, sizeof req1);
@AchilleFraise, On te donne deux moyens différents pour atteindre ton objectif. Tu sembles un peu perdu entre les possibilités. Il faut à la fois bien suivre la partie Python et bien comprendre le code C que tu génères.
Pour la méthode par structure, selon moi elle a des pièges (ainsi tu y a mis des pointeurs et en aucun cas ça n'a de sens de transmettre un pointeur à une entité externe), il y des données à longueurs variables dans la trame et cela n'est pas simple à coder en C.
Je t'ai proposé de procéder par écriture dans un buffer (méthode plus génèrale).
Au final le but des deux méthodes est identique, arriver à une zone de mémoire remplie avec les bonnes données au bon format pour la transmettre.
Code:
1
2
3
4
5
6
7
8
9
10
11
12 // Le format ">" indique bigEndian, "B" sont des octets, "%d" spécifie la longueur du "s" msg = pack(">BBBB%dsBB%dsBB%dsBBB%ds%ds" // pour chaque %d du format indique sa longueur (ne correspond à aucune donnée à mettre dans msg!) %(len(source_id),len(destination_id),len(namespace),len(lnData),len(data)), // les données à placer dans la trame(ou la structure) sont indiquées à partir d'ici getType(1,TYPE_ENUM),protocolVersion,getType(2,TYPE_STRING), // correspond à BBB len(source_id),source_id, // correspond à B%ds donc lg sur 1oct et chaîne sur sa lngueur getType(3,TYPE_STRING),len(destination_id),destination_id, // correspond à BB%ds getType(4,TYPE_STRING),len(namespace),namespace, // correspond à BB%ds getType(5,TYPE_ENUM),0,getType(6,TYPE_BYTES), // correspond à BBB lnData, // correspond à %ds, lnData est une chaîne calculée avant data) // correspond à %ds
Donc ca donne ça :
?Code:
1
2
3
4
5
6
7
8 unsigned offset = 4; // reserver 4 octets pour le pack("I") fait en dernier offset = pack_B( buffer , offset , getType(1, TYPE_ENUM) ); offset = pack_B( buffer , offset , protocolVersion ); offset = pack_B( buffer , offset , getType(2, TYPE_STRING) ); offset = pack_B( buffer , offset , strlen(source_id) ); ... send(sock, buffer , offset, 0); }
Personne ?
J'ai 4 octets de trop et le 4 octets n'est pas le bon. Sinon le reste du message va bien
Edit : Pour les 4 octets de trop je pense que c'est à cause du
dans pack_I, car on a déjà compté les 4 dans l'offset de départ. Mais pour le 4e octet du message qui n'est pas le bon ?Code:return offset + 4
Désolé, c'est plutôt le += de la fin que je t'ai indiqué qui doit être enlevé (sinon compte 2 fois ces 4 octets.)
Dac, merci :D
Et pour cette fonction :
J'ai fais ça :Code:
1
2
3
4
5
6
7
8 def getLenOf(s): x = "" l = len(s) while(l > 0x7F): x += pack("B",l & 0x7F | 0x80) l >>= 7 x += pack("B",l & 0x7F) return x
, mais ce ne marche pasCode:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 uint8_t *getLenOfBis(const char *s) { uint8_t *x; int len = strlen(s); int offset = 0; x = malloc(3); x[0] = 0; x[1] = 0; x[2] = 0; while(len > 0x7F) { offset=pack_B(x, offset, len&0x7F|0x80); len >>=7; } offset = pack_B(x, offset, len&0x7F); printf("%s\t%d\n", x, offset); return x; }
Bonjour
Déjà moi le malloc je lui demanderais d'allouer 3 * sizeof uint8_t octets au lieu de seulement 3 octets. Et s'il faut tout remplir ensuite de 0 ben on peut aussi alors se tourner alors vers memset ou faire un pack complet via calloc mais bon, le faire à la main ça marche aussi surtout que j'ai lu une fois dans ce forum une intéressante discussion où l'auteur disait que tout remplir une variable de 0 binaire ne garantit pas la valeur "0" à l'arrivée parce que rien n'oblige "0" à être codé "0x0000").
Mais surtout dans le code Python il y a un x qui est initialisé puis qui est ensuite modifié via le x += .... Or dans le code C, je ne vois rien d'analogue. Ce que renvoie la fonction pack va remplir un "offset" (offset qui est réutilisé à l'itération suivante) mais ne semble pas toucher à "x" (sauf que comme c'est un pointeur il est peut-être directement modifié par la fonction comme le montre l'exemple de dalfab). Mais si c'est cela, alors un autre souci c'est que cette boucle pourrait faire bien plus que 3 itérations tandis que "x" n'est prévu que pour 3 items.
Sinon quand tu dis "ce ne marche pas", tu ne pourrais pas être plus clair ? Par exemple tu affiches "x" et "offset" avant de le retourner. Tu n'aurais pas eu en plus l'idée d'afficher x[0], x[1] et x[2] et nous dire "en plus" si le contenu de tout cet affichage était cohérent avec ce que tu attends ??
Et sinon t'as testé de façon individuelle ta fonction pack ??? (remarque ça rejoint ma demande précédente)...