jeudi, mai 28, 2009

sendall

J'ai toujours été intrigué par cette fonction "sendall" du Beej Network Programming Guide. Selon Beej, un appel à send pour transmettre des données pourrait ne pas nécessairement envoyer autant de données que prévu. J'ai toujours trouvé ça un peu surprenant et je n'avais personnellement jamais été témoins d'une telle chose jusqu'à aujourd'hui.

Corrigeant les TP de programmation de BitTorrent, j'ai un strace ouvert aussi sur le client initial dans un "environnement de test contrôlé" et voilà que j'aperçois la séquence suivante:

send(10, "\0\0\200\t\7\0\0\0(\0\0\0\0\2619\r\222\265\315r\264a\363"..., 32781, 0) = 32781
send(10, "\0\0\200\t\7\0\0\0(\0\1\0\0\340\306\335\246\17\r\377\265"..., 32781, 0) = 32781
send(10, "\0\0\200\t\7\0\0\0(\0\2\0\0\233\257\314\25P\322\2363\316"..., 32781, 0) = 32781
send(10, "\0\0\200\t\7\0\0\0(\0\3\0\0002\235\311\36yyzt\322\363$"..., 32781, 0) = 28790
send(10, "UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU"..., 3991, 0) = 3991


Le programme tente d'envoyer des données de 32K accompagnées de 13 bytes de contrôle. Le plus souvent, celà signifie qu'un buffer de 32781 bytes est passé à l'appel-système send (cf. le 3eme argument) et que 32781 bytes ont effectivement été envoyés (cf. la valeur de retour, après le '='). Sauf pour les deux derniers, où si on a bien 32K+7 soumis, seuls 28790 bytes ont pu effectivement être "envoyés" ... Je devrais plutôt dire "pris en charge par le protocole TCP". Effectivement, TCP a sa propre mémoire-tampon dans laquelle il conserve les données à envoyer, mais elle n'est pas infinie. Si a un moment donné elle "déborde", TCP prend le plus de données possibles mais signale à l'application qu'une partie n'a pas pu être traitée.

On aurait pu préférer une approche "l'appel va être bloquant jusqu'à ce que tous les bytes soumis soient traités". Bin ce n'est pas forcément le cas, et ce n'est pas plus mal: l'application a peut-être quelque-chose de plus utile à faire avant de ré-envoyer le reste.

Comme j'ai de temps en temps des fichiers qui "passent mal" avec les transfers wifi de runme, je regarderai à l'occasion si je ne ferais pas bien de rajouter un "sendall" ici ou là.

PS: au passage, Beej a finalement fait une version "bouquin" de son superbe tutoriel.

1 commentaire:

DevMusings a dit…

Et pour ceux qui aiment les appels sytèmes low level, je signale que write() a exactement le même problème. Il faut écrire et utiliser un writeAll() si on veut être certain que toutes les données sont bien écrites.