Bonjour à tous,

J'ai comme projet de faire un serveur de messagerie de groupe.

Pour les messages "normaux" entre clients et serveur, je dois utiliser le protocole TCP, et pour les messages de commande type requêtes/réponses ( par exemple le client qui envoie une commande /who pour savoir qui est connecté sur le serveur), je dois utiliser le protocole UDP.

Voilà le code client :

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
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
 
 
int main(int argc, char **argv)
{
  int sockfd;                   // descripteur de socket
  struct sockaddr_in serveur;   // structure d'adresse qui contiendra les param reseaux du serveur
  fd_set readfds;               // ensemble des descripteurs en lecture qui seront surveilles par select
  char buf[1024];               // espace necessaire pour stocker le message recu
  char buf2[1024];              // espace necessaire pour envoyer un message au serveur
 
  memset(buf,'\0',1024);        // initialisation du buffer qui sera utilisé
  memset(buf2,'\0',1024);       // initialisation de l'autre buffer qui sera utilisé
 
  // verification du nombre d'arguments sur la ligne de commande
  if(argc != 3)
  {
      printf("Usage: %s @serveur port_serveur\n", argv[0]);
      exit(-1);
  }
 
  // creation de la socket
  sockfd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
 
  // initialisation de la structure d'adresse du serveur :
 
  // famille d'adresse
  serveur.sin_family = AF_INET;
 
  // recuperation de l'adresse IPv4 du serveur
  inet_aton(argv[1], &(serveur.sin_addr));
 
  // recuperation du port du serveur
  serveur.sin_port = htons(atoi(argv[2]));
 
  printf("Tentative de connexion\n");
 
  // tentative de connexion
  if(connect(sockfd,(struct sockaddr*)&serveur,sizeof(serveur)) == -1)
  {
      perror("Erreur de connexion -> ");
      exit(2);
  }
 
  printf("Connexion etablie\n");
 
  while(1)
  {
           memset(buf,'\0',1024);  // on reinitialise les buffers qui seront utilises
           memset(buf2,'\0',1024);
 
           FD_ZERO(&readfds);    // il faut remettre tt les elements ds readfds a chaque recommencement de la boucle, vu que select modifie les ensembles
           FD_SET(0,&readfds);  // on rajoute l'entree standard
           FD_SET(sockfd,&readfds); // on rajoute la socket de communication avec le serveur
 
           if(select(sockfd+1,&readfds,NULL,NULL,NULL) == -1)
           {
                perror("Erreur lors de l'appel a select -> ");
                exit(1);
           }
 
           if(FD_ISSET(0,&readfds))
           {      // si l'entree standard est dans readfds, alors l'utilisateur en en train de rédiger un message a envoyer
                if(read(0,buf2,1024) == -1)
                {                             // on lit donc ce qui arrive sur l'entrée standard
                     perror("Erreur lors de l'appel a read -> ");
                     exit(1);
                }
 
                if(send(sockfd,buf2,1024,0) == -1)
                {                      // puis on l'envoie au serveur
                     perror("Erreur lors de l'appel a send -> ");
                     exit(1);
                }                
           }    
 
           if(FD_ISSET(sockfd,&readfds))
           {     // si la socket de communication est dans readfds, alors le serveur nous a envoye un message
               if(recv(sockfd,&buf,1024,0) == -1)
               {
                    perror("Erreur lors de la reception -> ");
                    exit(4);
               }
 
               printf("La chaine recue est: %s\n",buf);            // on l'affiche
           }
  }
}
Dans FD_ISSET(0,&read_fds) (ligne 69), il faudra donc que je récupères ce que le client tape (avec un fgets ?), et envoyer le message en UDP ou TCP en fonction de ce qu'il rentre. (si chaîne rentrée = /who alors je l'envoie en UDP, sinon envoie en TCP). Pour ça, pas trop de problèmes je pense, vous m'arrêtez si je me trompe.

