IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Réseau C Discussion :

Je ne comprends pas ceci dans les sockets en C


Sujet :

Réseau C

  1. #1
    Nouveau Candidat au Club
    Homme Profil pro
    Lycéen
    Inscrit en
    Octobre 2015
    Messages
    7
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 26
    Localisation : France, Finistère (Bretagne)

    Informations professionnelles :
    Activité : Lycéen

    Informations forums :
    Inscription : Octobre 2015
    Messages : 7
    Points : 0
    Points
    0
    Par défaut Je ne comprends pas ceci dans les sockets en C
    Bonjour, j'ai suivi le cours sur les sockets en C de developpez.com : http://broux.developpez.com/articles/c/sockets/ . Et dans l'exemple de serveur TCP/IP qu'il donne, je ne comprends pas l'intérêt de ce if ternaire : max = csock > max ? csock : max;
    Ainsi que l'intérêt de l'instruction qui suit le if ternaire : FD_SET(csock, &rdfs);


    Si quelqu'un pouvait m'éclairer là-dessus ça serait sympa !

    Merci d'avance ! (je suis un grand débutant en programmation)

  2. #2
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 369
    Points : 23 623
    Points
    23 623
    Par défaut
    Bonjour et bienvenue,

    La réponse se trouve dans la section 4.1 du cours que tu cites.

    FD_SET() est une macro qui sert à marquer les descripteurs à faire surveiller par l'appel select() qui, lui, n'est pas spécialement lié aux sockets en particulier mais qui trouve quand même son usage principal dans leur domaine. Pour faire simple, select() est un appel bloquant, comme les accès sur les fichiers (ou flux) ordinaires, et qui se débloque dès lors qu'une opération spécifiée (lecture, écriture, ou cas spéciaux) cesserait elle-même d'être bloquante si elle était effectuée individuellement sur au moins un des descripteurs fournis.

    Autrement dit, select() se met à l'écoute de tous les descripteurs fournis en même temps et rend la main dès que l'un d'eux est disponible. On comprend que ça va être particulièrement utile dans le cas de la gestion d'un serveur aux multiples clients.

    Or, les descripteurs de fichiers proprement dits ne sont que des entiers servant d'index dans un grand tableau. Tous les descripteurs ouverts ne sont pas concernés par l'appel mais surtout, il n'y a rien a priori qui nous informe sur la taille du tableau ni sur le nombre total de descripteurs ouverts possédés par un processus. Par conséquent, select() demande à recevoir en argument « le numéro du plus grand descripteur à gérer + 1 ». Et comme accept() gère chaque nouvelle connexion entrante à l'aide d'un socket propre, on reçoit à chaque fois un nouveau numéro.

    La ligne en question sert donc uniquement à garder trace du plus haut numéro de descripteur actuellement géré pour pouvoir en informer select().

    FD_SET() sert ensuite à indiquer, dans cette plage de 0 à n, quels sont les descripteurs qui nous intéressent vraiment. En outre, il faut le faire à chaque tour car une fois appelé, tous les flags dressés par FD_SET() sont remis à zéro et parce que select() se sert de ce même moyen pour indiquer, en retour, quels sont les descripteurs qui se sont débloqués, car il peut y en avoir plusieurs à la fois.

    Bon courage.

  3. #3
    Nouveau Candidat au Club
    Homme Profil pro
    Lycéen
    Inscrit en
    Octobre 2015
    Messages
    7
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 26
    Localisation : France, Finistère (Bretagne)

    Informations professionnelles :
    Activité : Lycéen

    Informations forums :
    Inscription : Octobre 2015
    Messages : 7
    Points : 0
    Points
    0
    Par défaut
    Merci beaucoup !!! Mais je n'ai toujours pas compris ceci :

    "FD_SET() sert ensuite à indiquer, dans cette plage de 0 à n, quels sont les descripteurs qui nous intéressent vraiment. En outre, il faut le faire à chaque tour car une fois appelé, tous les flags dressés par FD_SET() sont remis à zéro et parce que select() se sert de ce même moyen pour indiquer, en retour, quels sont les descripteurs qui se sont débloqués, car il peut y en avoir plusieurs à la fois."

    de qu'elle plage parles-tu ? pour la ligne : "FD_SET(csock, &rdfs);" la seule chose que je pense avoir compris, c'est qu'on ajoute un socket d'un nouveau client à notre ensemble de descripteurs de fichiers nommé "rdfs", et que aussitôt ce socket ajouté, on remet à 0 rdfs. Donc déjà là je vois pas trop l'intérêt d'ajouter si c'est pour l'enlever aussitôt Je suis un grand débutant, j'imagine que pour toi ce que tu me dis te parait évident !

    Merci

  4. #4
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 369
    Points : 23 623
    Points
    23 623
    Par défaut
    Citation Envoyé par VicPi Voir le message
    de qu'elle plage parles-tu ? pour la ligne : "FD_SET(csock, &rdfs);" la seule chose que je pense avoir compris, c'est qu'on ajoute un socket d'un nouveau client à notre ensemble de descripteurs de fichiers nommé "rdfs",
    En fait, on va surtout « marquer » le descripteur en question dans l'ensemble rdfs (qui devrait en toute logique être nommé « rfds » d'ailleurs) de façon à demander à select() de le surveiller avec les autres.

    et que aussitôt ce socket ajouté, on remet à 0 rdfs. Donc déjà là je vois pas trop l'intérêt d'ajouter si c'est pour l'enlever aussitôt Je suis un grand débutant, j'imagine que pour toi ce que tu me dis te parait évident !
    Effectivement, après une vérification plus approfondie, il semble qu'à cet endroit précis, le FD_SET() ne sert à rien… Il y a quelques autres erreurs, notamment le fait d'utiliser des conditions exclusives « if … else if … else » pour traiter le résultat de select(), ce qui fait que tout ce qui est envoyé par les clients déjà connectés au moment où le client suivant arrive est perdu.

    Il a d'autres ressources concernant ce sujet disponibles ici, comme cette discussion qui avait permis de mettre pas mal de choses en lumière.

    Bon courage.

  5. #5
    Nouveau Candidat au Club
    Homme Profil pro
    Lycéen
    Inscrit en
    Octobre 2015
    Messages
    7
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 26
    Localisation : France, Finistère (Bretagne)

    Informations professionnelles :
    Activité : Lycéen

    Informations forums :
    Inscription : Octobre 2015
    Messages : 7
    Points : 0
    Points
    0
    Par défaut
    D'accord, merci beaucoup, je vais aller voir ton lien quand j'aurai le temps, en espérant qu'il me soit utile

    Sinon, aurai tu un autre code de serveur/client TCP / IP, si possible un chat, sans erreur ?

    à moins que ton lien me permette de corriger ces erreurs ?

    car en fait, qu'est ce que je dois modifié finalement dans mon code ?


    Merci bcp encore une fois !

  6. #6
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 369
    Points : 23 623
    Points
    23 623
    Par défaut
    Citation Envoyé par VicPi Voir le message
    D'accord, merci beaucoup, je vais aller voir ton lien quand j'aurai le temps, en espérant qu'il me soit utile

    Sinon, aurai tu un autre code de serveur/client TCP / IP, si possible un chat, sans erreur ?
    J'ai posté le mien à la page 3 de ladite discussion. :-)

    à moins que ton lien me permette de corriger ces erreurs ?
    car en fait, qu'est ce que je dois modifié finalement dans mon code ?
    Pour te répondre, il faudrait qu'on puisse jeter un œil dessus.
    Poste-le ici, encadré par les balises [code] et [/code] (avec le bouton « # » de l'éditeur de message) et on tâchera de t'aider.

    Bon courage.

  7. #7
    Nouveau Candidat au Club
    Homme Profil pro
    Lycéen
    Inscrit en
    Octobre 2015
    Messages
    7
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 26
    Localisation : France, Finistère (Bretagne)

    Informations professionnelles :
    Activité : Lycéen

    Informations forums :
    Inscription : Octobre 2015
    Messages : 7
    Points : 0
    Points
    0
    Par défaut
    Citation Envoyé par Obsidian Voir le message
    J'ai posté le mien à la page 3 de ladite discussion. :-)



    Pour te répondre, il faudrait qu'on puisse jeter un œil dessus.
    Poste-le ici, encadré par les balises [code] et [/code] (avec le bouton « # » de l'éditeur de message) et on tâchera de t'aider.

    Bon courage.
    D'accord Je lirai toute cette discussion ce week-end toute façon !

    Mon code est séparé en plusieurs fichiers et est assez long, je l'ai upload ici, ça sera plus simple pour toi comme pour moi ! https://mega.nz/#!a5kCHB4A!xs1mAFlpW...JLgpSfPnQVlwb8

    Merci

  8. #8
    Nouveau Candidat au Club
    Homme Profil pro
    Lycéen
    Inscrit en
    Octobre 2015
    Messages
    7
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 26
    Localisation : France, Finistère (Bretagne)

    Informations professionnelles :
    Activité : Lycéen

    Informations forums :
    Inscription : Octobre 2015
    Messages : 7
    Points : 0
    Points
    0
    Par défaut
    Sinon je peux mettre mon code uniquement avec les balises, mais ça sera long... comme vous voulez, vous êtes les seuls personnes qui puissent corriger mes erreurs !

  9. #9
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 369
    Points : 23 623
    Points
    23 623
    Par défaut
    Citation Envoyé par VicPi Voir le message
    Sinon je peux mettre mon code uniquement avec les balises, mais ça sera long... comme vous voulez, vous êtes les seuls personnes qui puissent corriger mes erreurs !
    Tes fichiers sources font chacun moins de cent lignes, tu peux donc raisonnablement les déposer ici avec les balises CODE. Ce sera meilleur pour la pérennité du sujet.

  10. #10
    Nouveau Candidat au Club
    Homme Profil pro
    Lycéen
    Inscrit en
    Octobre 2015
    Messages
    7
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 26
    Localisation : France, Finistère (Bretagne)

    Informations professionnelles :
    Activité : Lycéen

    Informations forums :
    Inscription : Octobre 2015
    Messages : 7
    Points : 0
    Points
    0
    Par défaut
    Citation Envoyé par Obsidian Voir le message
    Tes fichiers sources font chacun moins de cent lignes, tu peux donc raisonnablement les déposer ici avec les balises CODE. Ce sera meilleur pour la pérennité du sujet.
    D'accord, alors en fait, je résume la situation, je voulais faire un chat en console en C, donc je me suis renseigné sur les sockets en C au moyen du tutoriel de developpez.com, et je me suis rendu compte que l'exemple de client/serveur qui était donné, était en fait un chat, donc là je n'ai que fait copier/coller le code, je l'ai réparti dans différents fichiers, et je les commenté (pour m'assurer d'avoir bien compris ce qu'il se passer).

    Voici la partie CLIENT :

    main.c

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    #include <stdio.h>
    #include <stdlib.h>
    // définit le mécanisme qui permet de savoir si une fonction système ou une fonction de la bibliothèque standard a rencontré une erreur. Elle écrit alors une valeur entière/son code dans errno.
    #include <errno.h>
    #include <string.h>
     
    #include "client.h"
     
    int main(int argc, char **argv) // passage d'arguments à la fonction main : adresse du serveur et pseudo du client
    {
       if(argc < 2)
       {
          printf("Usage : %s [server address] [pseudo]\n", argv[0]);
          return EXIT_FAILURE;
       }
     
       system("clear");
       /* initialise une DLL permettant d'utiliser les sockets sous Windows */
       init(); 
     
       const char *address = argv[1]; // chaîne contenant l'adresse du serveur
       const char *name = argv[2]; // chaîne contenant le pseudo du client
     
    	/* variable contenant le point d'entrée de la connexion permettant la 
    	communication entre un système unique et un réseau utilisant le protocole TCP/IP */ 
     
       SOCKET sock = init_connection(address); // création du socket et connexion au serveur
       char buffer[BUF_SIZE]; //bufffer (zone de mémoire vive) permettant de stocker une chaîne de caractère
     
       /*
    	recv est une fonction bloquante, l'application sera bloquée tant qu'il n'y a rien à lire sur le socket.
    	comme on a pas de marqueur de fin ou de taille de bloc définis, il faut utiliser la fonction select qui supervise 
    	efficacement plusieurs descripteurs de fichiers pour vérifier si l'un d'entre eux est ou 	devient « prêt » ; 
    	c'est-à-dire savoir si des entrées-sorties deviennent possibles ou si une « condition exceptionnelle » est survenue sur l'un des descripteurs..
    	readfds est un paramètre de select() et un ensemble de descripteurs de fichiers. Avec ce paramètre les descripteurs seront surveillés pour voir si des données 
    	en lecture sont disponibles, un appel à recv par exemple, ne sera donc pas bloquant.
       */
     
       fd_set rdfs; 
     
       /* readfds = ensemble de descripteurs qui seront surveillés pour voir
        si des données en lecture sont disponnibles, un appel à recv par exemple, ne sera
        donc pas bloquant */
     
       write_server(sock, name); // envoie notre pseudo au serveur
     
       /* Après chaque appel à select on vérifie les différents descripteurs de nos ensembles,
        une fois les données traitées, il faut remettre nos descripteurs dans nos ensembles. */
     
       while(1)
       {
          FD_ZERO(&rdfs); // vide l'ensemble de descripteurs de fichiers readfds
     
          // descripteur de fichier associé au flux stdin (flux d'entrée d'entrée standard)
          FD_SET(STDIN_FILENO, &rdfs); // ajoute le descripteur STDIN_FILENO à notre ensemble de descripteurs de fichiers 
     
          /* ajoute le socket */
          FD_SET(sock, &rdfs);
     
          /* l'état des ou d'un descripteur de fichiers ainsi que les ensembles n'ont pas été modifiés */
          if(select(sock + 1, &rdfs, NULL, NULL, NULL) == -1) 
          {
          	// Cette fonction affiche, sur la sortie d'erreur stderr et sous forme textuelle, le message d'erreur associé à la variable errno. Par défaut, la sortie d'erreur est associée à la console.
             perror("select()");
             exit(errno); // on indique l'état de fin du programme
          }
     
          /* quelque chose de disponible sur l'entré standard */
          if(FD_ISSET(STDIN_FILENO, &rdfs)) 
          {
          	fgets(buffer, BUF_SIZE - 1, stdin); //l'équivalent de gets mais en version sécurisée, permettant de contrôler le nombre de caractères écrits en mémoire : évite le buffer overflow. 
             {
                char *p = NULL;
                p = strstr(buffer, "\n"); // strstr retourne un pointeur sur l'élément de buffer où commence "\n".
                if(p != NULL)
                {
                   *p = 0; // on enlève le retour chariot
                }
                else
                {
                   /* fclean */
                   buffer[BUF_SIZE - 1] = 0; // on supprime le dernier caractère
                }
             }
             write_server(sock, buffer); // on envoie notre message au serveur
          }
          else if(FD_ISSET(sock, &rdfs)) // des données sont disponibles sur le socket, traitement des données
          {
             int n = read_server(sock, buffer);
             /* serveur down */
             if(n == 0)
             {
                printf("Server disconnected !\n");
                break;
             }
             puts(buffer); // un message est reçu, nous l'affichons et nous repartons dans l'attente d'un événement.
          }
       }
     
       /* ferme le descripteur sock, de manière à ce qu'il ne référence plus aucun fichier, et puisse être réutilisé. */
       end_connection(sock);
     
       end(); // libère la DLL qui permet d'utiliser les sockets sous Windows
     
       return EXIT_SUCCESS;
    }
    fonctions_client.c

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    #include <stdio.h>
    #include <stdlib.h>
    #include <errno.h>
    #include <string.h>
     
    #include "client.h"
     
    int init_connection(const char *address)
    {
     
       /* En mode AF_INET, ce sont les numéros de port qui donnent le point de rendez-vous.
    	   SOCK_STREAM = TCP */
       SOCKET sock = socket(AF_INET, SOCK_STREAM, 0); 
       /* on récupère les informations de l'hôte auquel on veut se connecter et on les met dans la structure sin */
       SOCKADDR_IN sin = { 0 }; 
       struct hostent *hostinfo; 
     
       if(sock == INVALID_SOCKET) 
       {
          perror("socket()");
          exit(errno);
       }
     
       hostinfo = gethostbyname(address);
       if (hostinfo == NULL) // l'hôte n'existe pas
       {
          fprintf (stderr, "Unknown host %s.\n", address);
          exit(EXIT_FAILURE);
       }
     
       /* l'adresse se trouve dans le champ h_addr de la structure hostinfo */
       /* un pointeur de structure est utilisé car gethostbyname() renvoie un struct hostent* */
       /* hostinfo possède un pointeur sur un élément nommé h_addr.
    	  On cast ce pointeur en un pointeur sur IN_ADDR.
    	  Et on dit que sin.sin_addr prend la valeur pointé par cet élément.
    	  Donc sin_addr prend comme valeur un objet de type IN_ADDR. */
     
       sin.sin_addr = *(IN_ADDR *) hostinfo->h_addr; 
     
       /* convertit  un  entier  court  (short)
       hostshort  depuis  l'ordre des octets de l'hôte vers celui
       du réseau. */
     
       sin.sin_port = htons(PORT); 
       sin.sin_family = AF_INET; // spécifie que l'on utilise le protocole IP
     
       if(connect(sock,(SOCKADDR *) &sin, sizeof(SOCKADDR)) == SOCKET_ERROR) // connexion au serveur
       {
          perror("connect()");
          exit(errno);
       }
     
       return sock;
    }
     
    /* close() ferme le descripteur sock, de manière à ce qu'il ne référence plus aucun fichier, et puisse être réutilisé. */
    void end_connection(int sock)
    {
       closesocket(sock);
    }
     
    int read_server(SOCKET sock, char *buffer)
    {
       int n = 0;
     
    /* recv est à utiliser pour le mode connecté. Elle reçoit sur le socket sock, 
    des données qu'elle stockera à l'endroit pointé par buffer, pour une taille 
    maximale de BUF_SIZE - 1 octets, dans le but d'enlever le retour du chariot */
     
       if((n = recv(sock, buffer, BUF_SIZE - 1, 0)) < 0) // le flag 0 indique une lecture normale
       {
          perror("recv()");
          exit(errno);
       }
     
       buffer[n] = 0;
     
       return n; // recv() renvoie le nombre d'octets lus
    }
     
    /* envoie du texte au serveur */
    void write_server(SOCKET sock, const char *buffer) 
    {
       if(send(sock, buffer, strlen(buffer), 0) < 0)
       {
          perror("send()");
          exit(errno);
       }
    }
    init_end_client.c (sur windows)

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    #include <stdio.h>
    #include <stdlib.h>
    #include <errno.h>
    #include <string.h>
     
    #include "client.h"
     
    void init(void)
    {
    #ifdef WIN32
       WSADATA wsa;
       int err = WSAStartup(MAKEWORD(2, 2), &wsa);
       if(err < 0)
       {
          puts("WSAStartup failed !");
          exit(EXIT_FAILURE);
       }
    #endif
    }
     
    void end(void)
    {
    #ifdef WIN32
       WSACleanup();
    #endif
    }
    client.h

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    #ifndef CLIENT_H
    #define CLIENT_H
     
    #ifdef WIN32
     
    #include <winsock2.h>
     
    #elif defined (linux)
     
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <unistd.h> /* close */
    #include <netdb.h> /* gethostbyname */
    #define INVALID_SOCKET -1
    #define SOCKET_ERROR -1
    #define closesocket(s) close(s)
    typedef int SOCKET;
    typedef struct sockaddr_in SOCKADDR_IN;
    typedef struct sockaddr SOCKADDR;
    typedef struct in_addr IN_ADDR;
     
    #else
     
    #error not defined for this platform
     
    #endif
     
    #define CRLF	 "\r\n"
    #define PORT	 1977
     
    #define BUF_SIZE 1024
     
    void init(void);
    void end(void);
    int init_connection(const char *address);
    void end_connection(int sock);
    int read_server(SOCKET sock, char *buffer);
    void write_server(SOCKET sock, const char *buffer);
     
    #endif
    et maintenant la partie SERVEUR :

    main.c

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    #include <stdio.h>
    #include <stdlib.h>
    #include <errno.h>
    #include <string.h>
     
    #include "server.h"
    #include "client.h"
     
    int main(int argc, char **argv)
    {
       init();
     
       SOCKET sock = init_connection();
       char buffer[BUF_SIZE];
       /* l'index du tableau */
       int actual = 0;
       int max = sock;
       /* un tableau pour tous les clients */
       Client clients[MAX_CLIENTS];
     
       /* ensemble de descripteurs de fichiers qui seront surveillés
          pour voir si des données en lecture sont disponibles */
       fd_set rdfs; 
     
       while(1)
       {
          int i = 0;
          /* vide l'ensemble rdfs */
          FD_ZERO(&rdfs);
          /* ajoute STDIN_FILENO à l'ensemble rdfs */ 
          FD_SET(STDIN_FILENO, &rdfs); 
          /* ajoute le socket à l'ensemble rdfs */
          FD_SET(sock, &rdfs);
     
          /* ajoute le socket de chaque client */
          for(i = 0; i < actual; i++)
          {
             FD_SET(clients[i].sock, &rdfs);
          }
    	  /* l'état des ou d'un descripteur de fichiers ainsi que les ensembles n'ont pas été modifiés */
          if(select(max + 1, &rdfs, NULL, NULL, NULL) == -1)
          {
             perror("select()");
             exit(errno);
          }
     
          /* quelque chose de disponible sur l'entré standard */
          if(FD_ISSET(STDIN_FILENO, &rdfs))
          {
             /* si quelque chose est saisit au clavier nous arrêtons le programme */
             break;
          }
          else if(FD_ISSET(sock, &rdfs))
          {
             /* nouveau client */
             SOCKADDR_IN csin = { 0 };
             size_t sinsize = sizeof csin;
             /* accept renvoie un nouveau socket qui sera utiliser pour communiquer avec le client */
             /* elle remplie aussi une structure SOCKADDR_IN avec les informations du client (IP et port) */ 
             int csock = accept(sock, (SOCKADDR *)&csin, (socklen_t *)&sinsize);
             if(csock == SOCKET_ERROR)
             {
                perror("accept()");
                continue; // provoque l'arrêt de l'itération courante, et le passage au début de l'itération suivante. 
             }
     
             /* après s'être connecté, le client envoie son nom */
             if(read_client(csock, buffer) == 0)
             {
                /* déconnecté */
                continue;
             }
     
             /* what is the new maximum fd ? */
             max = csock > max ? csock : max;
     
             FD_SET(csock, &rdfs);
     
             // nous créons un objet de type Client, contenant le socket pour discuter avec lui ainsi que son pseudo.
     
             Client c = { csock };
             strncpy(c.name, buffer, BUF_SIZE - 1);
             clients[actual] = c;
             actual++;
          }
          else
          {
             int i = 0;
             for(i = 0; i < actual; i++)
             {
                /* un client parle */
                if(FD_ISSET(clients[i].sock, &rdfs))
                {
                   Client client = clients[i];
                   int c = read_client(clients[i].sock, buffer);
                   /* client déconnecté */
                   if(c == 0)
                   {
                      closesocket(clients[i].sock);
                      remove_client(clients, i, &actual);
                      strncpy(buffer, client.name, BUF_SIZE - 1);
                      strncat(buffer, " disconnected !", BUF_SIZE - strlen(buffer) - 1);
                      send_message_to_all_clients(clients, client, actual, buffer, 1);
                   }
                   else
                   {
                      send_message_to_all_clients(clients, client, actual, buffer, 0);
                   }
                   break;
                }
             }
          }
       }
     
       clear_clients(clients, actual);
       end_connection(sock);
     
       end();
     
       return EXIT_SUCCESS;
    }
    fonctions_serveur.c

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    #include <stdio.h>
    #include <stdlib.h>
    #include <errno.h>
    #include <string.h>
     
    #include "server.h"
    #include "client.h"
     
    void clear_clients(Client *clients, int actual)
    {
       int i = 0;
       for(i = 0; i < actual; i++)
       {
          closesocket(clients[i].sock);
       }
    }
     
    void remove_client(Client *clients, int to_remove, int *actual)
    {
       /* we remove the client in the array */
       memmove(clients + to_remove, clients + to_remove + 1, (*actual - to_remove - 1) * sizeof(Client));
       /* number client - 1 */
       (*actual)--;
    }
     
    void send_message_to_all_clients(Client *clients, Client sender, int actual, const char *buffer, char from_server)
    {
       int i = 0;
       char message[BUF_SIZE];
       message[0] = 0;
       for(i = 0; i < actual; i++)
       {
          /* ne n'envoyons pas le message à l'envoyeur */
          if(sender.sock != clients[i].sock)
          {
             if(from_server == 0)
             {
                strncpy(message, sender.name, BUF_SIZE - 1);
                strncat(message, " : ", sizeof message - strlen(message) - 1);
             }
             strncat(message, buffer, sizeof message - strlen(message) - 1);
             write_client(clients[i].sock, message);
          }
       }
    }
     
    int init_connection(void)
    {
       SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
       SOCKADDR_IN sin = { 0 };
     
       if(sock == INVALID_SOCKET)
       {
          perror("socket()");
          exit(errno);
       }
       /* convertit  un  entier  long  hostlong depuis l'ordre des octets de l'hôte vers celui du réseau. */
       /* nous sommes un serveur, nous acceptons n'importe qu'elle adresse */
       sin.sin_addr.s_addr = htonl(INADDR_ANY);
       sin.sin_port = htons(PORT);
       sin.sin_family = AF_INET; 
       /*  bind lie un socket avec notre interface de connexion (structure sockaddr). */
       if(bind(sock,(SOCKADDR *) &sin, sizeof sin) == SOCKET_ERROR) 
       {
          perror("bind()");
          exit(errno);
       }
       /* définie la taille de la file de connexions en attente pour notre socket */
       if(listen(sock, MAX_CLIENTS) == SOCKET_ERROR)
       {
          perror("listen()");
          exit(errno);
       }
     
       return sock;
    }
     
    void end_connection(int sock)
    {
       closesocket(sock);
    }
     
    int read_client(SOCKET sock, char *buffer)
    {
       int n = 0;
     
       if((n = recv(sock, buffer, BUF_SIZE - 1, 0)) < 0)
       {
          perror("recv()");
          /* if recv error we disonnect the client */
          n = 0;
       }
     
       buffer[n] = 0;
     
       return n;
    }
     
    void write_client(SOCKET sock, const char *buffer)
    {
       if(send(sock, buffer, strlen(buffer), 0) < 0)
       {
          perror("send()");
          exit(errno);
       }
    }
    init_end_serveur.c

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    #include <stdio.h>
    #include <stdlib.h>
    #include <errno.h>
    #include <string.h>
     
    #include "server.h"
    #include "client.h"
     
    void init(void)
    {
    #ifdef WIN32
       WSADATA wsa;
       int err = WSAStartup(MAKEWORD(2, 2), &wsa);
       if(err < 0)
       {
          puts("WSAStartup failed !");
          exit(EXIT_FAILURE);
       }
    #endif
    }
     
    void end(void)
    {
    #ifdef WIN32
       WSACleanup();
    #endif
    }
    client.h

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    #ifndef CLIENT_H
    #define CLIENT_H
     
    #include "server.h"
     
    typedef struct
    {
       SOCKET sock;
       char name[BUF_SIZE];
    }Client;
     
    #endif
    serveur.h

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    #ifndef SERVER_H
    #define SERVER_H
     
    #ifdef WIN32
     
    #include <winsock2.h>
     
    #elif defined (linux)
     
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <unistd.h> /* close */
    #include <netdb.h> /* gethostbyname */
    #define INVALID_SOCKET -1
    #define SOCKET_ERROR -1
    #define closesocket(s) close(s)
    typedef int SOCKET;
    typedef struct sockaddr_in SOCKADDR_IN;
    typedef struct sockaddr SOCKADDR;
    typedef struct in_addr IN_ADDR;
     
    #else
     
    #error not defined for this platform
     
    #endif
     
    #define CRLF		"\r\n"
    #define PORT	 	1977
    #define MAX_CLIENTS 	100
     
    #define BUF_SIZE	1024
     
    #include "client.h"
     
    void init(void);
    void end(void);
    int init_connection(void);
    void end_connection(int sock);
    int read_client(SOCKET sock, char *buffer);
    void write_client(SOCKET sock, const char *buffer);
    void send_message_to_all_clients(Client *clients, Client client, int actual, const char *buffer, char from_server);
    void remove_client(Client *clients, int to_remove, int *actual);
    void clear_clients(Client *clients, int actual);
     
    #endif
    Voilà ! Merci d'avance Obsidian et les autres !

Discussions similaires

  1. Réponses: 17
    Dernier message: 03/09/2006, 19h46
  2. Réponses: 2
    Dernier message: 26/07/2006, 10h53
  3. Réponses: 4
    Dernier message: 11/07/2006, 21h43
  4. ne pas ecrire dans les fichier log
    Par HULK dans le forum MS SQL Server
    Réponses: 1
    Dernier message: 07/04/2006, 08h22
  5. Je ne comprend pas très bien les templates...
    Par Paulinho dans le forum C++
    Réponses: 5
    Dernier message: 07/11/2005, 10h24

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo