Prog en C : serveur TCP multiclient (Linux)

Fermé
Utilisateur anonyme - 23 oct. 2009 à 21:29
 Utilisateur anonyme - 10 févr. 2010 à 10:23
Salut à tous,

Me voici me voila, venant humblement implorer votre aide ^^
Comme dit dans le titre, je dois pour un TP de Réseaux et Protocoles créer 2 programmes, un client et un serveur pour discuter a plusieurs hé ouais la classe ;-) Windows Live Messenger tiens-toi bien j'arrive XD
Sauf que ! J'y arrive pas trop trop quoi...

En fait le client doit se connecter sur le serveur (qui ouvre un port choisi en ligne de commande, en fait on est sur le réseau de l'université); dès le lancement on peut écrire (sur l'entrée standard) notre message, qui sera envoyé au serveur (a priori après appui de la touche Entrée lol) dont le role est, après réception du message d'un client, de le renvoyer a tous les clients (sauf l'envoyeur) qui l'afficheront ensuite...
Bon par contre j'ai fait en sorte de faire afficher le message AUSSI par le serveur, pour voir si ça marchait bien, etc...

Le truc marrant en plus, c'est que pour faire ça on est OBLIGES d'utiliser la fonction select autant pour le client que pour le serveur(hé oui pas de fork ni de thread, on a pas le droit héhé), fonction que je ne critique pas bien sur non mais p***** de fonction de m?#!* !!!



Alors mon code compile (yeah victoire !), et fonctionne a moitié : le serveur a l'air de fonctionner comme il faut, mais le client, et d'une, m'affiche un truc a l'infini croyant que le serveur lui a envoyé un truc (c'est deja un truc que je comprends pas), et de 2 n'affiche pas la chaine qu'un autre client a envoyé alors que le serveur a a priori renvoyé la chaine aux autres clients (2ème incompréhension ^^).

D'ailleurs voici 2 screenshots des terminaux que j'ouvre et ou je lance les 2 progs :
http://img12.imageshack.us/img12/4172/shot1rp.jpg
http://img190.imageshack.us/img190/2774/shot2q.jpg



Et donc, voici en avant-premiere le code commenté du successeur de WLM ^^

serveur :
#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
            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);                                  // et on l'affiche
                                                        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);
                                                                                                             }
                                                                                   }
                                                                              }
                                                        }              // reste donc a resoudre le probleme de supprimer la socket du tableau, et modifier "taille"...
                            }
           }
}



client :
#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
                                        }
           }
}




J'ai essayé de commenter le mieux que je puisse...

Voila voila, donc je remercie par avance les courageux qui se plongeront dans mon code et qui pourront me dire ou est le probleme....ou plutot ou sont les problèmes :), je vous en serais vraiment très très très reconnaissant ^^

Mais de toute façon je suis preneur de toute remarque me permettant de m'améliorer !
A voir également:

4 réponses

Utilisateur anonyme
10 févr. 2010 à 09:57
salut, ton programme fonctionne très bien !

le seul "débogage" que j'ai fait c'est d'indenter correctement ce qui m'a permis d'ajouter les accolades manquantes.

si je peut me permettre un conseil tu devrais utiliser des noms de variables plus explicites, ex :

int i_sockserver,i_sockclient;
struct sockaddr_in sockaddr_server;

int main(int i_argc, char **ppc_argv)

#define BUFFER_SIZE 1024
char c_buffer[BUFFER_SIZE];

FILE *pf_fichier; //pointeur sur le stream
char *pc_fichier; //pointeur sur le non du fichier

c'est comme ça que je fait et ça évite souvent des bugs.

merci pour ton programme, c'est exactement ce que je cherchais.
1
dodoecchi Messages postés 454 Date d'inscription samedi 9 septembre 2006 Statut Membre Dernière intervention 26 avril 2012 29
23 oct. 2009 à 23:50
Quelles études tu fais?
je suis en 1ere année d'IUT Réseaux et télécommunications
je vais faire ça aussi? ou c'est le departement informatique d'à côté ?

Dorian
0
Utilisateur anonyme
25 oct. 2009 à 10:39
En fait la je suis en 3ème année de Licence d'informatique a Strasbourg ^^
Et pour ce qui est de l'IUT, ben je sais pas trop moi lol
On vous apprend à programmer ou pas ? Si oui, y'a de fortes chances pour que tu fasses ça un jour ou l'autre
(ce serait un comble qu'un IUT en réseaux ne voie pas les sockets :), mais tes profs te répondront surement mieux que moi...

Sinon tjrs personne pour mon problème ?
0
Utilisateur anonyme
10 févr. 2010 à 10:23
PS : select() est un trés bonne fonction !
0