alors disons qu'il y a n octets a lire, et m est retourné par
ioctlsocket avec FIONREAD, si m<n comment connaitre n ? je ne peux pas
allouer de mémoire pour mon buffer de réception, c'est bien dommage.
je ne vois pas comment faire dans le cas d'un trop gros nombre
d'octets.
alors disons qu'il y a n octets a lire, et m est retourné par
ioctlsocket avec FIONREAD, si m<n comment connaitre n ? je ne peux pas
allouer de mémoire pour mon buffer de réception, c'est bien dommage.
je ne vois pas comment faire dans le cas d'un trop gros nombre
d'octets.
alors disons qu'il y a n octets a lire, et m est retourné par
ioctlsocket avec FIONREAD, si m<n comment connaitre n ? je ne peux pas
allouer de mémoire pour mon buffer de réception, c'est bien dommage.
je ne vois pas comment faire dans le cas d'un trop gros nombre
d'octets.
> alors disons qu'il y a n octets a lire, et m est retourné par
ioctlsocket avec FIONREAD, si m<n comment connaitre n ? je ne peux pas
allouer de mémoire pour mon buffer de réception, c'est bien dommage.
mais si un autre FD_READ, aucun problème alors, le restant des données
sera reçu plus tard et ajouté au buffer approprié.
> alors disons qu'il y a n octets a lire, et m est retourné par
ioctlsocket avec FIONREAD, si m<n comment connaitre n ? je ne peux pas
allouer de mémoire pour mon buffer de réception, c'est bien dommage.
mais si un autre FD_READ, aucun problème alors, le restant des données
sera reçu plus tard et ajouté au buffer approprié.
> alors disons qu'il y a n octets a lire, et m est retourné par
ioctlsocket avec FIONREAD, si m<n comment connaitre n ? je ne peux pas
allouer de mémoire pour mon buffer de réception, c'est bien dommage.
mais si un autre FD_READ, aucun problème alors, le restant des données
sera reçu plus tard et ajouté au buffer approprié.
> le code de la réception est basé sur 3 buffers char *.
1er cas, on a trouvé la fin avant d'avoir vidé tout le recvbuf dans
buf.
dans ce cas, mettre les octets "en trop" dans le 3eme buffer appelé
'savebuf', qui sera réutilisé lors d'un prochain FD_READ avant le
vidage de recvbuf.
ensuite noter la position courante dans ce buffer, et analyser les
données du paquet de 'buf'
> le code de la réception est basé sur 3 buffers char *.
1er cas, on a trouvé la fin avant d'avoir vidé tout le recvbuf dans
buf.
dans ce cas, mettre les octets "en trop" dans le 3eme buffer appelé
'savebuf', qui sera réutilisé lors d'un prochain FD_READ avant le
vidage de recvbuf.
ensuite noter la position courante dans ce buffer, et analyser les
données du paquet de 'buf'
> le code de la réception est basé sur 3 buffers char *.
1er cas, on a trouvé la fin avant d'avoir vidé tout le recvbuf dans
buf.
dans ce cas, mettre les octets "en trop" dans le 3eme buffer appelé
'savebuf', qui sera réutilisé lors d'un prochain FD_READ avant le
vidage de recvbuf.
ensuite noter la position courante dans ce buffer, et analyser les
données du paquet de 'buf'
le code de la réception est basé sur 3 buffers char *.
Essaie d'utiliser des buffers généraux et pas des chaînes asciiz, le
jour où tu voudras recevoir des données binaires tu auras moins de mal.
A mon avis c'est plus simple si tu lis un à un les octets du recvbuf
(fournis des fonctions du genre get et peek). Ça t'évitera le troisième
buffer. Le plus important c'est de recevoir toutes les données reçues en
bloc.
L'idée c'est que tu veux recevoir un bloc, dont la fin est par exemple
marquée par ".END".
Alors tu vas recevoir 1 à 1 les octets.
Pour cela, tu
fais un appel à recv_buf (ta fonction de buffer de réception intelligent).
Si le recvbuf contient déjà des données, tu renvoies l'octet demandé et tu
le supprimes du buffer.
Si recvbuf est vide, tu fais un appel à recv() pour
le remplir au maximum (cas sockets bloquantes), soit tu renvoies un message
comme quoi le buffer est vide (cas sockets non-bloquantes) et le buffer
sera rempli à la prochaine réception de FD_READ.
a+
le code de la réception est basé sur 3 buffers char *.
Essaie d'utiliser des buffers généraux et pas des chaînes asciiz, le
jour où tu voudras recevoir des données binaires tu auras moins de mal.
A mon avis c'est plus simple si tu lis un à un les octets du recvbuf
(fournis des fonctions du genre get et peek). Ça t'évitera le troisième
buffer. Le plus important c'est de recevoir toutes les données reçues en
bloc.
L'idée c'est que tu veux recevoir un bloc, dont la fin est par exemple
marquée par ".END".
Alors tu vas recevoir 1 à 1 les octets.
Pour cela, tu
fais un appel à recv_buf (ta fonction de buffer de réception intelligent).
Si le recvbuf contient déjà des données, tu renvoies l'octet demandé et tu
le supprimes du buffer.
Si recvbuf est vide, tu fais un appel à recv() pour
le remplir au maximum (cas sockets bloquantes), soit tu renvoies un message
comme quoi le buffer est vide (cas sockets non-bloquantes) et le buffer
sera rempli à la prochaine réception de FD_READ.
a+
le code de la réception est basé sur 3 buffers char *.
Essaie d'utiliser des buffers généraux et pas des chaînes asciiz, le
jour où tu voudras recevoir des données binaires tu auras moins de mal.
A mon avis c'est plus simple si tu lis un à un les octets du recvbuf
(fournis des fonctions du genre get et peek). Ça t'évitera le troisième
buffer. Le plus important c'est de recevoir toutes les données reçues en
bloc.
L'idée c'est que tu veux recevoir un bloc, dont la fin est par exemple
marquée par ".END".
Alors tu vas recevoir 1 à 1 les octets.
Pour cela, tu
fais un appel à recv_buf (ta fonction de buffer de réception intelligent).
Si le recvbuf contient déjà des données, tu renvoies l'octet demandé et tu
le supprimes du buffer.
Si recvbuf est vide, tu fais un appel à recv() pour
le remplir au maximum (cas sockets bloquantes), soit tu renvoies un message
comme quoi le buffer est vide (cas sockets non-bloquantes) et le buffer
sera rempli à la prochaine réception de FD_READ.
a+
>> Pour manipuler le recvbuf tu fournis des fonctions, appelons les
recvbuf_recv et recvbuf_peek par exemple. Ce que je te propose c'est tout
simple. C'est de faire ton programme "comme" si tu recevais tes données 1
octet à la fois depuis la socket, sauf qu'au lieu d'appeler recv() tu
appelles recvbuf_recv.
n'est-ce pas déjà ce que je fais ?
seulement, que faire des octets de données restés non lues dans recvbuf
lorsque la fin d'un bloc a été détectée ?
>> Pour manipuler le recvbuf tu fournis des fonctions, appelons les
recvbuf_recv et recvbuf_peek par exemple. Ce que je te propose c'est tout
simple. C'est de faire ton programme "comme" si tu recevais tes données 1
octet à la fois depuis la socket, sauf qu'au lieu d'appeler recv() tu
appelles recvbuf_recv.
n'est-ce pas déjà ce que je fais ?
seulement, que faire des octets de données restés non lues dans recvbuf
lorsque la fin d'un bloc a été détectée ?
>> Pour manipuler le recvbuf tu fournis des fonctions, appelons les
recvbuf_recv et recvbuf_peek par exemple. Ce que je te propose c'est tout
simple. C'est de faire ton programme "comme" si tu recevais tes données 1
octet à la fois depuis la socket, sauf qu'au lieu d'appeler recv() tu
appelles recvbuf_recv.
n'est-ce pas déjà ce que je fais ?
seulement, que faire des octets de données restés non lues dans recvbuf
lorsque la fin d'un bloc a été détectée ?
>>> Pour manipuler le recvbuf tu fournis des fonctions, appelons les
recvbuf_recv et recvbuf_peek par exemple. Ce que je te propose c'est
tout simple. C'est de faire ton programme "comme" si tu recevais tes
données 1 octet à la fois depuis la socket, sauf qu'au lieu
d'appeler recv() tu appelles recvbuf_recv.
n'est-ce pas déjà ce que je fais ?
seulement, que faire des octets de données restés non lues dans
recvbuf lorsque la fin d'un bloc a été détectée ?
j'ai regardé ça de plus près et décidément, c vraiment ce que je fais
déjà...
et il y a toujours les probèmes de l'allocation de la mémoire pour les
buffers (car les paquets qui arrivent ont une taille inconue), ainsi
que le problème des octets restant.
a moins que je n'ai tjrs pas compris ce que tu me raconte.
>>> Pour manipuler le recvbuf tu fournis des fonctions, appelons les
recvbuf_recv et recvbuf_peek par exemple. Ce que je te propose c'est
tout simple. C'est de faire ton programme "comme" si tu recevais tes
données 1 octet à la fois depuis la socket, sauf qu'au lieu
d'appeler recv() tu appelles recvbuf_recv.
n'est-ce pas déjà ce que je fais ?
seulement, que faire des octets de données restés non lues dans
recvbuf lorsque la fin d'un bloc a été détectée ?
j'ai regardé ça de plus près et décidément, c vraiment ce que je fais
déjà...
et il y a toujours les probèmes de l'allocation de la mémoire pour les
buffers (car les paquets qui arrivent ont une taille inconue), ainsi
que le problème des octets restant.
a moins que je n'ai tjrs pas compris ce que tu me raconte.
>>> Pour manipuler le recvbuf tu fournis des fonctions, appelons les
recvbuf_recv et recvbuf_peek par exemple. Ce que je te propose c'est
tout simple. C'est de faire ton programme "comme" si tu recevais tes
données 1 octet à la fois depuis la socket, sauf qu'au lieu
d'appeler recv() tu appelles recvbuf_recv.
n'est-ce pas déjà ce que je fais ?
seulement, que faire des octets de données restés non lues dans
recvbuf lorsque la fin d'un bloc a été détectée ?
j'ai regardé ça de plus près et décidément, c vraiment ce que je fais
déjà...
et il y a toujours les probèmes de l'allocation de la mémoire pour les
buffers (car les paquets qui arrivent ont une taille inconue), ainsi
que le problème des octets restant.
a moins que je n'ai tjrs pas compris ce que tu me raconte.
Elles y restent. C'est ça qui est bien. Typiquement ton serveur suit une
boucle du genre :
1. attendre des données (ie. recvbuf est agrandi)
2. retrouver le contexte : retrouver quel client a envoyé les données
et où on en est dans le protocole
3. traiter ces données en branchant au bon endroit
Elles y restent. C'est ça qui est bien. Typiquement ton serveur suit une
boucle du genre :
1. attendre des données (ie. recvbuf est agrandi)
2. retrouver le contexte : retrouver quel client a envoyé les données
et où on en est dans le protocole
3. traiter ces données en branchant au bon endroit
Elles y restent. C'est ça qui est bien. Typiquement ton serveur suit une
boucle du genre :
1. attendre des données (ie. recvbuf est agrandi)
2. retrouver le contexte : retrouver quel client a envoyé les données
et où on en est dans le protocole
3. traiter ces données en branchant au bon endroit
ça implique une variable pour la position courante de lecture dans le
recvbuf, et de plus, une variable indiquant que l'on commence une
nouvelle partie.
Non pas obligatoirement.
Pour l'instant tout ce qui t'intéresse est que recvbuf fonctionne
"comme" le buffer de send(). Tu appelles une fonction qui copie des
données là où il faut et qui les vire du buffer. En clair tu ne
travailles pas directement dans le buffer. Ou bien tu peux travailler
dessus et utiliser des fonctions du genre peek ce qui t'évite de passer
ton temps à déplacer la mémoire, mais c'est moins propre (le buffer
recvbuf est un buffer de réception, ça ajoute de la confusion si on s'en
sert pour autre chose).
Maintenant comment le réaliser, une méthode est d'utiliser des realloc et
des memcopy mais elle peut être améliorée en faisant comme tu proposes,
un système de double pointeur début/fin/taille. Là c'est le sujet d'un
autre post si tu veux des explications. A priori il faut utiliser la
méthode la plus simple dans un premier temps, et n'optimiser qu'ensuite
si nécessaire.
Non, tu restes à 3 qui veut dire "entête reçue et message incomplet". au
prochain appel on repart à ce niveau, recv_message() est appelé et disons
que cette fois le .END est lu, alors seulement on passe à 4.
Ensuite on arrive à la fin de la fonction, on est dans un état achevé
donc on repart au début.
L'état passe à 2 qui signifie que l'entête est
incomplète. On essaie de la récupérer :
* S'il n'y a plus rien dans recvbuf, recv_entete() renvoie 0 et on sort
de la fonction
* S'il y a des choses mais pas une entête complète, idem.
* Si c'et bon on passe à l'état 3
... et ainsi de suite.
Ce qu'il faut que tu comprennes c'est le schéma suivant :
* Des données sont placées sur la pile TCP/IP
* Il y a un FD_READ, on remplit le recvbuf au maximum
* Ton programme lit le recvbuf. C'est bien de copier les données (je te
conseille de le faire bien que ce soit pas obligatoire c'est plus
propre). Les octets lus sont enlevé de recvbuf et placés dans un buffer
du contexte.
Un bon buffer a une interface similaire à la suivante:
int buffer_add_data( BUFFER *buf, char *data, size_t len );
---> cette fonction ajoute des données au buffer
size_t buffer_len( BUFFER *buf );
---> cette fonction renvoie la taille du buffer
int buffer_get_data( BUFFER *buf, char *dest, size_t len );
---> cette fonction lit len octets et les copie en dest et les
données sont enlevées du buffer
int buffer_peek_data( BUFFER *buf, char *dest, size_t len );
---> même chose sauf que les données lues ne sont pas enlevées du
buffer
Utiliser ces objets (buffer, etats) a beaucoup d'avantages : d'abord ils
sont génériques, tout le monde sait ce que c'est et ça rend le code plus
lisible. Ensuite le code de buffer est utilisable ailleurs (donc vive le
copier coller). Et tu peux les implémenter comme tu veux ; par exemple
pour optimiser ton programme tu auras juste à changer le code relatif au
buffer et tu laisseras le reste intact (gain de temps). Et enfin le code
est plus clair.
ça implique une variable pour la position courante de lecture dans le
recvbuf, et de plus, une variable indiquant que l'on commence une
nouvelle partie.
Non pas obligatoirement.
Pour l'instant tout ce qui t'intéresse est que recvbuf fonctionne
"comme" le buffer de send(). Tu appelles une fonction qui copie des
données là où il faut et qui les vire du buffer. En clair tu ne
travailles pas directement dans le buffer. Ou bien tu peux travailler
dessus et utiliser des fonctions du genre peek ce qui t'évite de passer
ton temps à déplacer la mémoire, mais c'est moins propre (le buffer
recvbuf est un buffer de réception, ça ajoute de la confusion si on s'en
sert pour autre chose).
Maintenant comment le réaliser, une méthode est d'utiliser des realloc et
des memcopy mais elle peut être améliorée en faisant comme tu proposes,
un système de double pointeur début/fin/taille. Là c'est le sujet d'un
autre post si tu veux des explications. A priori il faut utiliser la
méthode la plus simple dans un premier temps, et n'optimiser qu'ensuite
si nécessaire.
Non, tu restes à 3 qui veut dire "entête reçue et message incomplet". au
prochain appel on repart à ce niveau, recv_message() est appelé et disons
que cette fois le .END est lu, alors seulement on passe à 4.
Ensuite on arrive à la fin de la fonction, on est dans un état achevé
donc on repart au début.
L'état passe à 2 qui signifie que l'entête est
incomplète. On essaie de la récupérer :
* S'il n'y a plus rien dans recvbuf, recv_entete() renvoie 0 et on sort
de la fonction
* S'il y a des choses mais pas une entête complète, idem.
* Si c'et bon on passe à l'état 3
... et ainsi de suite.
Ce qu'il faut que tu comprennes c'est le schéma suivant :
* Des données sont placées sur la pile TCP/IP
* Il y a un FD_READ, on remplit le recvbuf au maximum
* Ton programme lit le recvbuf. C'est bien de copier les données (je te
conseille de le faire bien que ce soit pas obligatoire c'est plus
propre). Les octets lus sont enlevé de recvbuf et placés dans un buffer
du contexte.
Un bon buffer a une interface similaire à la suivante:
int buffer_add_data( BUFFER *buf, char *data, size_t len );
---> cette fonction ajoute des données au buffer
size_t buffer_len( BUFFER *buf );
---> cette fonction renvoie la taille du buffer
int buffer_get_data( BUFFER *buf, char *dest, size_t len );
---> cette fonction lit len octets et les copie en dest et les
données sont enlevées du buffer
int buffer_peek_data( BUFFER *buf, char *dest, size_t len );
---> même chose sauf que les données lues ne sont pas enlevées du
buffer
Utiliser ces objets (buffer, etats) a beaucoup d'avantages : d'abord ils
sont génériques, tout le monde sait ce que c'est et ça rend le code plus
lisible. Ensuite le code de buffer est utilisable ailleurs (donc vive le
copier coller). Et tu peux les implémenter comme tu veux ; par exemple
pour optimiser ton programme tu auras juste à changer le code relatif au
buffer et tu laisseras le reste intact (gain de temps). Et enfin le code
est plus clair.
ça implique une variable pour la position courante de lecture dans le
recvbuf, et de plus, une variable indiquant que l'on commence une
nouvelle partie.
Non pas obligatoirement.
Pour l'instant tout ce qui t'intéresse est que recvbuf fonctionne
"comme" le buffer de send(). Tu appelles une fonction qui copie des
données là où il faut et qui les vire du buffer. En clair tu ne
travailles pas directement dans le buffer. Ou bien tu peux travailler
dessus et utiliser des fonctions du genre peek ce qui t'évite de passer
ton temps à déplacer la mémoire, mais c'est moins propre (le buffer
recvbuf est un buffer de réception, ça ajoute de la confusion si on s'en
sert pour autre chose).
Maintenant comment le réaliser, une méthode est d'utiliser des realloc et
des memcopy mais elle peut être améliorée en faisant comme tu proposes,
un système de double pointeur début/fin/taille. Là c'est le sujet d'un
autre post si tu veux des explications. A priori il faut utiliser la
méthode la plus simple dans un premier temps, et n'optimiser qu'ensuite
si nécessaire.
Non, tu restes à 3 qui veut dire "entête reçue et message incomplet". au
prochain appel on repart à ce niveau, recv_message() est appelé et disons
que cette fois le .END est lu, alors seulement on passe à 4.
Ensuite on arrive à la fin de la fonction, on est dans un état achevé
donc on repart au début.
L'état passe à 2 qui signifie que l'entête est
incomplète. On essaie de la récupérer :
* S'il n'y a plus rien dans recvbuf, recv_entete() renvoie 0 et on sort
de la fonction
* S'il y a des choses mais pas une entête complète, idem.
* Si c'et bon on passe à l'état 3
... et ainsi de suite.
Ce qu'il faut que tu comprennes c'est le schéma suivant :
* Des données sont placées sur la pile TCP/IP
* Il y a un FD_READ, on remplit le recvbuf au maximum
* Ton programme lit le recvbuf. C'est bien de copier les données (je te
conseille de le faire bien que ce soit pas obligatoire c'est plus
propre). Les octets lus sont enlevé de recvbuf et placés dans un buffer
du contexte.
Un bon buffer a une interface similaire à la suivante:
int buffer_add_data( BUFFER *buf, char *data, size_t len );
---> cette fonction ajoute des données au buffer
size_t buffer_len( BUFFER *buf );
---> cette fonction renvoie la taille du buffer
int buffer_get_data( BUFFER *buf, char *dest, size_t len );
---> cette fonction lit len octets et les copie en dest et les
données sont enlevées du buffer
int buffer_peek_data( BUFFER *buf, char *dest, size_t len );
---> même chose sauf que les données lues ne sont pas enlevées du
buffer
Utiliser ces objets (buffer, etats) a beaucoup d'avantages : d'abord ils
sont génériques, tout le monde sait ce que c'est et ça rend le code plus
lisible. Ensuite le code de buffer est utilisable ailleurs (donc vive le
copier coller). Et tu peux les implémenter comme tu veux ; par exemple
pour optimiser ton programme tu auras juste à changer le code relatif au
buffer et tu laisseras le reste intact (gain de temps). Et enfin le code
est plus clair.
> A l'init, on doit se mettre dans l'état de réception de header.
BYTE *pBuf; // !=NULL si on est en train de recevoir un body
BYTE *pRecv; //ptr de reception dans pBuf ou dans Hdr
pBuf à NULL indique qu'on n'est pas en train de recevoir un
body mais bien un header.
if( Context.cbToReceive == 0 )
{
//on a un 'truc' complet; ce 'truc' est soit un header,
//soit un buffer; pour le savoir on teste pBuf
if( pBuf == NULL )
{
}
else
{
}
}
> A l'init, on doit se mettre dans l'état de réception de header.
BYTE *pBuf; // !=NULL si on est en train de recevoir un body
BYTE *pRecv; //ptr de reception dans pBuf ou dans Hdr
pBuf à NULL indique qu'on n'est pas en train de recevoir un
body mais bien un header.
if( Context.cbToReceive == 0 )
{
//on a un 'truc' complet; ce 'truc' est soit un header,
//soit un buffer; pour le savoir on teste pBuf
if( pBuf == NULL )
{
}
else
{
}
}
> A l'init, on doit se mettre dans l'état de réception de header.
BYTE *pBuf; // !=NULL si on est en train de recevoir un body
BYTE *pRecv; //ptr de reception dans pBuf ou dans Hdr
pBuf à NULL indique qu'on n'est pas en train de recevoir un
body mais bien un header.
if( Context.cbToReceive == 0 )
{
//on a un 'truc' complet; ce 'truc' est soit un header,
//soit un buffer; pour le savoir on teste pBuf
if( pBuf == NULL )
{
}
else
{
}
}