Programmation réseau : la fonction select

bonjour,

j’ai programmé en C, un serveur et un client pour un chat :
j’utilise la fonction select et je constate que lorsqu’un client se déconnecte brutalement (CTRL C) alors la fonction select dans le serveur n’est plus
bloquante et donc celui-ci boucle et devient inopérant.

peut-on régler le problème du côté serveur, car je pense qu’un client aussi mal écrit soit-il n’est pas censé faire planter un serveur …

Merci

Heu … Je précise il ne s’agit pas de l’animal mais un tchatte

Comme ça je pense à deux raisons possibles :
[ul]
[li]tu vérifie pas les erreurs dans le code de sortie de select ;[/li]
[li]il est possible que ton serveur reçoive un signal genre SIGPIPE et que tu l’ai mal ou pas géré.[/li][/ul]

Pareil que mister : vérifies tes retours (ne pas vérifier les retours des appels cay mal, surtout en C) pour une / des erreurs (genre select qui renvoie EINTR) et code des handlers de signaux d’erreur que tu risques de recevoir si c’est pas déjà fait.
Sinon, côté client tu peux toujours atrapper le signal de ctrl+c et envoyer EOF au serveur avant de quitter (que tu prendras soin de gérer de l’autre côté). Mais dans le cas ou tu modifierais ton client cela ne rendra pas le serveur beaucoup plus robuste, nous sommes d’accord.

Sinon pour éviter de boucler à l’infini dans certaines situations et pour un système client / serveur, tu peux toujours imaginer de mettre en place un timeout entre ton serveur et chacun de ses clients (un par client connecté je suppose, histoire qu’un client qui a un pb ferme pas toutes les connections de tous les clients).

C’est d’ailleurs intégré dans select.

Au pire tu peut utiliser une lib réseau un peu plus haut niveau.

Généralement j’utilise des forks ou des threads et je ferme ma socket client (côté serveur) dès qu’un read ou un write me retourne -1.
Mais bon, je n’ai pas un grande expérience de ce type de manipulation.

bonsoir et merci pour vos réponses

Pour info, je teste les retours mais probablement pas suffisamment …

J’ai utilisé strace pour mieux voir ce qui se passe :

select(5, [3 4], NULL, NULL, NULL)      = 1 (in [4])
read(4, "", 256)                        = 0
select(5, [3 4], NULL, NULL, NULL)      = 1 (in [4])
read(4, "", 256)                        = 0
select(5, [3 4], NULL, NULL, NULL)      = 1 (in [4])
read(4, "", 256)                        = 0
select(5, [3 4], NULL, NULL, NULL)      = 1 (in [4])
read(4, "", 256)                        = 0
select(5, [3 4], NULL, NULL, NULL)      = 1 (in [4])
read(4, "", 256)                        = 0
select(5, [3 4], NULL, NULL, NULL)      = 1 (in [4])
read(4, "", 256)                        = 0
select(5, [3 4], NULL, NULL, NULL)      = 1 (in [4])
read(4, "", 256)                        = 0
select(5, [3 4], NULL, NULL, NULL)      = 1 (in [4])
read(4, "", 256)                        = 0
select(5, [3 4], NULL, NULL, NULL)      = 1 (in [4])
read(4, "", 256)                        = 0
select(5, [3 4], NULL, NULL, NULL)      = 1 (in [4])
read(4, "", 256)                        = 0
select(5, [3 4], NULL, NULL, NULL)      = 1 (in [4])
read(4, "", 256)                        = 0
select(5, [3 4], NULL, NULL, NULL)      = 1 (in [4])
read(4, "", 256)                        = 0
select(5, [3 4], NULL, NULL, NULL)      = 1 (in [4])
read(4, "", 256)                        = 0
select(5, [3 4], NULL, NULL, NULL)      = 1 (in [4])
read(4, "", 256)                        = 0
select(5, [3 4], NULL, NULL, NULL)      = 1 (in [4])
read(4, "", 256)                        = 0
select(5, [3 4], NULL, NULL, NULL)      = 1 (in [4])
read(4, "", 256)                        = 0
select(5, [3 4], NULL, NULL, NULL)      = 1 (in [4])
read(4, "", 256)                  

Le serveur boucle dessus, alors que le client s’est arêté (CTRL C)
Les descripteurs 3 et 4 sont ceux de la socket principale et celle de service pour le client.

