/* udpservb.c  
 * Server using datagram service with sockets. It is called with one 
 * parameter, the name of the port it will use. In a loop, it accepts
 * a message from a client, and it creates a thread to take care of the 
 * message. The server has a timeout when waiting.
 * A thread prints out a message it receives, sleeps a bit, then
 * answers the client with a message that consists of a prefix
 * contained in the message received and with the current time.
*/

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
#include <pthread.h>

#define MAXBUF 2 * 1024
#define MAXSERVWAIT 3
#define PREFIXSIZE 23

pthread_t *threadp;        /* Pointer to thread that will handle a request */
                           /* [There may be multiple active threads. Only */
                           /* last is kept track of.*/
struct request {           /* Descriptor of a request from client */
  char b[MAXBUF];          /* The message from client */
  struct sockaddr_in client_addr; /* the address of the client */ 
  };
void handle(struct request *b); /* Function executed by thread */

int sock;                /* file descriptor for UDP datagram socket */


main(argc, argv)
     int argc;
     char *argv[];
{
  int  create_request = 1;  /* it is 1 when we need to create a request */
  struct request *requestp;  /* Pointer to a request */              
  int length;              /* length of address */
  fd_set read_template;    /* used by select statement */
  struct timeval  pwait;
  int  nb;
  struct sockaddr_in name; /* the socket used for request */

  if (argc < 2) {
    printf("\nUSE: udpservb serverport\n"); exit(1);}

  /* 
   * create a socket 
   */
  if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
    perror("Server: socket"); exit(1);}
  
  /* 
   * bind socket 
   */
  bzero((char *)&name, sizeof(name));
  name.sin_family = AF_INET;
  name.sin_addr.s_addr = INADDR_ANY;
  name.sin_port = htons(atoi(argv[1]));
  if(bind (sock, (struct sockaddr *)&name,
	   sizeof(name)) < 0) {
    perror("Server: bind"); exit(1); }

  while(1) {
    if (create_request) {
      /*
       * Allocate request structure
       */
      if ((requestp = (struct request *)malloc(sizeof(struct request))) == 0) {
	perror("Malloc request"); exit(1);}

    }
    /*
     * Wait for request
     */
    pwait.tv_sec = MAXSERVWAIT;
    pwait.tv_usec = 0;
    FD_ZERO(&read_template);
    FD_SET(sock, &read_template);
    if ((nb = select(FD_SETSIZE, &read_template, NULL, NULL, &pwait)) <= 0) {
      printf("Server timed out\n");
      create_request = 0;  /* No need to create socket in next iteration */
    } else if (FD_ISSET(sock, &read_template)) {
      create_request = 1;
      length = sizeof(struct sockaddr_in);
      if(recvfrom(sock, requestp->b, MAXBUF, 0, 
		  &requestp->client_addr, &length) < 0) 
	perror("Couldn't read datagram"); 
      /*create thread */
      if ((threadp = (pthread_t *)malloc(sizeof(pthread_t))) == 0) {
	perror("Malloc pthread_t"); exit(1);}
      if (pthread_create(threadp, pthread_attr_default, (void *) handle, 
			 requestp) != 0) {
	perror("pthread_create"); exit(1);}
      printf("thread created\n");
      /* Code to reclaim the space allocated for thread objects. */
      if (pthread_detach(threadp) == 0) {
	printf("pthread detached\n");
      } else {
	perror("pthread_detach error"); exit(1);}
    } else {
      printf ("How did I get here?\n"); exit(1);}
  }
}
/*
 *  The code executed by a thread.
 */

void handle (struct request * b) {
  struct timespec interval; /* Sleeping time */
  long tm;                  /* Time*/

  printf("Thread: From Client %s\n", b->b);
  interval.tv_sec = 2;
  interval.tv_nsec = 0;
  pthread_delay_np(&interval);
  time(&tm);
  strcpy(b->b+PREFIXSIZE, ctime(&tm));
  printf("Thread: To Client %s\n", b->b);
  if(sendto(sock, b->b, MAXBUF, 0, &b->client_addr, 
	    sizeof(struct sockaddr_in)) < 0) {
    perror("Thread: Couldn't send datagram");}
  free(b);
}


