socket et read

Le
unbewusst.sein
je m'essaie un petit programme envoyant une commande à un serveur de
nouvelles et lisant la réponse, la partie intéressée :

cmd = "LIST NEWSGROUPS";
send(s, cmd, strlen(cmd), 0);
// s est le socket
while ((bytes = read(s, buf, BUFSIZ)) > 0)
write(1, buf, bytes);

// hanging.
fflush(stdout);

fprintf(stdout, "Ready to close connection to news.free.fr");

close(s);
return 0;


apparemment, si j'ai bien compris, ça "hang" avant "flush(stdout);" car
je ne sorts pas de la boucle while.

le serveur lui a pourtant répondu correctement et a terminé par une
ligne avec simplement un point "." :

alt.irc.dalnet The DALnet IRC network.
.

^C

et je suis obligé d'arrêter par ctrl C.

donc, j'imagine que je dois rompre la boucle while si la ligne contient
bien cet unique caractère (avec CRLF?).

du coup je me pose la question de savoir que fait exactement read ?

(sur mac os x le man n'en dit pas grand chose si ce n'est que c'est une
commande 'buitin')

ça lit une ligne ? (ce que je suppose)

en fait si je teste buf à '.' ça coupe trop vite la connection :

while ((bytes = read(s, buf, BUFSIZ)) > 0 && *buf != '.')
write(1, buf, bytes);

fflush(stdout);

fprintf(stderr, "Ready to close connection to news.free.fr");

la dernière ligne au terminal étant :
altReady to close connection to news.free.fr
^ un "." et la suite
manquent.


ça signifie que ma comparaison :

*buf != '.'

n'est pas bonne

que je dois comparer le premier caractère de buf à . et le suivant à
?

comment faire ?

mon buf est déclaré ainsi :
char buf[BUFSIZ+1];

--
« La vie ne se comprend que par un retour en arrière,
mais on ne la vit qu'en avant. »
(Sören Kierkegaard)
Vidéos High-Tech et Jeu Vidéo
Téléchargements
Vos réponses Page 1 / 2
Gagnez chaque mois un abonnement Premium avec GNT : Inscrivez-vous !
Trier par : date / pertinence
unbewusst.sein
Le #22251611
Une Bévue
que je dois comparer le premier caractère de buf à . et le suivant à n
?



finalement j'ai changé mon code pour :
while ((bytes = read(s, buf, BUFSIZ)) > 0) {
if(*buf == '.' && buf[1] == 'n') {
break;
}
write(1, buf, bytes);
}

