//udp client
// can work on unix or windows machines
// this program tests the time it takes to transfer a fixed file size by varying the packet size

//#define  WIN                // WIN for Winsock and BSD for BSD sockets
#define  BSD
//note( BSD )
//	 compile on babbage% g++ -o client client_udp.cpp -lxnet
//	 compile on redhat % g++ -o client client_udp.cpp




//----- Include files ---------------------------------------------------------
#include < stdio.h>          // Needed for printf()
#include < string.h>         // Needed for memcpy() and strcpy()
#include < time.h>				 // timing function
#include < iostream.h>		 //cout,etc.
#include < fstream.h>			//files


#ifdef WIN
  #include < windows.h>      // Needed for all Winsock stuff
#else
  #include < sys/types.h>    // Needed for system defined identifiers.
  #include < netinet/in.h>   // Needed for internet address structure.
  #include < sys/socket.h>   // Needed for socket(), bind(), etc...
  #include < arpa/inet.h>    // Needed for inet_ntoa()
  #include < sys/time.h>		 // Needed for timeval{}
  #include < fcntl.h>
  #include < netdb.h>
#endif

//----- Defines ---------------------------------------------------------------
#define  PORT_NUM        1050   // Port number used
#define  MESS_SIZE      100000   // Message size, 0.1mbytes
#define	REPEAT				  5	
#define  MAX_PACKET_SIZE  10000


//set time out for lost datagram
void setTimeout( int my_s ) ;
//crunch out some numbers
void crunch( int my_s,	
				 const struct sockaddr *pdest_addr,
				 unsigned int destlen ) ;



//===== Main program ==========================================================
void main(void)
{
  unsigned int         my_s;            // My (client1) socket descriptor
  struct sockaddr_in   dest_addr;       // Server Internet address
  struct hostent		  *myhostent ;
  int server_len;


#ifdef WIN
  WORD wVersionRequested = MAKEWORD(1,1);       // Stuff for WSA functions
  WSADATA wsaData;                              // Stuff for WSA functions
  
  // This stuff initializes winsock
  WSAStartup(wVersionRequested, &wsaData);
#endif

  // Create a socket
  //   - AF_INET is Address Family Internet and SOCK_DGRAM is datagram
  my_s = socket(AF_INET, SOCK_DGRAM, 0);

	
/* get internet address */
  myhostent = gethostbyname("booboo") ;
  server_len = sizeof( dest_addr ) ;
#ifdef WIN
	memset( (char *)&dest_addr, 0, server_len ) ;
   memcpy( (char *)&dest_addr.sin_addr,
			  (char *)myhostent->h_addr,
			  myhostent->h_length ) ;
#else
	bzero((char *)&dest_addr, server_len);
	bcopy((char *)myhostent->h_addr,
			(char *)&dest_addr.sin_addr,
			myhostent->h_length);
#endif
	dest_addr.sin_family      = AF_INET;            // Address family to use
	dest_addr.sin_port        = htons(PORT_NUM);    // Port num to use
/* end getting internet address */  
  
  
/* make sure address if valid */
	printf("Sending to %s\n\n", inet_ntoa(dest_addr.sin_addr));


//set timed out to 5 seconds
	setTimeout( my_s ) ;


//crunch out the numbers	
	crunch( my_s,
			 (struct sockaddr *) &dest_addr,
			 sizeof( dest_addr ) ) ;





  // Close all open sockets (my socket)
#ifdef WIN
  closesocket(my_s);
  WSACleanup();
#else
  close(my_s);
#endif
}


void setTimeout( int my_s )
{
	struct timeval tv ;

	tv.tv_sec = 2000 ;//2 seconds
	tv.tv_usec = 0 ;

#ifdef WIN
	setsockopt( my_s,
					SOL_SOCKET,
					SO_RCVTIMEO,
					(char FAR *)&tv,
					sizeof( tv ) );
#else	//BSD
	setsockopt( my_s,
					SOL_SOCKET,
					SO_RCVTIMEO,
					(char *)&tv,
					sizeof( tv ) ) ;
#endif


}


void crunch( int my_s,
				 const struct sockaddr *pdest_addr,
				 unsigned int destlen )
{
	int i,				//loop variable
		 n,				//check lost datagrams
		 packet_size,	//another loop variable
		 changeable_packet,
		 buffer_left,
		 lost_packets = 0 ;//count of

	long double clock_diff,
				   total_clock1,
					total_clock2 ;

	char   *sendline = new char[ MAX_PACKET_SIZE ];   // Buffer for output data
	char   *recvline = new char[ MAX_PACKET_SIZE + 1 ];   // Buffer for output data


	clock_t clock1, clock2;
	fstream outfile ;


//open file
	outfile.open( "outfile_udp.dat", ios::out ) ;  
//file header
	outfile << "packet_size" << "  " << "time( sec )\n\n" ;

	memset( sendline, 'a', MAX_PACKET_SIZE ) ;
	memset( recvline, '\0', MAX_PACKET_SIZE + 1 ) ;


	for( packet_size=8990; packet_size<=MAX_PACKET_SIZE; packet_size++ )
	{	//actually, packetsize = 20 ip_header + 8 udp_header + packet_size
	
		
		clock_diff	= 0 ;		//reset clocks
		for( i=0; i< REPEAT; i++ )//statistically do 5 times
		{
			
			total_clock1 = 0;//reset total to zero
			total_clock2 = 0;
			changeable_packet = packet_size ;

			buffer_left = MESS_SIZE ;//will decrease buffer size in do loop
			do
			{

				//sendline[ changeable_packet ] = 0 ;
				clock1 = clock() ;//start timer
				
				sendto( my_s,	//in sendto, 28, ie 20 + 8, more bytes will be added
						  sendline,
						  changeable_packet,
						  0,
						  pdest_addr,
						  destlen ) ;
		

				n = recvfrom( my_s,	//receive acknowledgement
								  recvline,
								  changeable_packet + 1,
								  0,
								  NULL,
								  NULL ) ;
				if( n<0 )//timed out, resend packet
				{
					cout << "timeout" << endl;
					cout << "resending packet " << packet_size << endl;
				
					//for fun, count number of lost packets
					lost_packets++ ;

					//go back to beginning of do loop
					continue ;
				}
						
				clock2 = clock() ;//end timer

				total_clock1 += (double)clock1 ;
				total_clock2 += (double)clock2 ;
		
				buffer_left -= packet_size ;//decrease buffer size
				
				if( buffer_left < packet_size )//update packet
					changeable_packet = buffer_left ;

			}while( buffer_left > 0 ) ;

			
			//add up 5 times then divide by 5 to get average
			clock_diff = clock_diff +( total_clock2 - total_clock1 );
		}//end sampling five times
		

		//output to screen
		cout	<< endl 
				<< "packet_size = " << packet_size
				<< "     diff in clocks in secs = " << clock_diff / ( REPEAT * CLOCKS_PER_SEC )
				<< endl ;
		
		//output to file
		outfile	<< packet_size << "  " 
					<< clock_diff/( REPEAT * CLOCKS_PER_SEC ) << "\n" ;
		
	}//end for loop

	cout << "total lost packets... " << lost_packets << endl ;	

	outfile.close() ;
	delete [] sendline ;
	delete [] recvline ;
}