/* tcp-ping.c - code for example client that uses TCP                         */
/* From Computer Networks and Internets by Douglas F. Comer                   */
/* Modified to send to the tcp-ping server                                    */
/*                                                                            */
/* To compile me in Solaris, type:  gcc -o tcp-ping tcp-ping.c -lsocket -lnsl */
/* To compile me in Linux, type:    gcc -o tcp-ping tcp-ping.c                */

#define COUNT 1000
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>

#include <stdio.h>
#include <string.h>
#include <sys/time.h>


/*****************************************
 *  Control-C handler function          
 *  for finding average after control-c 
 *****************************************/

#include <signal.h>     
long int count=0;
long int totalusecs=0;
void PrintAverage() {
  printf("\nAverage round trip time = %ld\n\n", totalusecs / count );
}

void * controlChandler(int parm)
{
  printf("\nThe tcp-ping client was terminated by a signal!\n");
  PrintAverage();
  exit(0);
}

/**********************************************************
 * Function to subtract 2 timeval structs:  out = out - in.
 * Out is assumed to be >= in.
 **********************************************************/
tvsub( out, in )
register struct timeval *out, *in;
{
	if( (out->tv_usec -= in->tv_usec) < 0 )   {
		out->tv_sec--;
		out->tv_usec += 1000000;
	}
	out->tv_sec -= in->tv_sec;
}


#define PROTOPORT        5193        /* default protocol port number */

extern int               errno;
char   localhost[] = "localhost";    /* default host name            */
/**********************************************************************
 * Program:   tcp-ping
 *
 * Purpose:   Allocate a socket, connect to a server, and print all output
 *            Time a send/recv exchange in a tight loop.  Must end with
 *               a signal.  
 *
 * Syntax:    tcp-ping [ host [port] ]
 *
 *              host - name of a computer on which server is executing
 *              port - protocol port number server is using
 *
 * Note:      Both arguments are optional.  If no host name is specified,
 *            the client uses "localhost";  if no protocol port is
 *            specified, the client uses the default given by PROTOPORT.
 *
 **********************************************************************
 */
main(int argc, char *argv[])
{
   struct  hostent  *ptrh;   /* pointer to a host table entry       */
   struct  protoent *ptrp;   /* point to a protocol table entry     */
   struct  sockaddr_in sad;  /* structure to hold server's address  */
   int     sd;               /* socket descriptor                   */
   int     port;             /* protocol port number                */
   char    *host;            /* pointer to host name                */
   int     n;                /* number of characters read           */
   char    buf[40000];       /* buffer for data from the server     */

   struct timezone tz;       /* for timing */
   register struct timeval start;      /* for timing */
   register struct timeval stop;       /* for timing */
   int i;
   int SIZE;                 /* SIZE of the exchanged message       */

   signal( SIGINT, (void *)  controlChandler );  /* catch control-C  */

   memset((char *)&sad,0,sizeof(sad));  /* clear sockaddr structure */
   sad.sin_family = AF_INET;            /* set family to Internet   */

   /* Check command-line argument for protocol port and extract     */
   /* port number if on is specified.  Otherwise, use the default   */
   /* port value biven by constant PROTOPORT                        */

   if (argc > 2) port = atoi(argv[2]);
   else port = PROTOPORT;
   
   if (port > 0) sad.sin_port = htons((u_short)port);
   else 
     { fprintf( stderr,"bad port number %s\n", argv[2]);
          exit(1);
     }
   
   if (argc > 1 ) host = argv[1];
   else host = localhost;

   if (argc > 3) SIZE = atoi(argv[3]);
   else SIZE=1;
   if (SIZE < 1 || SIZE > 40000) {
	   printf("ERROR:  SIZE must be an integer between 1 and 40000.\n");
	   exit(1);
   }

   ptrh = gethostbyname(host);
   if( ((char *)ptrh) == NULL)
     { fprintf( stderr, "invalid host:  %s\n", host);
       exit(1);
     }
   
   memcpy(&sad.sin_addr, ptrh->h_addr, ptrh->h_length);

   if ( ((int)(ptrp = getprotobyname("tcp"))) == 0)
     { fprintf( stderr, "cannot map \"tcp\" to protocol number\n");
       exit(1);
     }

   sd = socket(PF_INET, SOCK_STREAM, ptrp->p_proto);
   if (sd < 0)
     { fprintf( stderr, "socket creation failed\n");
       exit(1);
     }

   if (connect(sd, (struct sockaddr *)&sad, sizeof(sad)) < 0)
     { fprintf( stderr, "connect failed\n");
       exit(1);
     }

  
   for (i=0; i<COUNT; i++) {
     gettimeofday( &start, &tz );
     send(sd,buf,SIZE,0);  /* have to send at least 1 byte for server logic */
     n = 0;
     while (n < SIZE) 
     	n += recv(sd, buf+n, sizeof(buf)-n, 0);
     gettimeofday( &stop, &tz );
     tvsub( &stop, &start );
     printf("Round trip time = %d usecs\n", 
	    stop.tv_sec*1000000+stop.tv_usec);
     count++;
     totalusecs += stop.tv_sec*1000000+stop.tv_usec;
   }

   PrintAverage();
   close(sd);
   exit(0);
}



