socket

7 réponses
Avatar
Matt...
Bonsoir,

Je voudrais transmettre ce genre de structure via une socket :

struct packet
{
unsigned char type;
void *data;
};

data peut contenir diff=C3=A9rents types de donn=C3=A9es (structures ou =
tableau en =

fonction du type).
Faut il que je cr=C3=A9e autant de structure "packet" que j'ai de type =

diff=C3=A9rent ?

Merci pour vos r=C3=A9ponses,

Matt...

-- =

Utilisant le logiciel de courrier r=C3=A9volutionnaire d'Opera : =

http://www.opera.com/mail/

7 réponses

Avatar
Olivier Miakinen
Bonjour,

Le 02/11/2013 20:58, Matt... a écrit :

Je voudrais transmettre ce genre de structure via une socket :

struct packet
{
unsigned char type;
void *data;
};

data peut contenir différents types de données (structures ou tableau en
fonction du type).
Faut il que je crée autant de structure "packet" que j'ai de type
différent ?



Je ne suis pas sûr de comprendre la question, mais je vais essayer de
répondre quand même. :-)

La structure « packet », tu en as défini une, et non seulement tu ne
pourras pas en définir d'autres mais en plus tu n'as à priori pas
besoin de le faire. Ce sont les types de données pointés par le
« void * » qui seront éventuellement multiples et variés.

Et bien sûr, selon le type de données, la fonction de sérialisation
et celle de désérialisation ne feront pas la même chose.

Maintenant, si ma réponse ne t'éclaire pas, essaye de poser plus
clairement la question.

Cordialement,
--
Olivier Miakinen
Avatar
Matt...
Bonjour,

Tout d'abord, merci pour ta réponse...

Le Sun, 03 Nov 2013 00:21:16 +0100, Olivier Miakinen
<om+ a écrit:


La structure « packet », tu en as défini une, et non se ulement tu ne
pourras pas en définir d'autres mais en plus tu n'as à prior i pas
besoin de le faire. Ce sont les types de données pointés par le
« void * » qui seront éventuellement multiples et varià ©s.




C'est bien mon but, pouvoir mettre dans mon champ data, tout et n'import e
quoi.
Le soucis, quand j'envoie via la socket ma structure "packet", je n'envo ie
que le pointeur "data" et non les données (ce qui me semble logique ).

Et bien sûr, selon le type de données, la fonction de sé rialisation
et celle de désérialisation ne feront pas la même chose .



Je pense que c'est à ce niveau que je bloque. Pourrais tu m'en dire plus
la dessus ?

Merci,

Matt...


--
Utilisant le logiciel de courrier révolutionnaire d'Opera :
http://www.opera.com/mail/
Avatar
Olivier Miakinen
Le 03/11/2013 07:31, Matt... a écrit :

La structure « packet », tu en as défini une, et non seulement tu ne
pourras pas en définir d'autres mais en plus tu n'as à priori pas
besoin de le faire. Ce sont les types de données pointés par le
« void * » qui seront éventuellement multiples et variés.



C'est bien mon but, pouvoir mettre dans mon champ data, tout et n'importe
quoi.
Le soucis, quand j'envoie via la socket ma structure "packet", je n'envoie
que le pointeur "data" et non les données (ce qui me semble logique).



Oui.


Et bien sûr, selon le type de données, la fonction de sérialisation
et celle de désérialisation ne feront pas la même chose.



Je pense que c'est à ce niveau que je bloque. Pourrais tu m'en dire plus
la dessus ?



En très gros, tu dois remplacer un code du genre :
write(fd, &thepacket.type, sizeof(thepacket.type));
write(fd, &thepacket.data, sizeof(thepacket.data));

par un code du genre :
write(fd, &thepacket.type, sizeof(thepacket.type));
switch(thepacket.type) {
case STRUCTOFBIDULE:
... code pour écrire du struct of bidule ...
break;
case ARRAYOFMACHIN:
... code pour écrire du array of machin ...
break;
...
}

Mais il y a sans doute mieux, surtout si la machine émettrice n'a pas
forcément la même architecture que la machine réceptrice :
https://www.google.fr/search?q=s%C3%A9rialisation+en+C
Avatar
Matt...
Le Sun, 03 Nov 2013 10:30:58 +0100, Olivier Miakinen
<om+ a écrit:

Mais il y a sans doute mieux, surtout si la machine émettrice n'a pas
forcément la même architecture que la machine réceptric e :



Au début, je n'y avais pas pensé. J'envoyais ma structure et j e récupérais
la structure (directement avec un cast) sur la même machine.

Mais si les types ont des tailles différentes de l'autre coté, ça va moins
bien marcher...

https://www.google.fr/search?q=s%C3%A9rialisation+en+C



Merci, je regarde tout ceci,

Matt...

--
Utilisant le logiciel de courrier révolutionnaire d'Opera :
http://www.opera.com/mail/
Avatar
Antoine Leca
Matt... écrivit :

struct packet
{
unsigned char type;
void *data;
};

data peut contenir différents types de données (structures ou tableau en
fonction du type).



Euh, non. Pas du tout.
« data » contient un pointeur. Les données sont ailleurs, et le pointeur
n'est qu'une adresse --au sens postal-- qui permet de savoir où elles
sont réellement, entre autres possibilités des pointeurs.

Faut il que je crée autant de structure "packet" que j'ai de type
différent ?



Non. Par contre, il faut créer autant de fonctions d'interface (ou
d'exportation) qu'il y a de types différents de données.

Un truc que permet le C pour gérer ce genre de cas, est de définir une
famille de fonctions, genre (TOTALEMENT NON TESTÉ)

int exporte_MACHIN(socket_t s, void *p)
{
MACHIN * m = p;
int résultat = EINVAL;
// ...
// sérialise *m et écrit le résultat dans s
// ...
return résultat;
}

int exporte_STRUCTOFBIDULE(socket_t s, void *p)
{
struct STRUCTOFBIDULE * b = p;
int résultat = EINVAL;
// ...
// sérialise *b et écrit le résultat dans s
// ...
// Évidemment, s'il y a un MACHIN dans *b,
// on peut faire un appel à exporte_MACHIN(s, b->m)
// ...
return résultat;
}

#if 0
// C99 ; suppose que l'encodage des types est compact
// Voir ci-après pour gérer les trous (initialisé à 0)...
int (* tableau_exporte[])(socket_t, void *) = {
[id_MACHIN] = exporte_MACHIN,
[id_STRUCTOFBIDULE] = exporte_STRUCTOFBIDULE,
// ...
};
#else
int exporte_erreur(socket_t s, void *p)
{
/* Insérer pragma pour paramètre non utilisé */
/* Insérer code de débogage pour faire connaître erreur */
return EINVAL;
}

int (* tableau_exporte[])(socket_t, void *) = {
/*0: non utilisé*/ exporte_erreur,
/*id_STRUCTOFBIDULE*/ exporte_STRUCTOFBIDULE,
/*id_MACHIN */ exporte_MACHIN,
/* ... */
};
#endif


int écrit_packet(socket_t s, struct packet * p)
{

#if 1
if (!p || (unsigned)p->type > MAX_id)
#else
// Pas complètement portable: considère que l'on
// peut comparer un pointeur de fonction avec 0.
// Mais plus souple pour les erreurs de codification.
if (!p || (unsigned)p->type > MAX_id
|| tableau_exporte[p->type] == 0)
#endif
// Code pour émettre un message d'avertissement
return EINVAL;
return tableau_exporte[p->type](s,p->data);
}


Si tu avances un peu dans cette direction, tu vas rencontrer un problème
avec les tableaux; en particulier, Olivier proposait de gérer des
ARRAYOFMACHIN... sauf qu'il va manquer une information, la taille du
tableau; il y a trois solutions à cela: la première est de définir la
taille (fixe) des tableaux:

typedef MACHIN ARRAYOF25MACHINS[25];

ou
// constantes:
#define TAILLE_ARRAYOFMACHINS 25

// ...

typedef MACHIN ARRAYOFMACHINS[TAILLE_ARRAYOFMACHINS];

Une autre est de créer une structure avec la taille suivie du tableau:

typedef struct {
size_t taille;
MACHIN e[taille]; // ou MACHIN e[1] et allocation dynamique
} ARRAYOFMACHIN;

L'autre enfin est la solution utilisée pour les chaînes de caractères,
utiliser une valeur spécifique (souvent 0 ou un pointeur nul, mais c'est
purement conventionnel) pour marquer la fin du « tableau ».
Si tu suis cette voie-là, tu vas avoir toutes les contraintes associées
au traitement des chaînes de caractères de C, en particulier devoir
éviter les débordements.


Antoine
Avatar
espie
Cote serialisation, les choses sont plus sournoises que prevu... souvent
ca va marcher jusqu'a ce que ca ne marche pas. Si on envoie des donnees
d'une machine a une autre, souvent le premier test se fait sur des machines
identiques... mais des qu'on utilise vraiment du reseau, il va y avoir
des differences:
- de taille des donnees (int sur 32 ou 64 bits, etc)
- de codage des donnees (float IEEE ou autre, encodage des chaines de
caractere)
- d'alignement et de packing (octets intersticiels "de bourrage" dans certaines
structures)
- d'endianness (ordre des octets dans un short, un int, etc).

C'est pour ca que les protocoles internet "classiques" sont bases sur du
texte (telnet, en gros), avec des contraintes supplementaires (retour
a la ligne=retour a la ligne dos, crlf, ascii uniquement...)

envoyer directement des donnees binaires... c'est complique, et casse-gueule
si on veut que ca soit interoperable.

(confere ntohs et assimiles)
Avatar
Erwan David
(Marc Espie) écrivait :

Cote serialisation, les choses sont plus sournoises que prevu... souvent
ca va marcher jusqu'a ce que ca ne marche pas. Si on envoie des donnees
d'une machine a une autre, souvent le premier test se fait sur des machines
identiques... mais des qu'on utilise vraiment du reseau, il va y avoir
des differences:
- de taille des donnees (int sur 32 ou 64 bits, etc)
- de codage des donnees (float IEEE ou autre, encodage des chaines de
caractere)
- d'alignement et de packing (octets intersticiels "de bourrage" dans certaines
structures)
- d'endianness (ordre des octets dans un short, un int, etc).

C'est pour ca que les protocoles internet "classiques" sont bases sur du
texte (telnet, en gros), avec des contraintes supplementaires (retour
a la ligne=retour a la ligne dos, crlf, ascii uniquement...)

envoyer directement des donnees binaires... c'est complique, et casse-gueule
si on veut que ca soit interoperable.

(confere ntohs et assimiles)



Si on veut transmettre du binaire il faut en pratique définir le format
uniquement sous forme de tableaux d'octets, le protocole définissant les
ordres et les interprétations des octets (et encore...)



--
Les simplifications c'est trop compliqué