Merci

Tu veux pas nous montrer le code ?
Je suis curieux de voire comment tu fais ton appel au select()

Je sais pas comment tu t’y prends,
mais quand l’autre extrémité de la connexion d’une socket se ferme,
je crois que tu peux lire dedans pour justement avoir le EOF
(là tu t’aperçois que ton descripteur de fichier de sert plus à rien et tu fais le ménage).

Si tu peux lire dedans, le select() est non bloquant.

bonsoir,

voici le code de mon serveur :

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

int main()

{
      struct sockaddr_in Sock_Serv_Name ;
      struct sockaddr_in Sock_Connexion_Name ;
      int Sock_Serv ;
      int s ;
      int i, j ;
      int nb_lu ;
      int autorisation = 1 ;
      int Nbr_clients = 0 ;
      struct client { 
            int sock ;
            char  pseudo[256] ;
      };
      struct client clients[10] ; 
      int nfds ;
      char message[256] ;
      fd_set readfds ;
      fd_set cpreadfds ;

      socklen_t len = sizeof(struct sockaddr_in) ;

      // création de la socket principale 
      Sock_Serv = socket(AF_INET,SOCK_STREAM,0) ;

      // création de l'adresse clients
      Sock_Serv_Name.sin_family = AF_INET ;
      Sock_Serv_Name.sin_addr.s_addr = 0 ;
      Sock_Serv_Name.sin_port = htons(2000) ;

      // au cas ou le serveur s'arête et redémarre
      setsockopt(Sock_Serv, SOL_SOCKET, SO_REUSEADDR, &autorisation, sizeof(int)) ;

      // attribution de l'adresse à la socket
      if (bind(Sock_Serv, (struct sockaddr *)&Sock_Serv_Name, len) != 0)  {
      	perror("bind") ;
	exit(-1) ;
      }

      // attente de demande de connexion d'un client
      if (listen(Sock_Serv, 10) != 0)  {
      	perror("listen") ;
	exit(-1) ;
      }

      // initialisation de l'ensemble des descripteurs pour la lecture
      FD_ZERO(&readfds) ;
      FD_SET(Sock_Serv,&readfds) ;
      nfds = Sock_Serv + 1 ;

      while(1){

      // utilisation d'une copie de l'ensemble des descripteurs pour select
      cpreadfds = readfds ;
      
      // attente d'une demande de lecture 
      if (select(nfds, &cpreadfds, NULL, NULL, NULL) < 1) {
         perror("select") ;
         exit(0) ;
      } ;

      // vérification de la socket principale
      if (FD_ISSET(Sock_Serv,&cpreadfds))
      {
         /* Un client se connecte au serveur */
         s = accept(Sock_Serv,(struct sockaddr *)& Sock_Connexion_Name, &len) ;
         if (s >= nfds) nfds = s + 1;
         FD_SET(s, &readfds) ;

         //lecture du pseudo du client qui vient de se connecter
         if ((nb_lu = read(s, clients[Nbr_clients].pseudo, 256) == -1)) {
               perror("read") ;
               exit(-1) ;
               } 
         clients[Nbr_clients].pseudo[nb_lu] = '\0' ;
         clients[Nbr_clients].sock = s ;
         sprintf(message, "<<< %s est entre dans le chat\n",clients[Nbr_clients].pseudo) ; 
         Nbr_clients++ ;
         for (j=0; j<Nbr_clients; j++) 
         if (clients[j].sock != s ) {
         // tout le monde est prévenu de la l'arrivée du nouveau client sauf le nouveau client
         write(clients[j].sock,message, strlen(message)) ;
         }
      }

      // vérification des sockets de service
      for (i=0; i<Nbr_clients; i++)
      {
         if (FD_ISSET(clients[i].sock, &cpreadfds))
         {
            /*un client a envoye un message */
            if ((nb_lu = read(clients[i].sock, message, 256)) == -1) {
               printf("error\n") ;
               perror("read") ;
               exit(-1) ;
            } 
            message[nb_lu]='\0' ;
            for (j=0; j<Nbr_clients; j++) 
            //on envoie le message aux autres clients
            if (clients[j].sock != clients[i].sock) write(clients[j].sock,message, strlen(message)) ;
         }
      }

      }



}