OVH Cloud OVH Cloud

Sockets : Commend savoir qu'un send() a été consommé

13 réponses
Avatar
Marc
Bonjour,

J'ai besoin de transférer des gros blocs par sockets, donc je suis obligé de
"tronçonner" en plusieurs messages.
Mon problème est que dès le 2ème appel à send(), l'appli de bloque car le
recepteur n'a pas fini le traitement du 1er send().
Le problème disparait si je mets une tempo après chaque send.

Est-ce qu'il est possible de savoir, après avoir fait un send() si le
recepteur l'a consommé et s'il est prêt à en recevoir un nouveau ?
Pour des raisons de performance et de simplicité, je voudrais éviter de
devoir faire un recv() entre 2 send() même si ça peut résoudre le problème
de synchro.

Merci de votre aide.

10 réponses

1 2
Avatar
Dominique Vaufreydaz
Bonjour,

J'ai besoin de transférer des gros blocs par sockets, donc je suis obligé de "tronçonner" en plusieurs messages.
Mon problème est que dès le 2ème appel à send(), l'appli de bloque car le recepteur n'a pas fini le traitement du 1er send().



Euh, comprends pas bien la...

Le problème disparait si je mets une tempo après chaque send.
Est-ce qu'il est possible de savoir, après avoir fait un send() si le recepteur l'a consommé et s'il est prêt à en recevoir un
nouveau ?



Nan, c'est du niveau du protocole...

Pour des raisons de performance et de simplicité, je voudrais éviter de devoir faire un recv() entre 2 send() même si ça peut
résoudre le problème de synchro.



Ca change rien de faire un recv sur l'emetteur... Pourrait-on :
- savoir ce que tu veux faire
- avoir un extrait de code
- as-tu regardé du coté des sockets non bloquantes ?

