Forum |  HardWare.fr | News | Articles | PC | S'identifier | S'inscrire | Shop Recherche
1802 connectés 

  FORUM HardWare.fr
  Programmation
  C

  Questions sur les sockets.

 


 Mot :   Pseudo :  
 
Bas de page
Auteur Sujet :

Questions sur les sockets.

n°1234721
thoduv
Posté le 31-10-2005 à 09:04:34  profilanswer
 

Bonjour !
 
J'essaie en ce moment de bidouiller avec les sockets en C. Tout allait bien jusqu'au moment ou je me suis rendu compte que des fonctions comme connect ou recv bloquent le programme tant qu'elles n'ont pas de résultat satisfaisant. Après quelques recherches, je trouve le moyen de passer en mode non-bloquant avec un ioctl. Mais sur ce moe non-bloquant je n'ai pas trouvé beaucoup de doc alors voilà mes questions :
 
TOUT CELA EN MODE CONNECTE (tcp) !
 
- Comment peut-on gérer un "connect" non-bloquant ? En le répétant n fois jusqu'à un résultat, ou alors n secondes ?
- Pourquoi "select" signale-t-il des données vides qui font bloquer le recv ? (voir code à la fin).
Et une autre question sans rapport avec les sockets bloquants/non-bloquants :
- Comment savoir si un socket et toujours actif ou s'il a été fermé par le serveur
 
Et puis si vous avez des liens de tutos ou de cours sur les sockets je suis prenneur !
 
Merci d'avance !
 
Mon code avec select :

Code :
  1. int main(int argv, char **argc)
  2. {
  3. struct sockaddr_in so_addr;
  4. int fd;
  5. char buffer[1024] = "message d'authentification";
  6. struct timeval tv;
  7. fd_set readfds;
  8. tv.tv_sec = 0;
  9. tv.tv_usec = 0;
  10. so_addr.sin_family = AF_INET;
  11. so_addr.sin_port = htons(atoi(argc[2]));
  12. memcpy(&so_addr.sin_addr, gethostbyname(argc[1])->h_addr, sizeof(u_long));
  13. fd = socket(AF_INET,SOCK_STREAM,0);
  14. connect(fd, (void*)&so_addr, sizeof(struct sockaddr_in));
  15. send(fd,buffer, 1024,0);
  16. while(1)
  17. {
  18.  FD_ZERO(&readfds);
  19.  FD_SET(fd, &readfds);
  20.  c = select(fd+1, &readfds, NULL, NULL, &tv);
  21.  if (c == 1 && FD_ISSET(fd, &readfds))
  22.  {
  23.        recv(fd,bf,1024,0);
  24.                         printf("données recues\n" );
  25.  }
  26. }
  27. return 0;
  28. }


Résultat : j'ai "données recues"  qui s'affiche sans arret !
Si j'enleve le "send" qui envoie le message d'authentification au serveur (message auquel je ne devrais recevoir qu'une seule réponse du serveur), il ne se passe rien (pas de données recues, et c'est logique).

mood
Publicité
Posté le 31-10-2005 à 09:04:34  profilanswer
 

n°1234746
Emmanuel D​elahaye
C is a sharp tool
Posté le 31-10-2005 à 09:39:45  profilanswer
 

thoduv a écrit :


Mon code avec select :

Code :
  1. struct timeval tv;
  2. tv.tv_sec = 0;
  3. tv.tv_usec = 0;
  4. while(1)
  5. {
  6.  c = select(fd+1, &readfds, NULL, NULL, &tv);
  7. }


Résultat : j'ai "données recues"  qui s'affiche sans arret !


 
Si tu mets un timeout de 0, c'est normal !
 
Pas de timeout (attente infinie) :

 c = select(fd+1, &readfds, NULL, NULL, NULL);


Timeout de 1 minute :


 struct timeval tv;
 
 tv.tv_sec = 60;
 tv.tv_usec = 0;
 
 while(1)
 {
   
  c = select(fd+1, &readfds, NULL, NULL, &tv);
 }


 
D'autre part,  

              recv(fd,bf,1024,0);


C'est pas une bonne idée, car tu ne sais pas combien d'octets ont réelement été reçus. Ce '1024' est horrible ! Quand à 'bf', on ne sait pas ce que c'est... Attention, il est probable que le 0 de la chaine C ne soit pas transmis. Il faut donc lui garder une place à l'arriver...


   char buffer[1024] = "message d'authentification";
   ssize_t nb_rec;
               nb_rec = recv (fd, buffer, sizeof buffer - 1, 0);
 
               if (nb_rec >0)
               {
                  buffer [nb_rec] = 0;
               }



