/***********************************************************
  B. Dupouy / modifie par E. Lecolinet pour comm avec Java
  *********************************************************/

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


/* Ce fichier montre un exemple de "pseudo-serveur" pour communiquer
 * avec une interface graphique ecrite en Java (ou autre).
 * L'interface joue le role du "client" et envoie des requetes quand
 * l'utilisateur clique sur quelque chose. Le "serveur" receptionne
 * ces requetes, effectue un calcul a partir des donnees contenues
 * dans la requete et renvoie un resultat au client. Le client
 * (c'est-a-dire l'interface) recupere ce resultat et l'affiche
 * de maniere ad hoc
 *
 * Remarque: contrairement a un serveur classique il n'y pas de fork
 * ou equivalent: le serveur travaille en sequence avec son unique client
 * de telle sorte que les resultats soient affiches dans le bon ordre
 * pas l'interface. D'autres architectures pourraient egalement etre
 * employees a condition de gerer les eventuelles incoherences qui
 * pourraient alors survenir (si les actions de l'utilisateur sur
 * l'interface n'engendrent pas des "calculs" exactement dans le 
 * meme ordre
 */

/**************** Effectue le "calcul" demande par le client ********/ 

void Calcule(char *entree, char *sortie, int sortie_len) {
  static int no_calcul = 0;

  /* ne pas oublier le \n a la fin ! */  
  /* att: sortie est supposee etre "assez longue" ! */
  sprintf(sortie, "Calcul No= %d - Entree= [%s]\n", no_calcul++, entree);
}


/**************** Fonction de gestion des communications ************/ 

static void GereComm(int sock_com) {
  char entree[1024], sortie[1024];
  int readstat;
  int pos;
  static char entete[] = "Debut de comm.\n";

  if (write(sock_com, entete, strlen(entete)) < 0)
    perror("! Erreur ecriture message");

  pos = 0;
  do {
    if ((readstat = read(sock_com, entree + pos, sizeof entree - pos)) < 0) { 
      perror("! Erreur lecture Message") ;
      return;
    }
    if (readstat == 0) { 
      fprintf(stderr, "> Fin de connexion\n");
      return;
    } 
    pos += readstat;
  }

  while (entree[pos - 1] !='\n') ;

  entree[pos - 2] = 0;
  fprintf(stderr, ">Recu: lstr=[%s]\n", entree);
  
  Calcule(entree, sortie, sizeof(sortie)-1);
  
  fprintf(stderr, ">send: lstr=[%s]\n", sortie);
  if (write(sock_com, sortie, strlen(sortie)) < 0) {
    perror("! Erreur ecriture message") ;
    exit(2);
  }
}

/**************** Serveur *********************************************/ 

static char MESSAGE[] = "Reponse de pid                            \n";

int main(int argc, char *argv[]) {
  struct sockaddr_in le_serveur ;
  // int taille;
  socklen_t taille;
  int sock_serv;

  if (argc != 2) {
    printf("Usage: %s port\n", argv[0]);
    exit(1);
  }

  if ((sock_serv = socket(AF_INET,SOCK_STREAM,0)) < 0) {
    perror("Serveur: erreur ouverture socket en mode connecte\n") ;
    exit(1);
  }

  /* construction de l'adresse du serveur */
  le_serveur.sin_family      = AF_INET;
  le_serveur.sin_addr.s_addr = INADDR_ANY;
  le_serveur.sin_port        = htons(atoi(argv[1]));
  
  sprintf(MESSAGE+15, " %d fils de %d",(int)getpid(), (int)getppid());
  printf("Ici le serveur  %s\n", MESSAGE);
  
  taille = sizeof(le_serveur) ;
  
  if (bind(sock_serv, (struct sockaddr *)&le_serveur, taille) < 0) {
    perror("Serveur : erreur sur bind ");
    exit(1);
  }
  
  /* Verifications sur le serveur : */
  if (getsockname(sock_serv, (struct sockaddr *)&le_serveur, &taille) < 0) {
    perror("Serveur : erreur sur getsocketname ");
    exit(1);
  }
  printf(" Numero de mon port : %d\n", ntohs(le_serveur.sin_port)) ;
  printf(" **********************************\n"); 
  
  /* Le serveur se met en attente sur le socket d ecoute */
  listen(sock_serv, 5);
  
  /* Boucle d'attente des messages du client.
   * Envoie un "resultat" en retour.
   * Remarque: pas de fork: ce serveur communique sequentiellement
   * avec son unique client
   */
  while (1) {
    int sock_com;
    if ((sock_com = accept(sock_serv, NULL, NULL)) == -1) {
      perror(" Serveur : erreur sur Accept");
      continue;
    }
      
    /* gestion de la communication */
    GereComm(sock_com);
    close(sock_com);
  }
}


