[C] Sockets et appel système "select()"

Sockets et appel système "select()" [C] - C - Programmation

Marsh Posté le 16-04-2004 à 22:03:01    

Bonjour tout le monde,
 
Je suis en train de coder une application client/serveur se basant sur l'appel système select(). Le problème est que le FD_ISSET() (macro détectant qu'un descripteur de fichiers a été utilisé) est vrai, même si le client n'a rien envoyé !
 
Note importante : vous trouverez dans la première réponse à ce message juste les parties du code "intéressantes". Dans ce message, j'ai volontairement mis l'intégralité (enfin, c'est pas non plus monstreux !) des deux codes source (client/serveur).
 
Voici le code du serveur :

Code :
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <unistd.h>
  5. #include <netdb.h>
  6. #include <errno.h>
  7. #include <sys/socket.h>
  8. #include <sys/types.h>
  9. #include <sys/time.h>
  10. #include <sys/types.h>
  11. #include <sys/select.h>
  12. #include <netinet/in.h>
  13. #include <arpa/inet.h>
  14. #define MAX_PENDING_CONNECT (5U)
  15. #define PORT (5000U)
  16. #define BUFSIZE (1024)
  17. /****************************************************************************
  18. * Macro that allow to save and load the descriptors sets. Indeed, when  
  19. * select is called, it removed all descriptors that have not been modified
  20. * since last call. So, we must have an "history" of the initial set and we
  21. * must load it before each select call in order to consider all  
  22. * descriptors.
  23. ****************************************************************************/
  24. #define SAVE_SETS                                                 \
  25. {                                                                 \
  26.    memcpy(&fds_read_copy, &fds_read, sizeof(fds_read));           \
  27. }
  28. #define LOAD_SETS                                                 \
  29. {                                                                 \
  30.    memcpy(&fds_read, &fds_read_copy, sizeof(fds_read_copy));      \
  31. }
  32. /****************************************************************************
  33. *
  34. * Description : Create a socket, disable its TIME_WAIT option, bind the
  35. * port and listen for connections.
  36. *
  37. * Return
  38. *    - '-1' if the creation failed.
  39. *    - the new file descriptor if the creation succeed.
  40. *
  41. ****************************************************************************/
  42. int create_socket(unsigned int port, unsigned int max_pending_connections)
  43. {
  44.    struct sockaddr_in server_info;     /* Information of the server */
  45.    int server_fd;                      /* The file descriptor that will be
  46.                                           returned */
  47.    int flag;                           /* Flag for the setsockopt func. */
  48.    /* Note : htons() is used to convert the port value in the Network Byte  
  49.     * Order (NBO). This order is the one used in the TCP/IP packets. */
  50.    server_info.sin_family = AF_INET;    /* We treats with IPv4 address */
  51.    server_info.sin_port = htons(port);
  52.    server_info.sin_addr.s_addr = INADDR_ANY;   /* My IP address :) */
  53.    memset(&(server_info.sin_zero), '\0', (size_t)8);
  54.    /* Creation of the socket */
  55.    if((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
  56.       return -1;
  57.    /* Disable TIME_WAIT */
  58.    flag=1;
  59.    if(setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag)) == -1
  60. )
  61.       fprintf(stderr, "Error while using setsockopt()\n" );
  62.    /* Binding the port */
  63.    if(bind(server_fd, (struct sockaddr*)&server_info, sizeof(struct sockaddr)) =
  64. = -1)
  65.       return -1;
  66.    /* Listenning connections */
  67.    if(listen(server_fd, max_pending_connections) == -1)
  68.       return -1;
  69.    return (server_fd);
  70. }
  71. int main()
  72. {
  73.    fd_set fds_read;              /* Set of file descriptors for reading */
  74.    fd_set fds_read_copy;         /* Copy of fds_read not to lose info */
  75.    fd_set fds_write_copy;        /* Copy of fds_write not to lose info */
  76.    /* List of the file descriptors of each client. The file descriptor
  77.     * list_file_descriptors[0] is this of the server. */
  78.    int server_fd, new_fd, sin_size, nb_bytes;
  79.    int max_fd;
  80.    char buffer[BUFSIZE];
  81.    struct sockaddr_in client_info;  /* Store the client information */
  82.    FD_ZERO(&fds_read);
  83.    FD_ZERO(&fds_read_copy);
  84.    if((server_fd = create_socket(PORT, MAX_PENDING_CONNECT)) == -1)
  85.    {
  86.       fprintf(stderr, "Error while creating the socket.\n" );
  87.       return (EXIT_FAILURE);
  88.    }
  89.    FD_SET(server_fd, &fds_read);
  90.    SAVE_SETS;
  91.    max_fd = server_fd;
  92.    while(1)
  93.    {
  94.       LOAD_SETS;
  95.       if(select(max_fd + 1, &fds_read, NULL, NULL, NULL)  == -1)
  96.          fprintf(stderr, "select error !\n" );
  97.       /* Après un select, les "sets" peuvent être modifié. */
  98.       if(FD_ISSET(server_fd, &fds_read))
  99.       {
  100.          /* Accepting connections :) */
  101.          printf("Un client s'est connecté, je l'ajoute dans le set\n" );
  102.          sin_size = sizeof(struct sockaddr_in);
  103.          if((new_fd = accept(server_fd, (struct sockaddr*)&client_info, &sin_siz
  104. e)) == -1)
  105.             fprintf(stderr, "Error while accepting connection...\n" );
  106.          if(new_fd > max_fd)
  107.             max_fd = new_fd;
  108.          FD_SET(new_fd, &fds_read);
  109.          SAVE_SETS;
  110.       }
  111.       if(FD_ISSET(new_fd, &fds_read))
  112.       {
  113.          if ((nb_bytes = recv(new_fd, buffer, 50, 0)) == -1)
  114.          {
  115.             fprintf(stderr, "Probleme avec la fonction recv()\n" );
  116.             exit(EXIT_FAILURE);
  117.          }
  118.          else
  119.             printf("." );
  120.       }
  121.    }
  122.    printf("JE QUITTE\n" );
  123.    return EXIT_SUCCESS;
  124. }


 
Vous remarquez donc que dès que le serveur reçoit un message du client, il affiche un point ("." ). Or, avec le client ci-dessous (qui n'envoie que deux messages), le serveur affiche une infinité de points !
 
Le code du client :

Code :
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <sys/socket.h>
  5. #include <sys/types.h>
  6. #include <netinet/in.h>
  7. #include <arpa/inet.h>
  8. #include <netdb.h>
  9. #include <errno.h>
  10. #include <unistd.h>
  11. #define MAX_PENDING_CONNECT (5)
  12. #define PORT (5000)
  13. #define BUFSIZE (1024)
  14. int main(int argc, char **argv)
  15. {
  16.    struct sockaddr_in server_info;
  17.    struct hostent *hostinfo;
  18.    int fd, nb_bytes;
  19.    const char msg[] = "CS 'lockness";
  20.    char buffer[BUFSIZE];
  21.    if(argc < 2)
  22.    {
  23.       fprintf(stderr, "Hostname or IP in argument please !\n" );
  24.       return (EXIT_FAILURE);
  25.    }
  26.    /*************************
  27.     * CREATION DE LA SOCKET *
  28.     *************************/
  29.    if((hostinfo = gethostbyname(argv[1])) == NULL)
  30.       fprintf(stderr, "Et là... c'est le drame !\n" );
  31.    server_info.sin_family = AF_INET;    /* We treats with IPv4 address */
  32.    server_info.sin_port = htons(5000);
  33.    server_info.sin_addr = *((struct in_addr*)hostinfo->h_addr);
  34.    memset(&(server_info.sin_zero), '\0', (size_t)8);
  35.    if((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
  36.       fprintf(stderr, "Error in the socket creation\n" );
  37.    printf("ip = %s\n", inet_ntoa(server_info.sin_addr));
  38.    /************************
  39.     * CONNEXION AU SERVEUR *
  40.     ************************/
  41.    if (connect(fd, (struct sockaddr *)&server_info, sizeof(struct sockaddr)) ==
  42. -1)
  43.    {
  44.       fprintf(stderr, "Impossible de se connecter au serveur\n" );
  45.       exit(EXIT_FAILURE);
  46.    }
  47.    /**************
  48.     * TRANSFERTS *
  49.     **************/
  50.    /* Envoie d'un msg au serveur */
  51.    if (send(fd, msg, strlen(msg), 0) == -1)
  52.       fprintf(stderr, "Error while using send()\n" );
  53.    printf("Message sent by the client : %s\n", msg);
  54.    sleep(2);
  55.    if (send(fd, msg, strlen(msg), 0) == -1)
  56.       fprintf(stderr, "Error while using send()\n" );
  57.    printf("Message sent by the client : %s\n", msg);
  58.    close(fd);
  59.    return EXIT_SUCCESS;
  60. }


 
Quelqu'un pourrait-il m'aider à résoudre ce problème (tout bete en plus) ?


Message édité par Lockness le 16-04-2004 à 22:10:39
Reply

Marsh Posté le 16-04-2004 à 22:03:01   

Reply

Marsh Posté le 16-04-2004 à 22:08:47    

Chose promie, chose due, voici juste les parties "intéressantes" du serveur et du client :
 
Le serveur :  

Code :
  1. FD_SET(server_fd, &fds_read);
  2.    SAVE_SETS;
  3.    max_fd = server_fd;
  4.    while(1)
  5.    {
  6.       LOAD_SETS;
  7.       if(select(max_fd + 1, &fds_read, NULL, NULL, NULL)  == -1)
  8.          fprintf(stderr, "select error !\n" );
  9.       /* Après un select, les "sets" peuvent être modifié. */
  10.       if(FD_ISSET(server_fd, &fds_read))
  11.       {
  12.          /* Accepting connections :) */
  13.          printf("Un client s'est connecté, je l'ajoute dans le set\n" );
  14.          sin_size = sizeof(struct sockaddr_in);
  15.          if((new_fd = accept(server_fd, (struct sockaddr*)&client_info, &sin_siz
  16. e)) == -1)
  17.             fprintf(stderr, "Error while accepting connection...\n" );
  18.          if(new_fd > max_fd)
  19.             max_fd = new_fd;
  20.          FD_SET(new_fd, &fds_read);
  21.          SAVE_SETS;
  22.       }
  23.       if(FD_ISSET(new_fd, &fds_read))
  24.       {
  25.          if ((nb_bytes = recv(new_fd, buffer, 50, 0)) == -1)
  26.          {
  27.             fprintf(stderr, "Probleme avec la fonction recv()\n" );
  28.             exit(EXIT_FAILURE);
  29.          }
  30.          else
  31.             printf("." );
  32.       }
  33.    }


 
Le client :

Code :
  1. int main(int argc, char **argv)
  2. {
  3.    struct sockaddr_in server_info;
  4.    struct hostent *hostinfo;
  5.    int fd, nb_bytes;
  6.    const char msg[] = "CS 'lockness";
  7.    char buffer[BUFSIZE];
  8.    if(argc < 2)
  9.       return (EXIT_FAILURE);
  10.    /*************************
  11.     * CREATION DE LA SOCKET *
  12.     *************************/
  13.    if((hostinfo = gethostbyname(argv[1])) == NULL)
  14.       fprintf(stderr, "Et là... c'est le drame !\n" );
  15.    server_info.sin_family = AF_INET;    /* We treats with IPv4 address */
  16.    server_info.sin_port = htons(5000);
  17.    server_info.sin_addr = *((struct in_addr*)hostinfo->h_addr);
  18.    memset(&(server_info.sin_zero), '\0', (size_t)8);
  19.    if((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
  20.       fprintf(stderr, "Error in the socket creation\n" );
  21.    if (connect(fd, (struct sockaddr *)&server_info, sizeof(struct sockaddr)) ==
  22. -1)
  23.       exit(EXIT_FAILURE);
  24.    /**************
  25.     * TRANSFERTS *
  26.     **************/
  27.    /* Envoie d'un msg au serveur */
  28.    if (send(fd, msg, strlen(msg), 0) == -1)
  29.       fprintf(stderr, "Error while using send()\n" );
  30.    printf("Message sent by the client : %s\n", msg);
  31.    sleep(2);
  32.    if (send(fd, msg, strlen(msg), 0) == -1)
  33.       fprintf(stderr, "Error while using send()\n" );
  34.    printf("Message sent by the client : %s\n", msg);
  35.    close(fd);
  36.    return EXIT_SUCCESS;
  37. }

Reply

Marsh Posté le 16-04-2004 à 22:09:25    

c'est à toi de faire un FD_SET au début je pense pour les initialiser


Message édité par antsite le 16-04-2004 à 22:10:04
Reply

Marsh Posté le 16-04-2004 à 22:12:47    

C'est ce que j'ai fait : juste après les deux FD_ZERO.

Reply

Marsh Posté le 17-04-2004 à 16:25:49    

Lockness a écrit :

Bonjour tout le monde,
 
Je suis en train de coder une application client/serveur se basant sur l'appel système select(). Le problème est que le FD_ISSET() (macro détectant qu'un descripteur de fichiers a été utilisé) est vrai, même si le client n'a rien envoyé !
 


 
Au pif-ô-mètre, le problème me semble venir du code retour du select qui n'est pas testé. A savoir si select retourne 0, votre code effectue quand même le FD_ISSET or, pour peu que le set retourné par le select soit vide dans ce cas, il est fort possible que ça chie un max.

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed