bon voila des petites modif :
traiter( ... )
{
do
{
if( contexte.etat == 1 )
{
contexte.etat = 2;
}
if( contexte.etat == 2 )
{
ret = recv_entete();
if( ret != 0 )
{
traiter_entete();
/*on peut libérer la mémoire de userbuf*/
free(userbuf)
contexte.etat = 3;
}
else
{
// sortie de boucle a contexte.etat==2
// pour remplir le recvbuf
}
}
if( contexte.etat == 3 )
{
// on a BodySize normalement, on a qu'à allouer
ret = recv_message();
if( ret != 0 )
{
traiter_message();
// on en a fini avec le message on peut
free(userbuf);
contexte.etat = 1; // ou 0 pour quitter
}
{
// sortie à contexte.etat==3
}
}
while ( contexte.etat == 1 );
}
bon voila des petites modif :
traiter( ... )
{
do
{
if( contexte.etat == 1 )
{
contexte.etat = 2;
}
if( contexte.etat == 2 )
{
ret = recv_entete();
if( ret != 0 )
{
traiter_entete();
/*on peut libérer la mémoire de userbuf*/
free(userbuf)
contexte.etat = 3;
}
else
{
// sortie de boucle a contexte.etat==2
// pour remplir le recvbuf
}
}
if( contexte.etat == 3 )
{
// on a BodySize normalement, on a qu'à allouer
ret = recv_message();
if( ret != 0 )
{
traiter_message();
// on en a fini avec le message on peut
free(userbuf);
contexte.etat = 1; // ou 0 pour quitter
}
{
// sortie à contexte.etat==3
}
}
while ( contexte.etat == 1 );
}
bon voila des petites modif :
traiter( ... )
{
do
{
if( contexte.etat == 1 )
{
contexte.etat = 2;
}
if( contexte.etat == 2 )
{
ret = recv_entete();
if( ret != 0 )
{
traiter_entete();
/*on peut libérer la mémoire de userbuf*/
free(userbuf)
contexte.etat = 3;
}
else
{
// sortie de boucle a contexte.etat==2
// pour remplir le recvbuf
}
}
if( contexte.etat == 3 )
{
// on a BodySize normalement, on a qu'à allouer
ret = recv_message();
if( ret != 0 )
{
traiter_message();
// on en a fini avec le message on peut
free(userbuf);
contexte.etat = 1; // ou 0 pour quitter
}
{
// sortie à contexte.etat==3
}
}
while ( contexte.etat == 1 );
}
Voici ce que tu dois faire en cas de FD_READ :
"case FD_READ:"
1_ Remplir recvbuf avec les données qui viennent d'arriver.
C'est là que le buffer est rechargé, quand il y a des
données à recevoir. Typiquement tu fais des recv() tant
que WSAGetLastError()!=WSAE_WOULDBLOCK.
2_ Ensuite appeler traiter() pour queles données qui viennent
d'arriver soient traitées.
J'ai reformaté ton code pour plus de lisibilité. Voici les états que tu
as retenu. J'ai enlevé l'état 4 qui ne sert à rien.
0 : pas de client : suivant=1
1 : prêt à traiter une requête (etat achevé) : suivant=2
2 : entête incomplète (etat inachevé) : suivant=3
3 : message incomplet (etat inachevé) : suivant=1 ou 0
traiter( ... )
{
do
{
if( contexte.etat == 1 )
{ // prêt à traiter une requête
contexte.etat = 2;
}
if( contexte.etat == 2 )
{
// recevoir (et traiter) l'entête
// recv_entete place l'entête dans userbuf
// ou mieux dans context.header_bufret = recv_entete();
if( ret != 0 )
{
// la réception est complète
// lit l'entête context.header_buf, par exemple
// vérifie si elle est valide, découpe la chaine
// et stocke dans la structure HEADER :
// - BodyType
// - BodySize (pour l'allocation du body)traiter_entete();/*on peut libérer la mémoire de userbuf*/
free(userbuf)
contexte.etat = 3;
}
else
{
// l'entête n'est pas reçue en entier// sortie de boucle a contexte.etat==2
// pour remplir le recvbuf
}
}
if( contexte.etat == 3 )
{ // recevoir (et traiter) le message
// on a BodySize normalement, on a qu'à allouer
// BodySize octets pour userbuf
context.message = malloc( contect.BodySize );
// on reçoit tant qu'on a pas copié BodySize
// octets du recvbuf vers userbuf.
// si nbr_octet_copies==BodySize, retour !=0
// on a tout reçu
// si <BodySize ça veut dire que le recvbuf
// contient pas tout le body, on retourne 0ret = recv_message();
if( ret != 0 )
{
// la réception est complète, on peut traiter le
// message selon le type de BodyType, le message
// est dans recvbuf (ou mieux dans context.message)traiter_message();
// on en a fini avec le message on peut // libérer la
mémoire. free(userbuf);
contexte.etat = 1; // ou 0 pour quitter
}
else{ // body pas entier dans le recvbuf
// sortie à contexte.etat==3
}
}
}while ( contexte.etat == 1 );
}
Tu vois, si on termine sur un état incomplet (0, 2 ou 3) c'est qu'il n'y
a pas assez de données dans le recvbuf pour faire quelquechose d'utile.
Alors on sort de la fonction de traitement. Celle-ci ne sera appelée
qu'au prochain FD_READ (quand des données auront été reçues).
Le mieux pour la structure context est de créer un membre par élément que
tu veux recevoir (c'est plus clair et facile à déboguer) :
struct context
{
int etat;
// header
// données brutes : (le recvbuf dans le cas réception de header)
char *header_buf;
// données analysées
int BodyType;
int BodyLen;
// body
// données brutes : (le recvbuf dans le cas réception de body)
char *body;
// données analysées
char *param1;
int param2;
};
a+
Voici ce que tu dois faire en cas de FD_READ :
"case FD_READ:"
1_ Remplir recvbuf avec les données qui viennent d'arriver.
C'est là que le buffer est rechargé, quand il y a des
données à recevoir. Typiquement tu fais des recv() tant
que WSAGetLastError()!=WSAE_WOULDBLOCK.
2_ Ensuite appeler traiter() pour queles données qui viennent
d'arriver soient traitées.
J'ai reformaté ton code pour plus de lisibilité. Voici les états que tu
as retenu. J'ai enlevé l'état 4 qui ne sert à rien.
0 : pas de client : suivant=1
1 : prêt à traiter une requête (etat achevé) : suivant=2
2 : entête incomplète (etat inachevé) : suivant=3
3 : message incomplet (etat inachevé) : suivant=1 ou 0
traiter( ... )
{
do
{
if( contexte.etat == 1 )
{ // prêt à traiter une requête
contexte.etat = 2;
}
if( contexte.etat == 2 )
{
// recevoir (et traiter) l'entête
// recv_entete place l'entête dans userbuf
// ou mieux dans context.header_buf
ret = recv_entete();
if( ret != 0 )
{
// la réception est complète
// lit l'entête context.header_buf, par exemple
// vérifie si elle est valide, découpe la chaine
// et stocke dans la structure HEADER :
// - BodyType
// - BodySize (pour l'allocation du body)
traiter_entete();
/*on peut libérer la mémoire de userbuf*/
free(userbuf)
contexte.etat = 3;
}
else
{
// l'entête n'est pas reçue en entier
// sortie de boucle a contexte.etat==2
// pour remplir le recvbuf
}
}
if( contexte.etat == 3 )
{ // recevoir (et traiter) le message
// on a BodySize normalement, on a qu'à allouer
// BodySize octets pour userbuf
context.message = malloc( contect.BodySize );
// on reçoit tant qu'on a pas copié BodySize
// octets du recvbuf vers userbuf.
// si nbr_octet_copies==BodySize, retour !=0
// on a tout reçu
// si <BodySize ça veut dire que le recvbuf
// contient pas tout le body, on retourne 0
ret = recv_message();
if( ret != 0 )
{
// la réception est complète, on peut traiter le
// message selon le type de BodyType, le message
// est dans recvbuf (ou mieux dans context.message)
traiter_message();
// on en a fini avec le message on peut // libérer la
mémoire. free(userbuf);
contexte.etat = 1; // ou 0 pour quitter
}
else
{ // body pas entier dans le recvbuf
// sortie à contexte.etat==3
}
}
}
while ( contexte.etat == 1 );
}
Tu vois, si on termine sur un état incomplet (0, 2 ou 3) c'est qu'il n'y
a pas assez de données dans le recvbuf pour faire quelquechose d'utile.
Alors on sort de la fonction de traitement. Celle-ci ne sera appelée
qu'au prochain FD_READ (quand des données auront été reçues).
Le mieux pour la structure context est de créer un membre par élément que
tu veux recevoir (c'est plus clair et facile à déboguer) :
struct context
{
int etat;
// header
// données brutes : (le recvbuf dans le cas réception de header)
char *header_buf;
// données analysées
int BodyType;
int BodyLen;
// body
// données brutes : (le recvbuf dans le cas réception de body)
char *body;
// données analysées
char *param1;
int param2;
};
a+
Voici ce que tu dois faire en cas de FD_READ :
"case FD_READ:"
1_ Remplir recvbuf avec les données qui viennent d'arriver.
C'est là que le buffer est rechargé, quand il y a des
données à recevoir. Typiquement tu fais des recv() tant
que WSAGetLastError()!=WSAE_WOULDBLOCK.
2_ Ensuite appeler traiter() pour queles données qui viennent
d'arriver soient traitées.
J'ai reformaté ton code pour plus de lisibilité. Voici les états que tu
as retenu. J'ai enlevé l'état 4 qui ne sert à rien.
0 : pas de client : suivant=1
1 : prêt à traiter une requête (etat achevé) : suivant=2
2 : entête incomplète (etat inachevé) : suivant=3
3 : message incomplet (etat inachevé) : suivant=1 ou 0
traiter( ... )
{
do
{
if( contexte.etat == 1 )
{ // prêt à traiter une requête
contexte.etat = 2;
}
if( contexte.etat == 2 )
{
// recevoir (et traiter) l'entête
// recv_entete place l'entête dans userbuf
// ou mieux dans context.header_bufret = recv_entete();
if( ret != 0 )
{
// la réception est complète
// lit l'entête context.header_buf, par exemple
// vérifie si elle est valide, découpe la chaine
// et stocke dans la structure HEADER :
// - BodyType
// - BodySize (pour l'allocation du body)traiter_entete();/*on peut libérer la mémoire de userbuf*/
free(userbuf)
contexte.etat = 3;
}
else
{
// l'entête n'est pas reçue en entier// sortie de boucle a contexte.etat==2
// pour remplir le recvbuf
}
}
if( contexte.etat == 3 )
{ // recevoir (et traiter) le message
// on a BodySize normalement, on a qu'à allouer
// BodySize octets pour userbuf
context.message = malloc( contect.BodySize );
// on reçoit tant qu'on a pas copié BodySize
// octets du recvbuf vers userbuf.
// si nbr_octet_copies==BodySize, retour !=0
// on a tout reçu
// si <BodySize ça veut dire que le recvbuf
// contient pas tout le body, on retourne 0ret = recv_message();
if( ret != 0 )
{
// la réception est complète, on peut traiter le
// message selon le type de BodyType, le message
// est dans recvbuf (ou mieux dans context.message)traiter_message();
// on en a fini avec le message on peut // libérer la
mémoire. free(userbuf);
contexte.etat = 1; // ou 0 pour quitter
}
else{ // body pas entier dans le recvbuf
// sortie à contexte.etat==3
}
}
}while ( contexte.etat == 1 );
}
Tu vois, si on termine sur un état incomplet (0, 2 ou 3) c'est qu'il n'y
a pas assez de données dans le recvbuf pour faire quelquechose d'utile.
Alors on sort de la fonction de traitement. Celle-ci ne sera appelée
qu'au prochain FD_READ (quand des données auront été reçues).
Le mieux pour la structure context est de créer un membre par élément que
tu veux recevoir (c'est plus clair et facile à déboguer) :
struct context
{
int etat;
// header
// données brutes : (le recvbuf dans le cas réception de header)
char *header_buf;
// données analysées
int BodyType;
int BodyLen;
// body
// données brutes : (le recvbuf dans le cas réception de body)
char *body;
// données analysées
char *param1;
int param2;
};
a+
Ce que tu peux faire c'est qu'une fois lues, les données sont enlevées du
buffer. Donc le premier octet est toujours le premier non lu.
recvbuf : xxxxxxxxxxxxxxxoooooooooo
userbuf : xxxxxxxxooooooooooo
--> tu lis des octets
recvbuf : lllllxxxxxxxxxxoooooooooo
userbuf : xxxxxxxxllllloooooo
--> puis
recvbuf : xxxxxxxxxxooooooooooooooo
userbuf : xxxxxxxxxxxxxoooooo
sinon j'alloue directement la taille reçue dans le recvbuf, elle peut
etre trop grande, trop petite (besoin d'un realloc après) ou pile
poil.
C'est aussi une bonne méthode.
Tu vois, toutes ces questions n'on en fait rien à voir avec le protocole.
La question est : comment faire un buffer (de type pile) efficace ? Ce
qui est le sujet d'un tout autre thread a mon avis.
Oui, dans le cas où n-m<BodySize tu restes à 3. Tous les autres if sont à
false, et la condition while( etat==1 ) (voir ton implémentation
légèrement modifiée) fait que tu sors de la fonction traiter. Cette
fonction ne sera appelée qu'au prochain FD_READ.
En restant à 3 tu sors de la boucle de lecture.
si je comprends, on reviendra ensuite dans la boucle avec un état 3,
donc on reprendra là où on en était. il faut donc conserver également
la position courante d'écriture dans le userbuf.
Oui !!!
Franchement ça vaut vraiment le coup d'y passer un peu de temps. Même si
dans le cas présent on pourrait torcher ton programme en quelques lignes,
en faisant comme ça tu ne seras pas capable d'appréhender un projet plus
important.
Ce que tu peux faire c'est qu'une fois lues, les données sont enlevées du
buffer. Donc le premier octet est toujours le premier non lu.
recvbuf : xxxxxxxxxxxxxxxoooooooooo
userbuf : xxxxxxxxooooooooooo
--> tu lis des octets
recvbuf : lllllxxxxxxxxxxoooooooooo
userbuf : xxxxxxxxllllloooooo
--> puis
recvbuf : xxxxxxxxxxooooooooooooooo
userbuf : xxxxxxxxxxxxxoooooo
sinon j'alloue directement la taille reçue dans le recvbuf, elle peut
etre trop grande, trop petite (besoin d'un realloc après) ou pile
poil.
C'est aussi une bonne méthode.
Tu vois, toutes ces questions n'on en fait rien à voir avec le protocole.
La question est : comment faire un buffer (de type pile) efficace ? Ce
qui est le sujet d'un tout autre thread a mon avis.
Oui, dans le cas où n-m<BodySize tu restes à 3. Tous les autres if sont à
false, et la condition while( etat==1 ) (voir ton implémentation
légèrement modifiée) fait que tu sors de la fonction traiter. Cette
fonction ne sera appelée qu'au prochain FD_READ.
En restant à 3 tu sors de la boucle de lecture.
si je comprends, on reviendra ensuite dans la boucle avec un état 3,
donc on reprendra là où on en était. il faut donc conserver également
la position courante d'écriture dans le userbuf.
Oui !!!
Franchement ça vaut vraiment le coup d'y passer un peu de temps. Même si
dans le cas présent on pourrait torcher ton programme en quelques lignes,
en faisant comme ça tu ne seras pas capable d'appréhender un projet plus
important.
Ce que tu peux faire c'est qu'une fois lues, les données sont enlevées du
buffer. Donc le premier octet est toujours le premier non lu.
recvbuf : xxxxxxxxxxxxxxxoooooooooo
userbuf : xxxxxxxxooooooooooo
--> tu lis des octets
recvbuf : lllllxxxxxxxxxxoooooooooo
userbuf : xxxxxxxxllllloooooo
--> puis
recvbuf : xxxxxxxxxxooooooooooooooo
userbuf : xxxxxxxxxxxxxoooooo
sinon j'alloue directement la taille reçue dans le recvbuf, elle peut
etre trop grande, trop petite (besoin d'un realloc après) ou pile
poil.
C'est aussi une bonne méthode.
Tu vois, toutes ces questions n'on en fait rien à voir avec le protocole.
La question est : comment faire un buffer (de type pile) efficace ? Ce
qui est le sujet d'un tout autre thread a mon avis.
Oui, dans le cas où n-m<BodySize tu restes à 3. Tous les autres if sont à
false, et la condition while( etat==1 ) (voir ton implémentation
légèrement modifiée) fait que tu sors de la fonction traiter. Cette
fonction ne sera appelée qu'au prochain FD_READ.
En restant à 3 tu sors de la boucle de lecture.
si je comprends, on reviendra ensuite dans la boucle avec un état 3,
donc on reprendra là où on en était. il faut donc conserver également
la position courante d'écriture dans le userbuf.
Oui !!!
Franchement ça vaut vraiment le coup d'y passer un peu de temps. Même si
dans le cas présent on pourrait torcher ton programme en quelques lignes,
en faisant comme ça tu ne seras pas capable d'appréhender un projet plus
important.
>> Ce que tu peux faire c'est qu'une fois lues, les données sont
enlevées du buffer. Donc le premier octet est toujours le premier non
lu.
recvbuf : xxxxxxxxxxxxxxxoooooooooo
userbuf : xxxxxxxxooooooooooo
--> tu lis des octets
recvbuf : lllllxxxxxxxxxxoooooooooo
userbuf : xxxxxxxxllllloooooo
--> puis
recvbuf : xxxxxxxxxxooooooooooooooo
userbuf : xxxxxxxxxxxxxoooooo
c'est bien mignon ça, mais comment tu veux faire pour "enlever" des
données du recvbuf ? :/
en changeant l'adresse du buffer et en la mettant a la position
courante ?
recvbuf=&recvbuf[position_courante];
>> Ce que tu peux faire c'est qu'une fois lues, les données sont
enlevées du buffer. Donc le premier octet est toujours le premier non
lu.
recvbuf : xxxxxxxxxxxxxxxoooooooooo
userbuf : xxxxxxxxooooooooooo
--> tu lis des octets
recvbuf : lllllxxxxxxxxxxoooooooooo
userbuf : xxxxxxxxllllloooooo
--> puis
recvbuf : xxxxxxxxxxooooooooooooooo
userbuf : xxxxxxxxxxxxxoooooo
c'est bien mignon ça, mais comment tu veux faire pour "enlever" des
données du recvbuf ? :/
en changeant l'adresse du buffer et en la mettant a la position
courante ?
recvbuf=&recvbuf[position_courante];
>> Ce que tu peux faire c'est qu'une fois lues, les données sont
enlevées du buffer. Donc le premier octet est toujours le premier non
lu.
recvbuf : xxxxxxxxxxxxxxxoooooooooo
userbuf : xxxxxxxxooooooooooo
--> tu lis des octets
recvbuf : lllllxxxxxxxxxxoooooooooo
userbuf : xxxxxxxxllllloooooo
--> puis
recvbuf : xxxxxxxxxxooooooooooooooo
userbuf : xxxxxxxxxxxxxoooooo
c'est bien mignon ça, mais comment tu veux faire pour "enlever" des
données du recvbuf ? :/
en changeant l'adresse du buffer et en la mettant a la position
courante ?
recvbuf=&recvbuf[position_courante];
>>> if( contexte.etat == 2 )
{
// recevoir (et traiter) l'entête
// recv_entete place l'entête dans userbuf
// ou mieux dans context.header_bufret = recv_entete();
if( ret != 0 )
{
// la réception est complète
// lit l'entête context.header_buf, par exemple
// vérifie si elle est valide, découpe la chaine
// et stocke dans la structure HEADER :
// - BodyType
// - BodySize (pour l'allocation du body)traiter_entete();/*on peut libérer la mémoire de userbuf*/
free(userbuf)
contexte.etat = 3;
}
là il faut ajouter "noter la position dans le recvbuf", en effet, si
la reception de l'entete est complète, ça signifie :
-soit on a "vidé" le recvbuf auquel cas dans le contexte.etat==3 on
aura un return 0 et une sortie de boucle et un rechargement du
recvbuf.
-soit il reste des octets dans le recvbuf, qui correspondent forcément
au body, ce qui signifie que recv_message() devra lire dans le recvbuf
a partir de là ou recv_entete() ct arrété... c logique !
else
{
// l'entête n'est pas reçue en entier// sortie de boucle a contexte.etat==2
// pour remplir le recvbuf
}
là, c'est là position dans le header_buf qu'il faut noter, car on sort
de la boucle, un nouveau FD_READ va remplir le recvbuf, et dans mon
prochain passage dans traiter(), contexte.etat sera égal a 2, et
recv_entete() ne devra pas "écraser" l'entete inachevée, mais
reprendre là ou elle c'était arrêtée. logique également.
oui, et penser aux positions dans les buffers appropriés.
>>> if( contexte.etat == 2 )
{
// recevoir (et traiter) l'entête
// recv_entete place l'entête dans userbuf
// ou mieux dans context.header_buf
ret = recv_entete();
if( ret != 0 )
{
// la réception est complète
// lit l'entête context.header_buf, par exemple
// vérifie si elle est valide, découpe la chaine
// et stocke dans la structure HEADER :
// - BodyType
// - BodySize (pour l'allocation du body)
traiter_entete();
/*on peut libérer la mémoire de userbuf*/
free(userbuf)
contexte.etat = 3;
}
là il faut ajouter "noter la position dans le recvbuf", en effet, si
la reception de l'entete est complète, ça signifie :
-soit on a "vidé" le recvbuf auquel cas dans le contexte.etat==3 on
aura un return 0 et une sortie de boucle et un rechargement du
recvbuf.
-soit il reste des octets dans le recvbuf, qui correspondent forcément
au body, ce qui signifie que recv_message() devra lire dans le recvbuf
a partir de là ou recv_entete() ct arrété... c logique !
else
{
// l'entête n'est pas reçue en entier
// sortie de boucle a contexte.etat==2
// pour remplir le recvbuf
}
là, c'est là position dans le header_buf qu'il faut noter, car on sort
de la boucle, un nouveau FD_READ va remplir le recvbuf, et dans mon
prochain passage dans traiter(), contexte.etat sera égal a 2, et
recv_entete() ne devra pas "écraser" l'entete inachevée, mais
reprendre là ou elle c'était arrêtée. logique également.
oui, et penser aux positions dans les buffers appropriés.
>>> if( contexte.etat == 2 )
{
// recevoir (et traiter) l'entête
// recv_entete place l'entête dans userbuf
// ou mieux dans context.header_bufret = recv_entete();
if( ret != 0 )
{
// la réception est complète
// lit l'entête context.header_buf, par exemple
// vérifie si elle est valide, découpe la chaine
// et stocke dans la structure HEADER :
// - BodyType
// - BodySize (pour l'allocation du body)traiter_entete();/*on peut libérer la mémoire de userbuf*/
free(userbuf)
contexte.etat = 3;
}
là il faut ajouter "noter la position dans le recvbuf", en effet, si
la reception de l'entete est complète, ça signifie :
-soit on a "vidé" le recvbuf auquel cas dans le contexte.etat==3 on
aura un return 0 et une sortie de boucle et un rechargement du
recvbuf.
-soit il reste des octets dans le recvbuf, qui correspondent forcément
au body, ce qui signifie que recv_message() devra lire dans le recvbuf
a partir de là ou recv_entete() ct arrété... c logique !
else
{
// l'entête n'est pas reçue en entier// sortie de boucle a contexte.etat==2
// pour remplir le recvbuf
}
là, c'est là position dans le header_buf qu'il faut noter, car on sort
de la boucle, un nouveau FD_READ va remplir le recvbuf, et dans mon
prochain passage dans traiter(), contexte.etat sera égal a 2, et
recv_entete() ne devra pas "écraser" l'entete inachevée, mais
reprendre là ou elle c'était arrêtée. logique également.
oui, et penser aux positions dans les buffers appropriés.
> Enlever n octets au buffer buf :
memmove( buf.ptr, buf.ptr+n, n );
buf.len -= n;
buf.ptr = realloc( buf.len );
> Enlever n octets au buffer buf :
memmove( buf.ptr, buf.ptr+n, n );
buf.len -= n;
buf.ptr = realloc( buf.len );
> Enlever n octets au buffer buf :
memmove( buf.ptr, buf.ptr+n, n );
buf.len -= n;
buf.ptr = realloc( buf.len );
Je crois que le mieux est que je te donne une implémentation complète pour
que tu voies comment je fais ça. Encore une fois on peut faire autrement,
ce n'est pas la seule méthode. Mais crois moi, j'ai passé du temps à
planifier tout ça pour que ce soit ple plus propre, flexible et logique
possible.
Aha !
Mais tout ça se passe au niveau de traitement du buffer (c'est à dire dans
un appel à une fonction buffer_* qui a lieu dans une des fonctions
recv_entete().
En gros tu as deux solutions qui se valent :
****************
* Solution 1 : * Les données restent dans le recvbuf tant que l'objet
**************** entier n'est pas reçu. Dans ce cas c'est simple, tu
reprends le code de traiter précédent (sans en changer une ligne ou
presque) et la fonction recv_bidule ressemble à :
Tes buffers sont des objets de type buffer avec l'interface que j'ai donnée
avant :
buffer_read : lit et supprime des octets du buffer
buffer_len : renvoie la taille du buffer
buffer_peek : lit mais ne supprime pas
buffer_get : renvoie un pointeur vers le premier octet
struct buffer recvbuffer; // ce n'est pas un simple char*, il contient
// d'autres infos telles que sa taille
int recv_bidule( ... )
{
// la condition qui va bien pour déterminer que l'entête est complète
char *pos;
pos = strnstr( buffer_get(recvbuffer), ".END", buffer_len(recvbuffer) );
if( pos!=0 ) {
// l'entête est reçue en entier
int size = pos-buffer_get(recvbuffer);
context.header = malloc( size );
buffer_read( recvbuffer, context.header, size );
return 1;
}
else
{
// non, continuer à chercher
return 0;
}
}
****************
* Solution 2 : * Les données lues sont copiées octet par octet dans un
**************** buffer de travail, c'est à dire directement en place.
C'est à peine plus long mais ça a l'avantage de vider le recvbuf au
maximum. On sait exactement ce qui a été lu en regardant recvbuf et cela
facilite le déboguage. Dans le cas précédent il faut deviner.
Là je rajoute deux fonctions à l'interface buffer :
buffer_get_char : qui renvoie le caractère suivant et l'enlève du buffer
buffer_append_char : qui ajoute un caractère à la fin d'un buffer
int recv_bidule( ... )
{
do
{
int err;
char c;
// lire un caractère
err = buffer_get_char( recvbuf, &c );
if( err!=BUFFER_EOF )
{
// ajouter le caractère
buffer_append_char( context.bidule, c );
}
else
{
// recvbuf est vide -> incomplet
return 0;
}
}
// les quatre derniers caractères du buffer sonr ".END"
while( strncmp(
buffer_get(recvbuffer)+buffer_len(recvbuffer)-4,
".END",
4)
);
return 1;
}
Dans le dernier cas il faut évidemment initialiser le buffer (ce que tu
appelles le malloc initial). Tu seras d'accord avec moi pour dire que ça a
a voir avec la réception de données. Donc c'est dans la fonction
recv_bidule que ça se place. Comment le mettre exactement dépend de
l'implémentation de buffer.
Un realloc de 0 normalement fait un malloc donc une solution (assez moche
je l'admets) est d'utiliser realloc tout le temps.
il semblerai donc qu'il faille encore une variable suplémentaire,
charger d'indiquer le nombre de boucle de reception d'un bloc.
Tu peux faire comme-ça mais ce n'est pas obligatoire et ça complique
(inutilement si tu veux mon avis) le code.
Je crois que le mieux est que je te donne une implémentation complète pour
que tu voies comment je fais ça. Encore une fois on peut faire autrement,
ce n'est pas la seule méthode. Mais crois moi, j'ai passé du temps à
planifier tout ça pour que ce soit ple plus propre, flexible et logique
possible.
Aha !
Mais tout ça se passe au niveau de traitement du buffer (c'est à dire dans
un appel à une fonction buffer_* qui a lieu dans une des fonctions
recv_entete().
En gros tu as deux solutions qui se valent :
****************
* Solution 1 : * Les données restent dans le recvbuf tant que l'objet
**************** entier n'est pas reçu. Dans ce cas c'est simple, tu
reprends le code de traiter précédent (sans en changer une ligne ou
presque) et la fonction recv_bidule ressemble à :
Tes buffers sont des objets de type buffer avec l'interface que j'ai donnée
avant :
buffer_read : lit et supprime des octets du buffer
buffer_len : renvoie la taille du buffer
buffer_peek : lit mais ne supprime pas
buffer_get : renvoie un pointeur vers le premier octet
struct buffer recvbuffer; // ce n'est pas un simple char*, il contient
// d'autres infos telles que sa taille
int recv_bidule( ... )
{
// la condition qui va bien pour déterminer que l'entête est complète
char *pos;
pos = strnstr( buffer_get(recvbuffer), ".END", buffer_len(recvbuffer) );
if( pos!=0 ) {
// l'entête est reçue en entier
int size = pos-buffer_get(recvbuffer);
context.header = malloc( size );
buffer_read( recvbuffer, context.header, size );
return 1;
}
else
{
// non, continuer à chercher
return 0;
}
}
****************
* Solution 2 : * Les données lues sont copiées octet par octet dans un
**************** buffer de travail, c'est à dire directement en place.
C'est à peine plus long mais ça a l'avantage de vider le recvbuf au
maximum. On sait exactement ce qui a été lu en regardant recvbuf et cela
facilite le déboguage. Dans le cas précédent il faut deviner.
Là je rajoute deux fonctions à l'interface buffer :
buffer_get_char : qui renvoie le caractère suivant et l'enlève du buffer
buffer_append_char : qui ajoute un caractère à la fin d'un buffer
int recv_bidule( ... )
{
do
{
int err;
char c;
// lire un caractère
err = buffer_get_char( recvbuf, &c );
if( err!=BUFFER_EOF )
{
// ajouter le caractère
buffer_append_char( context.bidule, c );
}
else
{
// recvbuf est vide -> incomplet
return 0;
}
}
// les quatre derniers caractères du buffer sonr ".END"
while( strncmp(
buffer_get(recvbuffer)+buffer_len(recvbuffer)-4,
".END",
4)
);
return 1;
}
Dans le dernier cas il faut évidemment initialiser le buffer (ce que tu
appelles le malloc initial). Tu seras d'accord avec moi pour dire que ça a
a voir avec la réception de données. Donc c'est dans la fonction
recv_bidule que ça se place. Comment le mettre exactement dépend de
l'implémentation de buffer.
Un realloc de 0 normalement fait un malloc donc une solution (assez moche
je l'admets) est d'utiliser realloc tout le temps.
il semblerai donc qu'il faille encore une variable suplémentaire,
charger d'indiquer le nombre de boucle de reception d'un bloc.
Tu peux faire comme-ça mais ce n'est pas obligatoire et ça complique
(inutilement si tu veux mon avis) le code.
Je crois que le mieux est que je te donne une implémentation complète pour
que tu voies comment je fais ça. Encore une fois on peut faire autrement,
ce n'est pas la seule méthode. Mais crois moi, j'ai passé du temps à
planifier tout ça pour que ce soit ple plus propre, flexible et logique
possible.
Aha !
Mais tout ça se passe au niveau de traitement du buffer (c'est à dire dans
un appel à une fonction buffer_* qui a lieu dans une des fonctions
recv_entete().
En gros tu as deux solutions qui se valent :
****************
* Solution 1 : * Les données restent dans le recvbuf tant que l'objet
**************** entier n'est pas reçu. Dans ce cas c'est simple, tu
reprends le code de traiter précédent (sans en changer une ligne ou
presque) et la fonction recv_bidule ressemble à :
Tes buffers sont des objets de type buffer avec l'interface que j'ai donnée
avant :
buffer_read : lit et supprime des octets du buffer
buffer_len : renvoie la taille du buffer
buffer_peek : lit mais ne supprime pas
buffer_get : renvoie un pointeur vers le premier octet
struct buffer recvbuffer; // ce n'est pas un simple char*, il contient
// d'autres infos telles que sa taille
int recv_bidule( ... )
{
// la condition qui va bien pour déterminer que l'entête est complète
char *pos;
pos = strnstr( buffer_get(recvbuffer), ".END", buffer_len(recvbuffer) );
if( pos!=0 ) {
// l'entête est reçue en entier
int size = pos-buffer_get(recvbuffer);
context.header = malloc( size );
buffer_read( recvbuffer, context.header, size );
return 1;
}
else
{
// non, continuer à chercher
return 0;
}
}
****************
* Solution 2 : * Les données lues sont copiées octet par octet dans un
**************** buffer de travail, c'est à dire directement en place.
C'est à peine plus long mais ça a l'avantage de vider le recvbuf au
maximum. On sait exactement ce qui a été lu en regardant recvbuf et cela
facilite le déboguage. Dans le cas précédent il faut deviner.
Là je rajoute deux fonctions à l'interface buffer :
buffer_get_char : qui renvoie le caractère suivant et l'enlève du buffer
buffer_append_char : qui ajoute un caractère à la fin d'un buffer
int recv_bidule( ... )
{
do
{
int err;
char c;
// lire un caractère
err = buffer_get_char( recvbuf, &c );
if( err!=BUFFER_EOF )
{
// ajouter le caractère
buffer_append_char( context.bidule, c );
}
else
{
// recvbuf est vide -> incomplet
return 0;
}
}
// les quatre derniers caractères du buffer sonr ".END"
while( strncmp(
buffer_get(recvbuffer)+buffer_len(recvbuffer)-4,
".END",
4)
);
return 1;
}
Dans le dernier cas il faut évidemment initialiser le buffer (ce que tu
appelles le malloc initial). Tu seras d'accord avec moi pour dire que ça a
a voir avec la réception de données. Donc c'est dans la fonction
recv_bidule que ça se place. Comment le mettre exactement dépend de
l'implémentation de buffer.
Un realloc de 0 normalement fait un malloc donc une solution (assez moche
je l'admets) est d'utiliser realloc tout le temps.
il semblerai donc qu'il faille encore une variable suplémentaire,
charger d'indiquer le nombre de boucle de reception d'un bloc.
Tu peux faire comme-ça mais ce n'est pas obligatoire et ça complique
(inutilement si tu veux mon avis) le code.
int recv_bidule( ... )
{
do
{
int err;
char c;
// lire un caractère
err = buffer_get_char( recvbuf, &c );
if( err!=BUFFER_EOF )
{
// ajouter le caractère
buffer_append_char( context.bidule, c );
}
else
{
// recvbuf est vide -> incomplet
return 0;
}
}
// les quatre derniers caractères du buffer sonr ".END"
while( strncmp(
buffer_get(recvbuffer)+buffer_len(recvbuffer)-4,
".END",
4)
);
int recv_bidule( ... )
{
do
{
int err;
char c;
// lire un caractère
err = buffer_get_char( recvbuf, &c );
if( err!=BUFFER_EOF )
{
// ajouter le caractère
buffer_append_char( context.bidule, c );
}
else
{
// recvbuf est vide -> incomplet
return 0;
}
}
// les quatre derniers caractères du buffer sonr ".END"
while( strncmp(
buffer_get(recvbuffer)+buffer_len(recvbuffer)-4,
".END",
4)
);
int recv_bidule( ... )
{
do
{
int err;
char c;
// lire un caractère
err = buffer_get_char( recvbuf, &c );
if( err!=BUFFER_EOF )
{
// ajouter le caractère
buffer_append_char( context.bidule, c );
}
else
{
// recvbuf est vide -> incomplet
return 0;
}
}
// les quatre derniers caractères du buffer sonr ".END"
while( strncmp(
buffer_get(recvbuffer)+buffer_len(recvbuffer)-4,
".END",
4)
);