Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

fgets et fprintf avec un flux TCP sous Irix

2 réponses
Avatar
Khanh-Dang
Bonjour,

J'essaye de compiler un programme écrit en C. Ce programme est concu
pour des systèmes UNIX. Il compile et fonctionne correctement avec les
systèmes GNU (avec la glibc donc). J'ai décidé de compiler ce même
programme sous Irix (version 6.5), l'UNIX propriétaire de SGI.

Le programme est un client qui se connecte via TCP/IP à un serveur, le
protocole utilisé au-dessus de TCP est un protocole texte asynchrone
(comme le protocole utilisé pour IRC par exemple).

Voici la structure du code qui pose problème, les commentaires en
francais étant de moi. J'ai enlevé le code de gestion d'erreur pour plus
de lisibilité.

| /* sd est un socket-descriptor */
| fd = fdopen(sd, "r+");
|
| /* ici, il y a quelques send() et recv() sur sd */
| send(sd, "foo...", foosize, 0);
|
| while(fgets(buf, sizeof(buf)-1, fd)!=NULL)
| {
| /* snip traitement des caractères recus */
| /* on envoie ensuite une réponse (construite dans buf) au serveur : */
| snprintf(buf, sizeof(buf)-1, "blabla", ...);
| fwrite(buf, strlen(buf), 1, fd);
|
| /* Je ne sais pas pourquoi mais fprintf est ici utilisé (1) */
| fprintf(fd, "autre blabla", ...);
| }
|
| /* Ici aussi il y a des send() et des recv() sur sd */
| send(sd, ...);


Je n'ai jamais pratiqué le C, tout ce que je connais est sa syntaxe.
J'ai donc deux questions de béotiens à vous soumettre.

1) Tout d'abord, y a-t-il un intérêt à utiliser fwrite() et consorts
(fonctions qui manipulent un flux) par rapport à send() et recv() dans
le cas d'une connection TCP ? Je n'en vois pas vraiment l'intérêt ici.
Ca pose même des problèmes comme je le raconterai plus bas.

2) Le fprintf() qui suit le commentaire numéroté (1) donne un joli
segfault sous IRIX. J'ai trouvé le problème en RTFMant.

Dans la page man fopen(3S) de Irix, je lis :
| When a file is opened for update (when + appears as the second (or
| third, in the case of fopen or freopen) character of type) both input
| and output may be done on the resulting stream. However, output may
| not be directly followed by input without an intervening fseek,
| fsetpos, or rewind. Similarly, input may not be directly followed by
| output without an intervening call to one of these functions, unless
| the input operation left the file positioned at end-of-file. (See
| note under BUGS below.)
Plus bas :
| BUGS
| When operating on a file opened for update on which the last operation
| was output, an input operation may be performed if there is an
| intervening call to a file positioning function. An input operation
| should also be possible under these circumstances if an intervening
| call is made to fflush. If this sequence of operations (i.e., output,
| fflush, input) is performed, however, the input operation fails with
| the misleading error EBADF.

Donc, il faut repositionner le pointeur de flux entre lecture et
écriture. D'ailleurs, c'est apparemment obligatoire d'après la norme
ANSI. D'après la page man fopen(3) sous GNU/Linux :
| Reads and writes may be intermixed on read/write streams in any
| order. Note that ANSI C requires that a file positioning function
| intervene between output and input, unless an input operation
| encounters end-of- file.

Bon, j'aurais au moins appris une chose.

Pour que ca fonctionne sous IRIX, et que ca soit plus portable, j'ai
rapidement rajouté des rewind(fd) avant chaque fgets(). En fait,
certainement à cause de quelques lacunes au niveau des flux, je me
comprends pas très bien. Quel est le sens d'un rewind() pour un flux
TCP ; même question pour fseek(). Pour un fichier, on comprend
aisément, puisqu'on peut représenter ca comme une tête de lecture d'un
magnétophone. Mais pour un flux TCP ?


Je vous remercie de m'avoir accordé quelques secondes :-)

2 réponses

Avatar
Yves ROMAN
"Khanh-Dang" a écrit dans le message de news:
42b80ba0$0$1237$
Bonjour,

J'essaye de compiler un programme écrit en C. Ce programme est concu
pour des systèmes UNIX. Il compile et fonctionne correctement avec les
systèmes GNU (avec la glibc donc). J'ai décidé de compiler ce même
programme sous Irix (version 6.5), l'UNIX propriétaire de SGI.

Le programme est un client qui se connecte via TCP/IP à un serveur, le
protocole utilisé au-dessus de TCP est un protocole texte asynchrone
(comme le protocole utilisé pour IRC par exemple).

Voici la structure du code qui pose problème, les commentaires en
francais étant de moi. J'ai enlevé le code de gestion d'erreur pour plus
de lisibilité.

| /* sd est un socket-descriptor */
| fd = fdopen(sd, "r+");
|
| /* ici, il y a quelques send() et recv() sur sd */
| send(sd, "foo...", foosize, 0);
|


Par précaution, j'aurais évité de les faire après le fdopen
Le FILE * est bufferisé, et pas les send()/recv() me semble-t-il
De même, après tous les fgets(), fwrite() j'aurai rajouté un fflush() avant
de reprendre les send()

| while(fgets(buf, sizeof(buf)-1, fd)!=NULL)


Avec des sockets, sur quoi va s'arrêter fgets() ? taille max atteinte ou fin
de paquet reçu ?...

| {
| /* snip traitement des caractères recus */
| /* on envoie ensuite une réponse (construite dans buf) au serveur : */
| snprintf(buf, sizeof(buf)-1, "blabla", ...);
| fwrite(buf, strlen(buf), 1, fd);
|
| /* Je ne sais pas pourquoi mais fprintf est ici utilisé (1) */
| fprintf(fd, "autre blabla", ...);
| }
|
| /* Ici aussi il y a des send() et des recv() sur sd */
| send(sd, ...);


Je n'ai jamais pratiqué le C, tout ce que je connais est sa syntaxe.
J'ai donc deux questions de béotiens à vous soumettre.

1) Tout d'abord, y a-t-il un intérêt à utiliser fwrite() et consorts
(fonctions qui manipulent un flux) par rapport à send() et recv() dans
le cas d'une connection TCP ? Je n'en vois pas vraiment l'intérêt ici.
Ca pose même des problèmes comme je le raconterai plus bas.


Je ne vois effectivement pas l'utilité.
On aurait pu croire que l'intérêt était de faire des fprintf(), mais on voit
qu'il fait des snprintf(), puis des frwite() !

Je doute même que ça puisse fonctionner avec des sockets,comme tu le
remarque ci-dessous, mais je n'ai jamais osé l'essayer.
Je te conseillerait vraiment de convertir ça en send()/recv(). Mixer les 2
me parait curieux
Ca m'étonne même que ça fonctionne avec GNU.


2) Le fprintf() qui suit le commentaire numéroté (1) donne un joli
segfault sous IRIX. J'ai trouvé le problème en RTFMant.

Dans la page man fopen(3S) de Irix, je lis :
| When a file is opened for update (when + appears as the second (or
| third, in the case of fopen or freopen) character of type) both input
| and output may be done on the resulting stream. However, output may
| not be directly followed by input without an intervening fseek,
| fsetpos, or rewind. Similarly, input may not be directly followed by
| output without an intervening call to one of these functions, unless
| the input operation left the file positioned at end-of-file. (See
| note under BUGS below.)
Plus bas :
| BUGS
| When operating on a file opened for update on which the last operation
| was output, an input operation may be performed if there is an
| intervening call to a file positioning function. An input operation
| should also be possible under these circumstances if an intervening
| call is made to fflush. If this sequence of operations (i.e., output,
| fflush, input) is performed, however, the input operation fails with
| the misleading error EBADF.



Je pense plutôt que les fonctions devraient signaler une erreur au retour,
plutôt qu'un SEGV.
Ou alors la libc IRIX n'a vraiment pas envisagé qu'on puisse faire un
fprintf() sur des sockets !

Ca ressemble plus à un problème classique de printf : incohérence entre
format et arguments passés.
C'est peut-être un problème de taille de données différentes entre les 2
environnements, endianness (mais tu dis que les données sont textuelles),
etc...
L'autre côté de la connexion est-il sur la même machine ou sur une autre
avec un OS différent ?
Pour vérifier rajoute un printf() identique au fprintf() et regarde si ça
plante sur le printf()

Donc, il faut repositionner le pointeur de flux entre lecture et
écriture. D'ailleurs, c'est apparemment obligatoire d'après la norme
ANSI. D'après la page man fopen(3) sous GNU/Linux :
| Reads and writes may be intermixed on read/write streams in any
| order. Note that ANSI C requires that a file positioning function
| intervene between output and input, unless an input operation
| encounters end-of- file.

Bon, j'aurais au moins appris une chose.

Pour que ca fonctionne sous IRIX, et que ca soit plus portable, j'ai
rapidement rajouté des rewind(fd) avant chaque fgets(). En fait,
certainement à cause de quelques lacunes au niveau des flux, je me
comprends pas très bien. Quel est le sens d'un rewind() pour un flux
TCP ; même question pour fseek(). Pour un fichier, on comprend
aisément, puisqu'on peut représenter ca comme une tête de lecture d'un
magnétophone. Mais pour un flux TCP ?

Aucune idée.

Essaye avec fseek() au lieu de rewind() et regarde s'il signale une erreur
et laquelle

Avatar
Antoine Leca
En <news:d99cr4$r2m$, Yves ROMAN va escriure:
while(fgets(buf, sizeof(buf)-1, fd)!=NULL)



Avec des sockets, sur quoi va s'arrêter fgets() ? taille max atteinte
ou fin de paquet reçu ?...


Probablement sur le n du protocole, non ?

Si j'ai bien suivi, les packets sont en-dessous, ils sont invisibles à
travers le mécanisme de tampon (sauf que si l'on dépasse le timeout, TCP va
faire comme si c'était une fin de fichier, avec tous les problèmes de fins
de fichier « virtuelles »).

Donc fgets() est une protection contre les dépassements de tampon réception
(buf ici), et c'est le protocole orienté texte qui dirige.

Pour que ca fonctionne sous IRIX, et que ca soit plus portable, j'ai
rapidement rajouté des rewind(fd) avant chaque fgets().



J'aurais probablement essayé fseek(fd, 0L, SEEK_CUR) à la place, mais cela
n'a probablement pas beaucoup d'importance dans la pratique (voire des
implémentations boguéss pourraient détecter le cas « idiot » et sortir
_avant_ de remettre à zéro le « sens »...)


Antoine