Message édité par Emmanuel Delahaye le 31-10-2005 à 09:51:20

---------------
Des infos sur la programmation et le langage C: http://www.bien-programmer.fr Pas de Wi-Fi à la maison : http://www.cpl-france.org/
n°1234750
thoduv
Posté le 31-10-2005 à 09:43:24  profilanswer
 

Non en fait l'idée c'est de porter sur un système sans OS (sans rentrer dans les détails). Donc à chaque cycle, la fonction contenant select est appelée, avec un timeout de 0, pour savoir s'il y a des données à recevoir.

n°1234760
Emmanuel D​elahaye
C is a sharp tool
Posté le 31-10-2005 à 09:53:10  profilanswer
 

thoduv a écrit :

Non en fait l'idée c'est de porter sur un système sans OS (sans rentrer dans les détails). Donc à chaque cycle, la fonction contenant select est appelée, avec un timeout de 0, pour savoir s'il y a des données à recevoir.


Ok, C'est un peu horrible, mais pourquoi pas. Dans ce cas, relis mon post, il y a la solution.
 


---------------
Des infos sur la programmation et le langage C: http://www.bien-programmer.fr Pas de Wi-Fi à la maison : http://www.cpl-france.org/
n°1234763
thoduv
Posté le 31-10-2005 à 09:57:44  profilanswer
 

Oui mais si je veux recevoir exactement le bon nombre d'octets, il faut faire un truc du style :
 

Code :
  1. while(recv(fd, buffer++,1,0));


 
Et ca c'est un peu lourd aussi non ?

n°1234765
Emmanuel D​elahaye
C is a sharp tool
Posté le 31-10-2005 à 10:02:20  profilanswer
 

thoduv a écrit :

Oui mais si je veux recevoir exactement le bon nombre d'octets, il faut faire un truc du style :
 

Code :
  1. while(recv(fd, buffer++,1,0));


 
Et ca c'est un peu lourd aussi non ?


 
"Recevoir exactement le bon nombre d'octets", ça n'existe pas. Tu proposes une taille max, et recv() te dis combien il a reçu, c'est tout. Comment peux tu prévoir ce qui va se passer au niveau de l'émission distante, et même au niveau des couches inférieures locales ?  
 
D'autre part, j'avais indiqué '> 0' et non '!= 0'. En effet le cas <0 (-1 en fait) peut exister et il indique une erreur.
 
http://wwwcgi.rdg.ac.uk:8081/cgi-b [...] OCKET/recv


---------------
Des infos sur la programmation et le langage C: http://www.bien-programmer.fr Pas de Wi-Fi à la maison : http://www.cpl-france.org/
n°1234773
thoduv
Posté le 31-10-2005 à 10:13:35  profilanswer
 

Tiens étrange ca marche maintenant ... En tout cas merci beaucoup, je pense que l'erreur venait du fait que j'envoyais 1024 octets au lieu de strlen(buffer) octets.
 