Le problème est dans le serveur. Voici le code :

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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
 
 
int main(int argc, char **argv)
{
    int sockfd, sockfd2;          // descripteurs de socket
    fd_set readfds;               // ensemble des descripteurs en lecture qui seront surveilles par select
    int t[FD_SETSIZE];            // tableau qui contiendra tous les descripteurs de sockets, avec une taille egale a la taille max de l'ensemble d'une structure fd_set
    int taille=0;                 // nombre de descripteurs dans le tableau precedent
    char buf[1024];               // espace necessaire pour stocker le message recu
 
    memset(buf,'\0',1024);        // initialisation du buffer qui sera utilisé
 
    struct sockaddr_in my_addr;   // structure d'adresse qui contiendra les param reseaux du recepteur
    struct sockaddr_in client;    // structure d'adresse qui contiendra les param reseaux de l'expediteur
 
    // taille d'une structure sockaddr_in utile pour la fonction recvfrom
    socklen_t sin_size = sizeof(struct sockaddr_in);
 
    // verification du nombre d'arguments sur la ligne de commande
    if(argc != 2)
    {
      printf("Usage: %s port_local\n", argv[0]);
      exit(-1);
    }
 
    // creation de la socket
    sockfd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
 
    // initialisation de la structure d'adresse du serveur (pg local)
 
    // famille d'adresse
    my_addr.sin_family = AF_INET;
 
    // recuperation du port du serveur
    my_addr.sin_port = ntohs(atoi(argv[1]));
 
    // adresse IPv4 du serveur
    my_addr.sin_addr.s_addr=htonl(INADDR_ANY);
 
    // association de la socket et des param reseaux du serveur
    if(bind(sockfd,(struct sockaddr*)&my_addr,sizeof(my_addr)) != 0)
    {
      perror("Erreur lors de l'appel a bind -> ");
      exit(1);
    }
 
    // indication de la limite MAX de la file d'attente des connexions entrantes
    if(listen(sockfd,10) != 0)
    {
      perror("Erreur lors de l'appel a listen -> ");
      exit(2);
    }
 
    printf("Attente de connexion\n");
 
    t[0]=sockfd;    // on ajoute deja la socket d'ecoute au tableau de descripteurs
    taille++;       // et donc on augmente "taille"
 
    while(1)
    {               
              FD_ZERO(&readfds);  //il faut remettre tt les elements ds readfds a chaque recommencement de la boucle, vu que select modifie les ensembles
              FD_SET(sockfd, &readfds); // ajoute le descripteur sockfd a l'ensemble readfs
              int j;
              int sockmax=0;
 
              for(j=0;j<taille;j++)
              {
                     if(t[j] != 0) 
                     {             
                                FD_SET(t[j],&readfds);  // on remet donc tous les elements dans readfds
                     }
                     if(sockmax < t[j])                 // et on prend ici le "numero" de socket maximal pour la fonction select
                     {
                                sockmax = t[j];
                     }
              }
 
              if(select(sockmax+1,&readfds,NULL,NULL,NULL) == -1)
              {      // on utilise le select sur toutes les sockets y compris celle d'ecoute 
                      perror("Erreur lors de l'appel a select -> ");
                      exit(1);
              }
 
              if(FD_ISSET(sockfd,&readfds))
              {     // si la socket d'ecoute est dans readfds, alors qqch lui a ete envoye (=connection d'un client)
                    if((sockfd2 = accept(sockfd,(struct sockaddr*)&client,&sin_size)) == -1)
                    {     // on accepte la connexion entrante et on cree une socket...
                                perror("Erreur lors de accept -> ");
                                exit(3);
                    }
 
                    printf("Connexion etablie avec %s\n", inet_ntoa(client.sin_addr));
                    taille++;      // ...qui est donc ajoutee au tableau de descripteurs
                    t[taille-1]=sockfd2;
              }
 
              int i;
              for(i=1;i<taille;i++)
              {       // on parcourt tous les autres descripteurs du tableau
                          if(FD_ISSET(t[i],&readfds))
                          {     // si une socket du tableau est dans readfds, alors qqch a ete envoye au serveur par un client
                                if(recv(t[i],&buf,1024,0) == -1)
                                {      // on stocke alors le message
                                     perror("Erreur lors de la reception -> ");
                                     exit(4);
                                }
 
                                printf("La chaine recue est: %s\n",buf);
 
 
 
 
                                int k;
 
                                for(k=1;k<taille;k++)
                                {         // puis on l'envoie a tous les clients... 
                                    if(k != i)
                                    {     // ...sauf l'envoyeur
                                         if(send(t[k],buf,1024,0) == -1)
                                         {              
                                               perror("Erreur lors de l'appel a send -> ");
                                               exit(1);
                                         }
                                    }
                                }
                                memset(buf,'\0',1024); // reinitialisation du buffer
                          }       
            }
  }
}
Dans la boucle for(i...) et FD_ISSET(...) (à partir de la ligne 106) qui parcourt tous les autres descripteurs du tableau, je ne sais pas où ni comment placer mon recv et mon recvfrom. Comment le serveur peut savoir si il va avoir un message en UDP ou TCP ? Où dois-je placer les commandes nécessaires ?

J'espère avoir été clair, si c'est pas le cas n'hésitez pas à le dire et j'essaierais d'être plus précis !

Merci d'avance !