et ça hang toujours là je pense que le "caractère" (à supposé qu'il n'y
en ait qu'un) de fin de ligne, n'est pas 'n'.

--
« La vie ne se comprend que par un retour en arrière,
mais on ne la vit qu'en avant. »
(Sören Kierkegaard)
Éric Lévénez
Le #22251671
Le 13/06/10 18:09, Une Bévue a écrit :

et ça hang toujours



Normal. Tu fais beaucoup trop de suppositions. Si tu veux programmer en
réseau en C, il faut que tu lises un livre de base : "Internetworking
with TCP/IP".

L'appel système read est par défaut bloquant. Il retourne 0 sur
déconnexion du distant. Pour éviter d'être bloqué on effectue
généralement un select avant l'appel système. Read ne connaît pas la
notion de ligne, il lit un flux de données et retourne les octets qu'il
a réussi à lire. Tester le premier mot du buffer ne teste pas le premier
mot d'une ligne, sauf hasard.

Tout est dans le bouquin. Bonne lecture.

--
Éric Lévénez
FAQ de fclc :
Samuel DEVULDER
Le #22251711
Une Bévue a écrit :
je m'essaie un petit programme envoyant une commande à un serveur de
nouvelles et lisant la réponse, la partie intéressée :

cmd = "LIST NEWSGROUPSn";
send(s, cmd, strlen(cmd), 0);
// s est le socket
while ((bytes = read(s, buf, BUFSIZ)) > 0)
write(1, buf, bytes);

// hanging....
fflush(stdout);



C'est pourri ca. Tu mélange les écriture sur write() et sur stdout. Il
ne faut pas faire ainsi. stdout a un buffer que tu bypass en utilisant
un write(1, ...). Il ne faut pas s'etonner si tu vois des trucs affichés
dans le mauvais ordre en procédant ainsi.

sois homogène dans tes API => utilise fwrite plutot.

fprintf(stdout, "Ready to close connection to news.free.frn");

close(s);
return 0;


apparemment, si j'ai bien compris, ça "hang" avant "flush(stdout);" car
je ne sorts pas de la boucle while.

le serveur lui a pourtant répondu correctement et a terminé par une
ligne avec simplement un point "." :

alt.irc.dalnet The DALnet IRC network.
.

^C

et je suis obligé d'arrêter par ctrl C.

donc, j'imagine que je dois rompre la boucle while si la ligne contient
bien cet unique caractère (avec CRLF?).

du coup je me pose la question de savoir que fait exactement read ?

(sur mac os x le man n'en dit pas grand chose si ce n'est que c'est une
commande 'buitin')

ça lit une ligne ? (ce que je suppose)



non. Ca lit au plus le nombre d'élements passés en argument ou jusqu'à
la fin de fichier. Ca peut donc lire une ligne, mais 2 lignes ou plus
aussi. Ca dépend. Ca peut aussi en lire moins (le début d'une ligne ou
pas). Ca depend de comment la couche reseau en dessous découple les
elements. (je connais des couches reseau qui découpent tout en 64bits,
donc on obtient jammais une ligne de 80 car d'un coup).

en fait si je teste buf à '.' ça coupe trop vite la connection :

while ((bytes = read(s, buf, BUFSIZ)) > 0 && *buf != '.')
write(1, buf, bytes);



C'est mieux.


fflush(stdout);

fprintf(stderr, "Ready to close connection to news.free.frn");

la dernière ligne au terminal étant :
altReady to close connection to news.free.fr
---^------------------------------------------ un "." et la suite
manquent.



C'est normal... regarde ton while(). Il lit le '.' mais ne l'imprime pas.


ça signifie que ma comparaison :

*buf != '.'

n'est pas bonne



Sisi.. c'est juste que tu oublie d'imprimer le '.' que tu viens de lire.

while((bytes = read(s, buff, BUFSIZ)) > 0) {
fwrite(buf, sizeof(*buf), BUFSIZ, stdout);
if(*buf == '.') break;
}

sam.
xtof.pernod
Le #22251701
Le 13/06/2010 18:09, Une Bévue a fait rien qu'à écrire :
Une Bévue
while ((bytes = read(s, buf, BUFSIZ))> 0) {
if(*buf == '.'&& buf[1] == 'n') {
break;
}
write(1, buf, bytes);
}



(En supposant que tu discutes avec un serveur nntp:) le protocole
spécifie que la fin de liste est '.', 'r', 'n' (point, CR, LF).

La boucle ne risque donc pas d'être cassée.

De plus, rien ne garantit que le '.' arrivera en 1ere position, ça
dépend du serveur, de tcp, du système utilisé, etc.

Si tu veux lire par ligne, tu peux utiliser fdopen(3) et fgets(3)
sur la sockette.

et ça hang toujours là je pense que le "caractère" (à supposé qu'il n'y
en ait qu'un) de fin de ligne, n'est pas 'n'.



Sisi.


--
christophe.
unbewusst.sein
Le #22251841
Éric Lévénez
Tester le premier mot du buffer ne teste pas le premier
mot d'une ligne, sauf hasard.



oui, c'est bien ce qui se passe.
--
« La vie ne se comprend que par un retour en arrière,
mais on ne la vit qu'en avant. »
(Sören Kierkegaard)
unbewusst.sein
Le #22251851
xtof.pernod
(En supposant que tu discutes avec un serveur nntp:) le protocole
spécifie que la fin de liste est '.', 'r', 'n' (point, CR, LF).

La boucle ne risque donc pas d'être cassée.



oui, c'est bien ".rn", je l'ai appris entretemps.

De plus, rien ne garantit que le '.' arrivera en 1ere position, ça
dépend du serveur, de tcp, du système utilisé, etc.



non, effectivement, c'est +/- aléatoire...

Si tu veux lire par ligne, tu peux utiliser fdopen(3) et fgets(3)
sur la sockette.



ok, merci.
--
« La vie ne se comprend que par un retour en arrière,
mais on ne la vit qu'en avant. »
(Sören Kierkegaard)
unbewusst.sein
Le #22251861
Samuel DEVULDER wrote:

while((bytes = read(s, buff, BUFSIZ)) > 0) {
fwrite(buf, sizeof(*buf), BUFSIZ, stdout);
if(*buf == '.') break;
}



ok, mais bon le problème est plus "grave" rien ne dir que le "." est en
début de buf donc mon test :

if(buf[0] == '.' && buf[1] == 'r' && buf[2] == 'n') {
break;
}

intervient +/- aléatoirement.

--
« La vie ne se comprend que par un retour en arrière,
mais on ne la vit qu'en avant. »
(Sören Kierkegaard)
unbewusst.sein
Le #22252071
xtof.pernod
Si tu veux lire par ligne, tu peux utiliser fdopen(3) et fgets(3)
sur la sockette.



ok, là j'arrête au "bon" moment.

--
« La vie ne se comprend que par un retour en arrière,
mais on ne la vit qu'en avant. »
(Sören Kierkegaard)
Samuel DEVULDER
Le #22252381
Une Bévue a écrit :
Samuel DEVULDER wrote:

while((bytes = read(s, buff, BUFSIZ)) > 0) {
fwrite(buf, sizeof(*buf), BUFSIZ, stdout);
if(*buf == '.') break;
}



ok, mais bon le problème est plus "grave" rien ne dir que le "." est en
début de buf



Ca dépend si tu as bien lu jusqu'à la fin de ligne avant ton while(). (A
priori oui vu que tu viens juste d'ouvrir la socket()). Quel est ton
protocole de lecture au juste?

donc mon test :

if(buf[0] == '.' && buf[1] == 'r' && buf[2] == 'n') {
break;
}

intervient +/- aléatoirement.



Sans compter qu'il lui manque un test sur "bytes>2", parce que si ca se
trouve bytes==1 et seul buf[0] est rempli.

Ce qu'il te faudrait c'est que tu te fasse une routine qui lit char par
char la socket et remplie le buffer jusqu'au 'n' ou EOF.

Comme c'est fastidieux, tu peux convertir ta socket en FILE* avec
fdopen(3) et lire dessus avec un bon fgets() des familles. Fais
attention cependant à avoir un buffer suffisamment grand ou sinon tu ne
va ne lire qu'une partie de la ligne courante et tu va te désynchroniser
avec le serveur.

L'idéal serait que tu te fasse une routine avec fgetc() et realloc()
pour lire une ligne "quelle que soit sa longueur". Sans vérif ca
ressemblerait à ceci

// lecture dynamique d'une ligne jusqu'au n final les 'r' sont
// ignores.
char *getLine(FILE *f) {
// buffer
const int CHUNK_SIZE = 1024;
int bufsize = CHUNK_SIZE;
char *buf = malloc(buffsize);

// position du prochain caractère à écrire
int pos = 0;

if(buf==NULL) {...erreur out of mem...}

while(1) {
int c = fgetc(f);
if(c=='n' || c==EOF) break;
if(c!='r') {
// on ajoute le caractère au buffer
buf[pos++] = c;
// plus de place pour le caractere suivant
// (ou le final): on realloue
if(pos>=bufsize) {
bufsize += CHUNK_SIZE;
buf = realloc(buf, bufsize);
if(buf==NULL) {...erreur out of mem...}
}
}
}

// ajout du '' final
buf[pos++] = 0;

// on reduit le buffer à la taille nécéssaire.
buf = realloc(buf, pos);
if(buf==NULL) {...erreur out of mem...}

return buf;
}
espie
Le #22252611
In article Une Bévue
je m'essaie un petit programme envoyant une commande à un serveur de
nouvelles et lisant la réponse, la partie intéressée :

cmd = "LIST NEWSGROUPSn";
send(s, cmd, strlen(cmd), 0);
// s est le socket
while ((bytes = read(s, buf, BUFSIZ)) > 0)
write(1, buf, bytes);

// hanging....
fflush(stdout);

fprintf(stdout, "Ready to close connection to news.free.frn");

close(s);
return 0;



En plus de tes autres soucis, tu es cense envoyer un crlf, et ne pas
t'inquieter s'il te renvoie du lf tout court (sachant qu'il peut renvoyer
du crlf, normalement).

Partant de la, read() va te lire des octets, mais pas forcement des lignes.
Dans le cas qui t'interesse pour l'instant, pas forcement besoin de
bloquant/non bloquant, il te faut juste reconstituer les lignes pour
trouver le "." tout seul qui constitue ta fin de reponse.

Un classique consiste a scanner le tampon pour verifier les "lignes completes"
(et les afficher), recopier le bout de ligne incomplete en debut de buffer,
et faire un read() plus court dans la boucle.

Une autre possibilite consiste a utiliser fdopen et laisser faire la
bibliotheque standard. Seul risque de cette possibilite: cela condamne une
utilisation ulterieure de la socket en mode non-bloquant (ben oui, aucune
garantie que stdio fonctionne avec des fd en non bloquant).

je ne saurais trop te conseiller de te procurer un peu de litterature,
comme le "network programming " de Stevens...
Publicité
Poster une réponse
Anonyme