Et sinon pour ce qui est du connect non-bloquant et de verifier l'existance d'un socket (savoir s'il a été fermé par le serveur) ?

n°1234792
Emmanuel D​elahaye
C is a sharp tool
Posté le 31-10-2005 à 10:28:24  profilanswer
 

thoduv a écrit :

Et sinon pour ce qui est du connect non-bloquant


En principe, ça n'existe pas. Les ioctl(), c'est de la bidouille pas forcément bien documentée et encore moins portable... un select() blocant est la bonne solution.

Citation :

et de verifier l'existance d'un socket (savoir s'il a été fermé par le serveur) ?


Surveiller les codes retour des fonctions. -1 = erreur. errno donne la raison (<errno.h> ), perror() l'affiche...


---------------
Des infos sur la programmation et le langage C: http://www.bien-programmer.fr Pas de Wi-Fi à la maison : http://www.cpl-france.org/
n°1234847
thoduv
Posté le 31-10-2005 à 11:37:58  profilanswer
 

Ok, concretement je peux faire comment pour faire un connect avec un timeout (qui laisse tomber si au bout de n secondes il s'est pas connecté) ?

Message cité 1 fois
Message édité par thoduv le 31-10-2005 à 11:41:04
n°1234852
Sve@r
Posté le 31-10-2005 à 11:41:08  profilanswer
 

thoduv a écrit :

Tiens étrange ca marche maintenant ... En tout cas merci beaucoup, je pense que l'erreur venait du fait que j'envoyais 1024 octets au lieu de strlen(buffer) octets.


Cela n'a aucune importance. Que tu envoies 10 ou 1024 octets, si tu en attends 1024 de l'autre coté tu en recevras 10 ou 1024 et ta fonction de lecture (read ou recv) t'indiquera combien t'en as reçu.
Evidemment, si tu envoies 1014 octets inutiles ça ralenti ton appli mais le nb d'octets envoyés n'influe pas sur la réussite ou l'échec...


Message édité par Sve@r le 31-10-2005 à 11:42:24
mood
Publicité
Posté le 31-10-2005 à 11:41:08  profilanswer
 

n°1234906
Emmanuel D​elahaye
C is a sharp tool
Posté le 31-10-2005 à 12:05:13  profilanswer
 

thoduv a écrit :

Ok, concretement je peux faire comment pour faire un connect avec un timeout (qui laisse tomber si au bout de n secondes il s'est pas connecté) ?


Tu regles une valeur (déjà indiqué) et tu testes le code retour de select(). Il y en a un qui veut dire 'timeout'.


---------------
Des infos sur la programmation et le langage C: http://www.bien-programmer.fr Pas de Wi-Fi à la maison : http://www.cpl-france.org/
n°1235802
thoduv
Posté le 01-11-2005 à 18:22:29  profilanswer
 

Bon, ca marche mais ca marche très bizarrement, ca bloque tout ...
 
Voilà le code de la fonction appelée à chaque cycle pour tester et lire sur un socket :
 
(simsn_Socket est une structure qui contient le fd du socket ainsi qu'un buffer pour ce socket)
 

Code :
  1. char *SocketCheckRead(simsn_Socket *s)
  2. {
  3. int i,r;
  4. struct timeval tv;
  5. fd_set readfds;
  6. tv.tv_sec = 0;
  7. tv.tv_usec = 0;
  8. FD_ZERO(&readfds);
  9. FD_SET(s->fd, &readfds);
  10. i = select(s->fd+1, &readfds, NULL, NULL, &tv);
  11. if ((i>0) && (FD_ISSET(s->fd, &readfds)))
  12. {
  13.  r = recv(s->fd, s->Buffer, s->BufferSize, 0);
  14.  s->Buffer[r] = '\0';
  15.  simsn_Log("< %s\n", s->Buffer);
  16.  return s->Buffer;
  17. }
  18. else
  19. {
  20.  return NULL;
  21. }
  22. }


 
Et voilà le main :
 

Code :
  1. int i = 0;
  2. char *buffer;
  3. while(i++<5000000)
  4. {
  5.    buffer = SocketCheckRead(monsocket);
  6.    if(buffer != NULL)
  7.    {
  8.        printf("Recu : %s\n", buffer);
  9.    }
  10. }


 
Ca marche, mais les "Recu : ..." n'apparaissent qu'à la fin des 5000000 cycles ... J'ai l'impression que le fait de lire comme ca bloque tout le reste, mais c'est étrange puisque l'execution est censée être linéaire ...

n°1235811
Emmanuel D​elahaye
C is a sharp tool
Posté le 01-11-2005 à 18:57:38  profilanswer
 

thoduv a écrit :

Bon, ca marche mais ca marche très bizarrement, ca bloque tout ...


Normal, select() est blocant, c'est fait pour. L'intéret, c'est qu'il peut se débloquer pour un tas de raisons (programmables)...
 
Si ça te gène, tu le mets dans un thread et on en parle plus.


Message édité par Emmanuel Delahaye le 01-11-2005 à 20:13:38

---------------
Des infos sur la programmation et le langage C: http://www.bien-programmer.fr Pas de Wi-Fi à la maison : http://www.cpl-france.org/
n°1235814
thoduv
Posté le 01-11-2005 à 19:00:41  profilanswer
 

Bloquant ? Comment ca ? Je lui ai mis un timeout de 0 ! Et thread je peux pas, c'est fait pour une machine sans OS.

n°1235825
Emmanuel D​elahaye
C is a sharp tool
Posté le 01-11-2005 à 19:13:25  profilanswer
 

thoduv a écrit :

Bloquant ? Comment ca ? Je lui ai mis un timeout de 0 ! Et thread je peux pas, c'est fait pour une machine sans OS.


Il faut mettre l'init de tv dans la boucle avant le select(), car select() modifie tv. (Lire la doc)
 

Citation :

On Linux, the function select modifies timeout to reflect the amount of time not slept; most other implementations do not do this.  This causes problems both when Linux code which reads timeout is ported to other operating systems, and when code is ported to Linux that reuses a struct timeval for multiple select s in a loop without reinitializing it.  Consider timeout to be undefined after select returns.  


 
Machine sans OS, ça m'inquiète. Quelle machine ? Quel compilateur ? Qu'est-ce qui t'empêche d'utiliser un Linux [embarqué] pour cette machine ?
 
Rappel : select() n'est pas standard, mais POSIX. POSIX est une norme 'système'.


Message édité par Emmanuel Delahaye le 01-11-2005 à 19:15:28

---------------
Des infos sur la programmation et le langage C: http://www.bien-programmer.fr Pas de Wi-Fi à la maison : http://www.cpl-france.org/
n°1235829
thoduv
Posté le 01-11-2005 à 19:15:53  profilanswer
 

Je constate l'inverse ...
 
Attente infinie :

Code :
  1. select(fdmax, read, write, exept, NULL);


Attente nulle   :

Code :
  1. timeval tv;
  2. tv.sec=0;
  3. tv.usec=0;
  4. select(fdmax, read, write, exept, &tv);


 
Non ?
 
Edit: Non je ne peux pas utiliser de Linux embarqué, et c'est n'est pas un problème, j'ai des fonctions équivalentes aux fonctions Linux (select, connect, etc ...) sur cette machine.

Message cité 1 fois
Message édité par thoduv le 01-11-2005 à 19:18:19
n°1235832
Emmanuel D​elahaye
C is a sharp tool
Posté le 01-11-2005 à 19:17:22  profilanswer
 

thoduv a écrit :

Je constate l'inverse ...
 
Attente infinie :

Code :
  1. select(fdmax, read, write, exept, NULL);


Attente nulle   :

Code :
  1. timeval tv;
  2. tv.sec=0;
  3. tv.usec=0;
  4. select(fdmax, read, write, exept, &tv);


 
Non ?


Oui, j'ai annulé et reposté...
 


---------------
Des infos sur la programmation et le langage C: http://www.bien-programmer.fr Pas de Wi-Fi à la maison : http://www.cpl-france.org/
n°1235835
thoduv
Posté le 01-11-2005 à 19:20:45  profilanswer
 

Ok. Mais mon select est dans une fonction, et le timeval est défini dans la fonction également, donc à chaque appel de la fonction tv est remis à 0
 


main
{
 boucle  
 {
  recevoir();
 }
}
 
recevoir
{
 initialise tv
 initialise ensemble select
 select
}

n°1235855
Emmanuel D​elahaye
C is a sharp tool
Posté le 01-11-2005 à 19:34:05  profilanswer
 

thoduv a écrit :

Ok. Mais mon select est dans une fonction, et le timeval est défini dans la fonction également, donc à chaque appel de la fonction tv est remis à 0
 


main
{
 boucle  
 {
  recevoir();
 }
}
 
recevoir
{
 initialise tv
 initialise ensemble select
 select
}



 
Ok. Avec quel système testes-tu ?


---------------
Des infos sur la programmation et le langage C: http://www.bien-programmer.fr Pas de Wi-Fi à la maison : http://www.cpl-france.org/
n°1235862
thoduv
Posté le 01-11-2005 à 19:37:02  profilanswer
 
n°1235915
Emmanuel D​elahaye
C is a sharp tool
Posté le 01-11-2005 à 20:19:49  profilanswer
 


Ok. En fait qu'est-ce qui ne fonctionne pas exactement ? Le comportement que tu décris parait finalement assez normal. Si tu en doutes, affiche la valeur du compteur.
 
Et ta trace simsn_Log(), elle dit quoi ?
 


---------------
Des infos sur la programmation et le langage C: http://www.bien-programmer.fr Pas de Wi-Fi à la maison : http://www.cpl-france.org/
n°1235924
thoduv
Posté le 01-11-2005 à 20:50:48  profilanswer
 

Voilà ce que ca fait exactement :
 

- Connection du socket.
- Envoie message authentification.
- Boucle de 5000000 cycles :
| - Fonction de réception (avec select).
| - Affichage des données recues (s'il y en a).


 
Ce qui commence par # est un log.
Ce qui commence par > est un log de message envoyé.
Ce qui commence par < est un log de message recu.
 

# Socket connecté.  
> AUTHENTIFICATION                    (Envoi)
< AUTHENTIFICATION REUSSIE    (Reception)
... Là le programme se bloque (écoulement des 5000000 cycles)
AUTHENTIFICATION REUSSIE        (Affichage du buffer de réception : ca aurait du être fait juste après le log de cette reception)
# Socket deconnecté.


 
Donc le problème c'est que l'affichage du message recu bloque sur le dernier caractère (pas normal du tout).
 
Ce n'est pas select qui bloque, car si on place un log du style log("-" ); dans la fonction de réception juste après le select(), on obtient l'affichage d'un "-" à chaque cycle (donc une masse pas possible dans la console).
 
Je comprends vraiment pas, pourtant l'affichage des données recues dans le log marche, mais il bloque une fois que la fonction de réception s'est terminée en renvoyant l'adresse du buffer ...

n°1235934
Emmanuel D​elahaye
C is a sharp tool
Posté le 01-11-2005 à 21:08:16  profilanswer
 

thoduv a écrit :

Voilà ce que ca fait exactement :
 

- Connection du socket.
- Envoie message authentification.
- Boucle de 5000000 cycles :
| - Fonction de réception (avec select).
| - Affichage des données recues (s'il y en a).


 
Ce qui commence par # est un log.
Ce qui commence par > est un log de message envoyé.
Ce qui commence par < est un log de message recu.
 

# Socket connecté.  
> AUTHENTIFICATION                    (Envoi)
< AUTHENTIFICATION REUSSIE    (Reception)
... Là le programme se bloque (écoulement des 5000000 cycles)
AUTHENTIFICATION REUSSIE        (Affichage du buffer de réception : ca aurait du être fait juste après le log de cette reception)
# Socket deconnecté.


 
Donc le problème c'est que l'affichage du message recu bloque sur le dernier caractère (pas normal du tout).
 
Ce n'est pas select qui bloque, car si on place un log du style log("-" ); dans la fonction de réception juste après le select(), on obtient l'affichage d'un "-" à chaque cycle (donc une masse pas possible dans la console).
 
Je comprends vraiment pas, pourtant l'affichage des données recues dans le log marche, mais il bloque une fois que la fonction de réception s'est terminée en renvoyant l'adresse du buffer ...


 
L'affiche est retardé. C'est ça qui te choque ? Quitte ta boucle dès reception, pour voir...
 
C'est sûr que de faire une attente active, ça bouffe 100% du CPU (Rappel, Linux est un système coopératif, et non préemptif). La tâche d'affichage étant moins prioritaire, elle passe après. L'attente active, c'est mauvais, mais c'est ça que tu veux, alors il faut en subir les conséquences... Essaye quand même de rendre la main au système avec un usleep() de quelques µs dans la boucle... Ton CPU va respirer un peu mieux...


---------------
Des infos sur la programmation et le langage C: http://www.bien-programmer.fr Pas de Wi-Fi à la maison : http://www.cpl-france.org/
n°1235940
thoduv
Posté le 01-11-2005 à 21:14:15  profilanswer
 

Ouah merci !
 

Code :
  1. usleep(1);

dans la boucle et ca repart !
 
Pour ce qui est de la consommation CPU c'est pas grave puisqu'au final mon appli aura un CPU pour elle toute seule.

mood
Publicité
Posté le   profilanswer
 


Aller à :
Ajouter une réponse
  FORUM HardWare.fr
  Programmation
  C

  Questions sur les sockets.

 

Sujets relatifs
c++ et socketscours sockets et tubes
questions bêtes sur l'utilisation de visual C++ (librairies)URL Rewriting - quelques questions...
2 questionsquestions sur SQL Server
questions d'un debutantQuestions PostgreSQL / SGBD en général
[CSS] Questions diverses[C++] Questions sur les sockets (code inside)
Plus de sujets relatifs à : Questions sur les sockets.


Copyright © 1997-2022 Hardware.fr SARL (Signaler un contenu illicite / Données personnelles) / Groupe LDLC / Shop HFR