Merci. Doms.
Avatar
Marc
Je reformule ma question : pour transferer des gros messages (plusieurs Mo)
j'ai besoin de les découper en plusieurs blocs, dont la taille est celui de
mon buffer emission/réception socket (pour l'intant 8ko).
Chaque bloc a une entête : No bloc courant, Nb total blocs, taille du bloc
courant, taille du message complet, ce qui permet de les réassembler sans
problème.
Ma question est : pour envoyer le message complet, est-il possible de faire
uniquement une succession de send() pour chaque bloc, ou alors un recv()
entre l'émission de 2 blocs est nécessaires pour être sûr que le recepteur
est prêt à recevoir le bloc suivant ?
Je pose la question parce que j'ai déjà vu la même chose sur un analyseur de
protocole, et la synchro entre 2 blocs était faite uniquement au niveau tcp
et pas au niveau applicatif, et le débit était proche du maximum que permet
Ethernet (c'était pour un transfert de blob d'une base de données).

Je ne pense pas que le mode opératoire des sockets (bloquant ou pas) permet
de savoir si le récepteur est prêt à recevoir ou en cours de traitement.
Avatar
Dominique Vaufreydaz
Bonjour,

"Marc" a écrit dans le message de news: 454af0ea$0$5089$
Je reformule ma question : pour transferer des gros messages (plusieurs Mo) j'ai besoin de les découper en plusieurs blocs, dont
la taille est celui de mon buffer emission/réception socket (pour l'intant 8ko).



Tu perux monter plus haut que ca, mais meme si ca a des consequence au
niveau reseau. Tu peux te documenter sur ce genre de données sur le Web
ou dans un bouquin.

Chaque bloc a une entête : No bloc courant, Nb total blocs, taille du bloc courant, taille du message complet, ce qui permet de
les réassembler sans problème.



Notons que ca n'a pas forcement d'interet. Si tu fais du TCP, tu sais que tout
arrive et ta suite de message est un stream. Donc tu fais une entete pour le bloc
complet et tu l'envois en plusieurs appels a send... Bien sur si tu fais de l'UDP
alors, il faut faire pour pouvoir assembler dans l'ordre et voir s'il y a eu
perte.

Ma question est : pour envoyer le message complet, est-il possible de faire uniquement une succession de send() pour chaque
bloc, ou alors un recv() entre l'émission de 2 blocs est nécessaires pour être sûr que le recepteur est prêt à recevoir le bloc
suivant ?



Perso, je ne fais jamais de recv sur l'expediteur, je ne vois pas bien a quoi
ca sert d'ailleurs... jamais vu de code qui faisait de recv... D'ailleurs, ton recv
va etre bloquant. Seul interet de faire recv c'est si tu fait de la validation
de reception de paquet que tu attendais. Encore une fois, je n'ai jamais
vu de code faisant un recv pour faire de la synchro TCP (j'ai pas dit que
ca se faisait pas, mais a alors c'est bien caché !).

Sinon, dans TCP, il ne faut pas oublié notre ami NAGGLE. C'est un algo
qui essaye de regrouper les paquet TCP avant de les envoyer sur le réseau
(pour des questions de performances). Tu peux essayer de le desactiver
pour voir ce qu'il se passe (perso je le fait car cela me penalisait). Voici
un extrait de mon code:
bool Socket::SetTcpNoDelay(bool Set)
{
if ( descriptor == SOCKET_ERROR || socketType != TCP )
{
return false;
}
int OptVal;
if ( Set == true )
{
OptVal = 1;
}
else
{
OptVal = 0;
}

#ifdef WIN32
if ( setsockopt(descriptor, IPPROTO_TCP, TCP_NODELAY, (char*)&OptVal, sizeof(OptVal)) < 0 )
{
OmiscidError("setsockopt: could not set TCP nodelayn");
return false;
}
return true;
#else
struct protoent *p;
p = getprotobyname("tcp");
if ( p && setsockopt(descriptor, p->p_proto, TCP_NODELAY, &OptVal, sizeof(OptVal)) < 0)
{
OmiscidError("setsockopt: could not set TCP nodelayn");
return false;
}
return true;
#endif
}

Pour des precision sur tout cas, tu peux regarder :
- http://en.wikipedia.org/wiki/Nagle's_algorithm et aussi le lien en bas pour
l'explication de pourquoi c'est pas toujours optimal
- un cours réseaux ici http://www.pakscot.org/formsys/Ftp/cours_reseau.pdf

Sinon, sous Windows on peut aussi le desactiver au niveau systeme :
http://support.microsoft.com/kb/235624/fr

Je pose la question parce que j'ai déjà vu la même chose sur un analyseur de protocole, et la synchro entre 2 blocs était faite
uniquement au niveau tcp et pas au niveau applicatif, et le débit était proche du maximum que permet Ethernet (c'était pour un
transfert de blob d'une base de données).



C'est a dire ? Il faut bien penser que quand on dit 100 MB c'est loin d'etre
100 MB utiles ! Y'a tous les protocoles (ethernet puis IP et enfin
TCP) qui ajoute entete + padding... il n'y a qu'a regarder avec Ethereal
pour s'en convaincre...

Je ne pense pas que le mode opératoire des sockets (bloquant ou pas) permet de savoir si le récepteur est prêt à recevoir ou en
cours de traitement.



Non, mais tu ne serait pas bloque, et tu pourrais faire autre chose... Puis
revenir faire ton send. Ca se fait dans certains cas.

Doms.
Avatar
Marc
>> la taille est celui de mon buffer emission/réception socket (pour
l'intant 8ko).



Tu perux monter plus haut que ca, mais meme si ca a des consequence au
niveau reseau. Tu peux te documenter sur ce genre de données sur le Web
ou dans un bouquin.



Comme il peut y avoir plusieurs centaines de connexions simultanément, je
dois rester mesuré pour les ressources consommées par chaque socket.

Notons que ca n'a pas forcement d'interet. Si tu fais du TCP, tu sais
que tout
arrive et ta suite de message est un stream. Donc tu fais une entete
pour le bloc
complet et tu l'envois en plusieurs appels a send... Bien sur si tu
fais de l'UDP
alors, il faut faire pour pouvoir assembler dans l'ordre et voir s'il y
a eu
perte.



C'est bien du TCP. Sans entête, je ne comprends pas comment le recepteur
peut faire la distinction à l'arriver d'un nouveau bloc entre la suite d'un
gros message et un nouveau message.


Sinon, dans TCP, il ne faut pas oublié notre ami NAGGLE. C'est un algo
qui essaye de regrouper les paquet TCP avant de les envoyer sur le
réseau
(pour des questions de performances). Tu peux essayer de le desactiver
pour voir ce qu'il se passe (perso je le fait car cela me penalisait).
Voici
un extrait de mon code:



Bien que je ne connaisse pas l'ordre de grandeur, mon but est que les blocs
soient suffisamment grands pour éviter ce genre de problème.

> C'est a dire ? Il faut bien penser que quand on dit 100 MB c'est loin
d'etre
100 MB utiles ! Y'a tous les protocoles (ethernet puis IP et enfin
TCP) qui ajoute entete + padding... il n'y a qu'a regarder avec
Ethereal
pour s'en convaincre...



Tout à fait, c'est le test que j'ai fait. Quand on arrive à 50mb de débit
de données utiles sur une ligne à 100mb, on est content. C'est mon objectif.

Je ne pense pas que le mode opératoire des sockets (bloquant ou pas)
permet de savoir si le récepteur est prêt à recevoir ou en cours de
traitement.



Non, mais tu ne serait pas bloque, et tu pourrais faire autre chose...
Puis
revenir faire ton send. Ca se fait dans certains cas.



Pour l'instant j'ai fait le choix de socket bloquant, avec un thread par
socket. L'idéal serait sans doute la complétion de port, mais j'ai encore du
chemin à faire.
Avatar
Dominique Vaufreydaz
Bonjour,

C'est bien du TCP. Sans entête, je ne comprends pas comment le recepteur peut faire la distinction à l'arriver d'un nouveau bloc
entre la suite d'un gros message et un nouveau message.



Simplement parceque si ton message ne commence pas par une nouvelle
entete, c'est la suite du message precedent ou au pire un erreur dans l'envoi !

Nous noterons qu'en TCP, c'est un stream. Si tu envois 2 petits paquets,
tu peux n'en recevoir qu'un ! Seul UDP garantit 1 envoi, 1 reception.
Tu dois meme pouvoir avoir le cas (+ rare) de 1 gros paquet, pas tout
recu d'un coup...

Donc tu envois qqchose du genre:
<bloc taille = "12000"> + 4 envois de 3000 octets... Mais tu peux tout
recevoir d'un coup ! un recv en TCP recoit n octets max mais pas forcement
d'1 seul message ! C'est une erreur courante dans certains programme qui
marche bien si on le test de la meme machine a elle-meme mais des qu'on
envoie a distance, ca marche plus...

Bien que je ne connaisse pas l'ordre de grandeur, mon but est que les blocs soient suffisamment grands pour éviter ce genre de
problème.



Il faut faire une estimation. Y'a des indicateurs... Regarde le cours
que je t'avais donné...

Tout à fait, c'est le test que j'ai fait. Quand on arrive à 50mb de débit de données utiles sur une ligne à 100mb, on est
content. C'est mon objectif.



Ouai, en considerant qu'il n'y ait pas d'autres equipement sur le réseau.
Attention au test fait en reseau neutre (ou rien d'autre ne se passe) et
ou l'on regle une taille de paquet qui va bien (puisque pratiquement pas
de collision, sinon un paquet SMB qui passe de temps en temps) et
hop quand on passe dans la vraie vie avec un vrai reseau via Internet
au milieu, c'est plus du tout optimum...

Pour l'instant j'ai fait le choix de socket bloquant, avec un thread par socket. L'idéal serait sans doute la complétion de
port, mais j'ai encore du chemin à faire.



Meme choix pour moi. Doms.
Avatar
Dominique Vaufreydaz
Bonjour,

si tu veux avoir une bonne idée du débit que tu obtiens avec différentes tailles
de paquet, y'a un outils qui se nomme TTCP qui fait ca de memoire a memoire
avec transfert de données entre une applie cliente et une applie serveur.

Doms.
Avatar
Marc
Merci pour toutes ces explications.
Je me demande si la commande TransmitFile avec l'option en mémoire ne
pourrait pas résoudre mon problème de tranfert, je vais approfondir.
Avatar
Dominique Vaufreydaz
Bonjour,

"Marc" a écrit dans le message de news: 454b1f56$0$27394$
Merci pour toutes ces explications.
Je me demande si la commande TransmitFile avec l'option en mémoire ne pourrait pas résoudre mon problème de tranfert, je vais
approfondir.



Encore une fois, la question est qu'est-ce que tu veux faire ? C'est sur
la meme machine ? Non, puisque tu parles de reseaux. Seule une reponse
precise a cette question ainsi qu'aux impératifs qui en decoulent pourront
te guider dans tes choix.

Doms.
Avatar
Dominique Vaufreydaz
Bonjour,

"Marc" a écrit dans le message de news: 454b1f56$0$27394$
Merci pour toutes ces explications.
Je me demande si la commande TransmitFile avec l'option en mémoire ne pourrait pas résoudre mon problème de tranfert, je vais
approfondir.



Sinon, si tu veux regarder un exemple de qqchose qui envoie via TCP des paquets
et qui vérifie que c'est unitaire, tu peux regarder par exemple la classe de notre
Middleware OMiSCID (http://omiscid.gforge.inria.fr/), dans la couche 0
(abstraction C++ du systeme) il y a une classe Socket et une classe MsgSocket
dans le repertoire Com (layer 1).

Par contre, nous n'avons pas les memes impératifs a priori, et donc
la facon dont c'est programmé ne te sera pas forcement utile. C'est
juste que ca envoie des paquets avec une entete au debut.

Doms.
Avatar
Marc
> Encore une fois, la question est qu'est-ce que tu veux faire ? C'est
sur
la meme machine ? Non, puisque tu parles de reseaux. Seule une reponse
precise a cette question ainsi qu'aux impératifs qui en decoulent
pourront
te guider dans tes choix.



Un middleware
- Potentiellement plusieurs centaines de clients
- Le temps de réponse doit être le plus court possible
- Les messages peuvent aussi bien contenir quelques octets ou quelques Mo
(besoin de transporter des images)
- Doit être bidirectionnel asynchrone : pas uniquement question/réponse, le
serveur peut aussi décider d'envoyer un message à un client sans requête
préalable de celui-ci.
- L'application doit pouvoir s'installer sur une machine de faible puissance
(ram, cpu) s'il y a peu de clients à servir.
1 2