/* serv_3_nonblock.c */

#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <errno.h>

#define PORT_NUMBER 60000
#define REQUETES 1 /* Empêche plus d'une connexion à la fois */
#define TAMPON 100

/* sortie avec un message d'erreur contextualisé */
void erreur(char *msg) {
  perror(msg);
  exit(1);
}

/* fonction qui "travaille" en parallèle du serveur */
int etat_travail=0;
char travail[4]={'-','\\','|','/'};
void travaille() {
  char buf[2]={0,0xD}; 
  buf[0]=travail[etat_travail];
    etat_travail = (etat_travail+1) & 3; /* incrémente modulo 4 */
  write(STDERR_FILENO, buf, 2); /* affiche l'état en cours */
  usleep(100*1000);   /* attente de 100ms */
}

int main(int argc, char *argv[]) {
  int sock_serveur, sock_client, port_number=PORT_NUMBER,
    len, flags;
  const int one = 1;
  char *env_port;
  struct sockaddr_in sockaddr_serveur, sockaddr_client;
  unsigned int taille_client = sizeof(sockaddr_client);
  char buffer[TAMPON];
  char *ack="Bonjour !\n";
  char *eot="Au revoir !\n";

  /* gestion du numéro du port */
  env_port = getenv("GHDL_TCPPORT");
  if (env_port != NULL) {
    port_number = atol(env_port);
    if ((port_number < 1024) || (port_number > 65535))
      erreur("Mauvais numéro de port");
  }

  /* Création d'une socket TCP */
  if ((sock_serveur = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
    erreur("Echec lors de la création de la socket");

  /* Fabrication de la structure sockaddr_in du serveur */
  memset(&sockaddr_serveur, 0, sizeof(sockaddr_serveur));
  sockaddr_serveur.sin_family = AF_INET;
  sockaddr_serveur.sin_addr.s_addr = htonl(INADDR_ANY);
  sockaddr_serveur.sin_port = htons(port_number);

#ifdef SO_REUSEADDR
  if (setsockopt(sock_serveur, SOL_SOCKET,
            SO_REUSEADDR, &one, sizeof(one)) < 0)
    erreur("setsockopt(SO_REUSEADDR)");
#endif
#ifdef SO_REUSEPORT
  if (setsockopt(sock_serveur, SOL_SOCKET,
             SO_REUSEPORT, &one, sizeof(one)) < 0)
    erreur("setsockopt(SO_REUSEPORT)");
#endif

  /* Branche la socket */
  if (bind(sock_serveur, (struct sockaddr *) &sockaddr_serveur,
                              sizeof(sockaddr_serveur)) < 0)
    erreur("Echec de bind() du serveur");

  /* Ecoute la socket */
  if (listen(sock_serveur, REQUETES) < 0)
    erreur("Echec de listen() du serveur");


  /* Débloque la socket */
  flags = fcntl(sock_serveur, F_GETFL);
  fcntl(sock_serveur, F_SETFL, flags | O_NONBLOCK);

  /* Boucle infinie */
  while (1) {
    printf("Attente sur le port %d\n", port_number);

    /* Boucle d'attente d'une connexion */
    while ((sock_client = accept(sock_serveur,
       (struct sockaddr *) &sockaddr_client, &taille_client)) < 0) {
      if ((errno == EWOULDBLOCK) || (errno == EAGAIN))
        travaille();
      else
        erreur("accept() : Echec d'attente de connexion");
    }

    fprintf(stdout, "Client connecté de %s\n",
                           inet_ntoa(sockaddr_client.sin_addr));

    /* Petit message de bienvenue */  
    len = strlen(ack);
    if (send(sock_client, ack, len, 0) != len)
      erreur("Echec d'envoi du message de bienvenue");

    /* attend quelque chose */
    while (recv(sock_client, buffer, TAMPON, MSG_DONTWAIT) < 0) {
      if ((errno == EWOULDBLOCK) || (errno == EAGAIN))
        travaille();
      else
        erreur("Echec de réception du message du client");
    }

    /* Adieu, serveur cruel ! */  
    len = strlen(eot);
    if (send(sock_client, eot, len, 0) != len)
      erreur("Echec d'envoi du message d'adieu");

    close(sock_client);
  }
}
