/* echoserver.c - Simple TCP server that accepts connections
 *            and echoes back everything it receives.
 *            It also writes what it receives to the screen.
 *            It listens on port PROTOPORT set to 5194
 *            or to the port you specify on command line.
 *            Compile as
 *              % gcc -o echoserver echoserver.c
 *            Run as
 *              % echoserver [port] &
 */

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


// If we are waiting reading from a pipe and
// the interlocutor dies abruptly (say because
// of ^C or kill -9), then we receive a SIGPIPE
// signal. Here we handle that.
void sig_pipe(int n) 
{
   fprintf(stderr, "Broken pipe signal\n");
}

extern int errno;

/*In an infinite loop, it reads characters from sockfd and writes them
  to the same socket (and to the screen).
*/
void
chr_echo(int sockfd)
{
  for ( ; ; ) {
    ssize_t n;
    char c;
  
    n = read(sockfd, &c, 1);
    if (n > 0) {
       write(sockfd, &c, 1);
       putchar(c);
    } else if ( n < 0) { 
       fprintf(stderr, "Negative return from Read\n");
       if (errno == EINTR) /* IO was interrupted by a signal */
           continue;
       return;
    } else  /* connection closed by other end */
      return;
  }
}


#define PROTOPORT 5194 /* default protocol port number*/
#define QLEN 6

/****************************************************************
 * Program: echoserver
 * Purpose: In a loop, accept a connection, echo back all you receive.
 * Syntax: echoserver [port]
 *            port:protocol port number to use
 * Note: The port argument is optional. If no protocol port is
 *       specified, the server uses the default given by PROTOPORT.
 ****************************************************************/
int
main(int argc, char *argv[])
{
  struct sockaddr_in sad;/* structure to hold server's address*/
  int    sd;             /* listening socket descriptor       */
  int    port;           /* protocol port number              */
  int    option_value;   /* needed for setsockopt             */
 
  /* Check command-line argument for protocol port and extract
   * port number if one is specified. Otherwise, use the default
   * port value given by constant PROTOPORT
   */
  if (argc > 1) {         /* If protocol port is specified */
    port = atoi(argv[1]); /* Convert to binary */
  } else {
    port = PROTOPORT;    /* use default port number */
  }
    
  /* clear and initialize server's sockaddr structure */
  memset((char *)&sad, 0, sizeof(sad)); 
  sad.sin_family = AF_INET;               /* set family to Internet */
  sad.sin_addr.s_addr = INADDR_ANY;       /* set the local IP address */
  sad.sin_port = htons((u_short)port);    /* Set port */
  
  /* Create socket */
  if ( (sd = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
    fprintf(stderr, "socket creation failed\n");
    exit(1);
  }

  /* Make listening socket's port reusable - must appear before bind */
  if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, (char *)&option_value, 
	sizeof(option_value)) < 0) {
	fprintf(stderr, "setsockopt failure\n");
	exit(1);
  }
  
  /* Bind a local address to the socket */
  if (bind(sd, (struct sockaddr *)&sad, sizeof(sad)) < 0) {
    fprintf(stderr, "bind failed\n");
    exit(1);
  }
  
  /* Specify size of request queue */
  if (listen(sd, QLEN) < 0) {
    fprintf(stderr, "listen failed\n");
    exit(1);
  }
 
  /* Establish handling of the SIGPIPE signal */
  if (signal(SIGPIPE, sig_pipe) == SIG_ERR) {
     fprintf(stderr, "Unable to set up signal handler\n");
  }
  
  /* Main server loop - accept and handle requests */
  for ( ; ; ) {
    struct sockaddr_in cad;/* structure to hold client's address*/
    int    alen;           /* length of address                 */
    int    sd2;            /* connected socket descriptor */

    alen = sizeof(cad);
    if ((sd2 = accept(sd, (struct sockaddr *)&cad, &alen)) < 0){
      if (errno == EINTR) continue;
      fprintf(stderr, "accept failed\n");
      exit(1);
    }
    chr_echo(sd2);
    close(sd2);
    printf("*******************************  END *******************************\n");
  }
}











