Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
udpbwtest.c 5.15 KiB
/* udpbwtest: a simple bandwidth test
 * 
 * To test all-to-all communication, we simply open up a
 * UDP socket and repeat the following:
 *
 * 1. Send a packet to a random host if possible
 * 2. Receive a packet if possible
 * 3. Periodically report our I/O bandwidth
 *
 */
 
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <strings.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <sys/time.h>
#include <poll.h>
#include <signal.h>

enum { Port=12345 };

/* Handy utility functions */

int bindAddr( int fd, long addr, u_short port ) {
   /* Easy interface to bind */
   struct sockaddr_in sa;
   bzero( &sa, sizeof( sa ) );
   sa.sin_family = AF_INET;
   sa.sin_addr.s_addr = htonl( addr );
   sa.sin_port = htons( port );
   return bind( fd, ( struct sockaddr * )&sa, sizeof( sa ) );   
}

int udpSocket( u_short port ) {
   /* Easy interface to making a UDP socket */
   int fd = socket( AF_INET, SOCK_DGRAM, 0 );
   if ( fd < 0 ) { return fd; }
   int err = bindAddr( fd, INADDR_ANY, port );
   if ( err < 0 ) { return err; }
   return fd;
}

ssize_t sendBytes( int sock, char *outbuf, size_t bufsize, 
   struct in_addr *addr, u_short port ) {
   /* Simpler sendto */
   struct sockaddr_in sa;
   int err;
   bzero( &sa, sizeof( sa ) );
   sa.sin_family = AF_INET;
   sa.sin_addr = *addr;
   sa.sin_port = htons( port );
   err = sendto( sock, outbuf, bufsize, 0, (const struct sockaddr *) &sa,
      sizeof( sa ) );
   if ( err < 0 ) { perror( "sendto:" ); }
   return err;
}

ssize_t recvBytes( int sock, char *inbuf, size_t bufsize, 
   struct in_addr *addr) {
   /* Simpler recvfrom */
   struct sockaddr_in sa;
   socklen_t saLen = sizeof( sa );
   ssize_t len;
   len = recvfrom( sock, inbuf, bufsize, 0, (struct sockaddr *) &sa, &saLen );
   if ( len >= 0 ) {
      assert( saLen == sizeof( sa ) );
      *addr = sa.sin_addr;
   }
   else { perror( "recvfrom:" ); }
   return len;
}

int readable( int fd ) {
   /* Poll a single file descriptor for reading */
   struct pollfd fds = { fd, POLLIN, POLLIN };
   int result = poll( &fds, 1, 0 );
   /* True if there is one readable descriptor */
   return ( result == 1 );
}

int writable( int fd ) {
   /* Poll a single file descriptor for writing */
   struct pollfd fds = { fd, POLLOUT, POLLOUT };
   int result = poll( &fds, 1, 0 );
   /* True if there is one writable descriptor */
   return ( result == 1 );
}

int poll1( int fd, int flags, int ms ) {
   /* Call poll on a single descriptor */
   struct pollfd fds[] = { { fd, flags, 0 } };
   return poll( fds, 1, ms );   
}

int waitReadable( int fd, int ms ) { return poll1( fd, POLLIN, ms ); }
int waitWritable( int fd, int ms ) { return poll1( fd, POLLIN, ms ); }
int waitReadableOrWritable( int fd, int ms ) {
    return poll1( fd, POLLIN | POLLOUT, ms  );
}

/* Timer support */

int alarm = 0; 
void handleAlarm( int sig) { alarm = 1; }

void startTimer( int seconds) {
   struct itimerval v;
   v.it_interval.tv_sec = seconds;
   v.it_interval.tv_usec = 0;
   v.it_value = v.it_interval;
   signal( SIGALRM, handleAlarm );
   setitimer( ITIMER_REAL, &v, 0 );
}

void startOneSecondTimer() { startTimer( 1 ); }

void stopTimer() { 
   struct itimerval v;
   bzero( &v, sizeof v );
   setitimer( ITIMER_REAL, &v, 0 );
}

/* Actual program */

void bwtest( int sock, struct in_addr *hosts, int hostCount ) {
   
   /* Test our bandwidth, by receiving whatever we get, and sending
    * randomly to a set of hosts */
   
   char outbuf[ 1024 ];
   char inbuf[ 1024 ];
   time_t seconds = time( 0 );
   uint64_t b, bytes;
   uint64_t inbytes = 0, outbytes = 0;
   int i;
   
   for ( i = 0; i < sizeof( outbuf ); i++ ) { outbuf[ i ] = i % 255; }
   
   while ( 1 ) {
      size_t addr_len;
      struct in_addr addr = hosts[ random() % hostCount ];
      /* Wait until we have something to do. */
      waitReadableOrWritable( sock, 0 );
      /* Receive some bytes */
      for ( b = 0; b < 10 && readable( sock ); b += 1 ) {
         bytes = recvBytes( sock, inbuf, sizeof( inbuf ), &addr );
         inbytes += bytes;
      }
      /* Send some bytes */
      for ( b = 0; b < 10 && writable(sock); b += 1 ) {
         bytes = sendBytes( sock, outbuf, sizeof( outbuf ), 
            &addr, Port );
         outbytes += bytes;
      }
      /* Periodically report bandwidth */
      if ( alarm ) {
         alarm = 0;
         seconds++;
         printf("%d s: in %.2f Mbps, out %.2f Mbps\n", 
            seconds, 8.0*inbytes/1e6, 8.0*outbytes/1e6 ); fflush( stdout );
         inbytes = outbytes = 0;
      }
   }
}

int main( int argc, char *argv[] ) {

   struct in_addr start, *hosts, addr;
   int count, sock, i;
   
   if ( argc < 2  ) {
      fprintf( stderr, "usage: %s host...\n", argv[ 0 ] );
      exit( 1 );
   }

   count = argc - 1;
   hosts = (struct in_addr *) malloc( count * sizeof( struct in_addr ) );
   if ( hosts == NULL ) { perror( "malloc:" ); exit( 1 ); }
   
   for ( i = 0; i < count; i++ ) 
      inet_aton( argv[ i + 1 ], &hosts[ i ] );
   
   sock = udpSocket( Port );
   if ( sock <  0 ) { perror( "udpSocket:" ); exit( 1 ); }
  
   startOneSecondTimer();

   bwtest( sock, hosts, count ); /* Never returns for now... */

   stopTimer();
   
}