#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/time.h>
#include <errno.h>
#include <netinet/tcp.h>

void *malloc();

#define NODEBUG

#define NOVERIFY

#define SENDRECVBUFSIZ 65536*1 


#define HOSTNAMELEN 80
#define MYPORTNO    21
#define MAXBUF      1000000

#define PERROR(x) fprintf(stderr,"%s\n",(x))

#define VOLUME      65536*16*4

#define NUMTIMES 100

#define NOTONEWAY

int client;

int sockno;
int otherdude;

struct timeval lattime,thrtime;

int latencyhist[1000];  /* ms basis */

#define TODRES 1000

void timer_start(struct timeval *tv)
{
  gettimeofday(tv, NULL);
}

float timer_stop(struct timeval *tv)
{
  struct timeval tv2;
  int usecdiff, secdiff;
  
  gettimeofday(&tv2, NULL);
  
  usecdiff=tv2.tv_usec-tv->tv_usec;
  secdiff=tv2.tv_sec-tv->tv_sec;
  if (usecdiff<0) {
    --secdiff;
    usecdiff=1000000+usecdiff;
  }

  if (usecdiff<TODRES)
    usecdiff=TODRES;

  return ((float)secdiff + 1.0e-6*usecdiff);

}

struct sockaddr_in sa_cli, sa_srv;


int main(int argc, char *argv[])
{
  unsigned short i;
  int rc;
  char hostname[HOSTNAMELEN];
  struct hostent *he;
  int bufsiz, optlen;


  if (argc<4) {
    printf("usage: sock1 <_|hostname> <portnum> [blocksize]+\n");
    exit(1);
  }

  if (toupper(*(argv[1]))=='_')
    client=0;
  else
    client=1;

  if ((sockno=socket(AF_INET, SOCK_DGRAM, 0))<0) {
    PERROR("Can't Create Socket.");
    exit(2);
  }

  /* Check and set buffer sizes */
  
  if (getsockopt(sockno,SOL_SOCKET,SO_SNDBUF,&bufsiz,&optlen)<0) {
    perror("getsockopt failed");
    exit(1);
  }
  
  printf("# socket send buffer length is %d bytes\n",bufsiz);
  
  bufsiz=SENDRECVBUFSIZ;
  
  if (setsockopt(sockno,SOL_SOCKET,SO_SNDBUF,&bufsiz, sizeof(bufsiz))<0) {
    perror("setsockopt failed");
    exit(1);
  }
  
  if (getsockopt(sockno,SOL_SOCKET,SO_SNDBUF,&bufsiz,&optlen)<0) {
    perror("getsockopt failed");
    exit(1);
  }
  
  printf("# socket send buffer length is now %d bytes\n",bufsiz);
  
  if (getsockopt(sockno,SOL_SOCKET,SO_RCVBUF,&bufsiz,&optlen)<0) {
    perror("getsockopt failed");
    exit(1);
  }
  
  printf("# socket receive buffer length is %d bytes\n",bufsiz);
  
  bufsiz=SENDRECVBUFSIZ;
  
  if (setsockopt(sockno,SOL_SOCKET,SO_RCVBUF,&bufsiz, sizeof(bufsiz))<0) {
    perror("setsockopt failed");
    exit(1);
  }
  
  if (getsockopt(sockno,SOL_SOCKET,SO_RCVBUF,&bufsiz,&optlen)<0) {
    perror("getsockopt failed");
    exit(1);
  }
  
  printf("# socket receive buffer length is now %d bytes\n",bufsiz);
  
  
  if (!client) {


    gethostname(hostname,HOSTNAMELEN);
    
    if ((he=gethostbyname(hostname))==NULL) {
      PERROR("Hostname lookup failed.");
      exit(3);
    }

    bzero(&sa_srv,sizeof sa_srv);
    sa_srv.sin_port=atoi(argv[2]);
    bcopy(he->h_addr,&(sa_srv.sin_addr),he->h_length);
    sa_srv.sin_family=he->h_addrtype;

#ifdef DEBUG
    printf("family=%d, port=%d\n",sa_srv.sin_family, sa_srv.sin_port);
    printf("ip=%x\n",sa_srv.sin_addr.s_addr);
    for (i=0;i<4;i++)
      printf("%d.",(unsigned char)((char *)&(sa_srv.sin_addr.s_addr))[i]);

    printf("sizeof sa_srv=%d",sizeof sa_srv);
#endif

    if (rc=bind(sockno,&sa_srv,sizeof sa_srv)) {
      perror("Bind Failed");
      exit(4);
    }

    for (i=3;i<argc;i++) {
      comm_serve(atoi(argv[i]),VOLUME/atoi(argv[i]));
    }

    close(otherdude);


  } else {

    if ((he=gethostbyname(argv[1]))==NULL) {
      PERROR("Hostname lookup failed.");
      exit(3);
    }
    

    bzero(&sa_srv,sizeof(struct sockaddr));
    sa_srv.sin_port=atoi(argv[2]);
    bcopy(he->h_addr,&(sa_srv.sin_addr),he->h_length);
    sa_srv.sin_family=he->h_addrtype;

    gethostname(hostname,HOSTNAMELEN);
    
    if ((he=gethostbyname(hostname))==NULL) {
      PERROR("Hostname lookup failed.");
      exit(3);
    }

    bzero(&sa_cli,sizeof(struct sockaddr));
    sa_cli.sin_port=atoi(argv[2]);
    bcopy(he->h_addr,&(sa_cli.sin_addr),he->h_length);
    sa_cli.sin_family=he->h_addrtype;
    
    if (bind(sockno,&sa_cli,sizeof(struct sockaddr))<0) {
      perror("Bind");
      exit(4);
    }
    
    for (i=3;i<argc;i++) 
      comm_client(atoi(argv[i]),VOLUME/atoi(argv[i]));

  }

  close(sockno);

}
      
  

int comm_client(int buflen, int numtimes)
{
  unsigned char *buf;
  int i;
  float howlong,avglatency;
  int left;
  int len;
  


  for(i=0;i<1000;latencyhist[i++]=0)
    ;

  buf=(unsigned char *)malloc(buflen);

  for(i=0;i<buflen;i++)
    buf[i]=i%256;

  timer_start(&thrtime);

  for (i=0;i<numtimes;i++) {

    timer_start(&lattime);

#ifdef ONEWAY
#else
    buf[0]=i%256;
    len=sendto(sockno,buf,buflen,0,&sa_srv,sizeof sa_srv);
    if (len!=buflen) {
      printf("len=%d, but buflen=%d\n",len,buflen);
      perror("Write failed");
      exit(10);
    }
#endif

    left=buflen;
    while (left) {
      len=recvfrom(sockno,buf,buflen,0,NULL,NULL);
      if (len!=buflen) {
	perror("read failed");
	exit(11);
      }
      if (buf[0]!=i%256) {
	printf("lost packet! buf[0]=%d, but i=%d\n",buf[0],i);
      }
      left-=len;
    }

#ifdef VERIFY    
    for (len=0;len<buflen;len++)
      if (buf[len]!=len%256)
	printf("buf[rand]=%d, but rand%%256=%d\n", buf[len],len%256);
#endif    

    latencyhist[(int) (timer_stop(&lattime)*1000.0)]++;

  }

  howlong=timer_stop(&thrtime);

#ifdef ONEWAY
#else
  numtimes*=2;
#endif

  printf("%5d buflen %8d volume, %10f time - %10f KB/s\n", buflen,
	 numtimes*buflen,
	 howlong, (((float)(numtimes*buflen))/(howlong))/1000.0);

  printhist();

  free(buf);
}


int printhist()
{
  int i;
  int t=0;

  for (i=0;i<1000;i++) {
    if (latencyhist[i]!=0) {
      printf("%d %d\n",i,latencyhist[i]);
      t+=latencyhist[i]*i;
    }
  }
  printf("Sanity Check: Latency totals to %d ms\n",t);

}


int comm_serve(int buflen, int numtimes)
{
  unsigned char *buf;
  int i;
  int len;
  float howlong;
  int left;
  int siz;

  siz=sizeof sa_cli;


  for(i=0;i<1000;latencyhist[i++]=0)
    ;

  buf=(unsigned char *)malloc(buflen);

  for(i=0;i<buflen;i++)
    buf[i]=i%256;

  timer_start(&thrtime);

  for (i=0;i<numtimes;i++) {

    timer_start(&lattime);
#ifdef ONEWAY
#else
    left=buflen;
    while (left) {
      len=recvfrom(sockno,buf,buflen,0,&sa_cli,&siz);
      if (len!=buflen) {
	perror("read failed");
	exit(11);
      }
      if (buf[0]!=i%256) {
	printf("lost packet - buf[0]=%d, but i=%d\n",buf[0],i);
      }
      left-=len;
    }

#ifdef VERIFY    
    for (len=0;len<buflen;len++)
      if (buf[len]!=len%256)
	printf("buf[%d]=%d, but %d%%256=%d\n", len,buf[len],len,len%256);
#endif    
#endif

    buf[0]=i%256;
    len=sendto(sockno,buf,buflen,0,&sa_cli,sizeof sa_cli);
    if (len!=buflen) {
      printf("len=%d, but buflen=%d\n",len,buflen);
      perror("Write failed");
      exit(10);
    }

    latencyhist[(int) (timer_stop(&lattime)*1000.0)]++;

  }

  howlong=timer_stop(&thrtime);

#ifdef ONEWAY
#else
  numtimes*=2;
#endif

  printf("%5d buflen %8d volume, %10f time - %10f KB/s\n", buflen, 
	 numtimes*buflen,
	 howlong, (((float)(numtimes*buflen))/(howlong))/1000.0);

  free(buf);

  printhist();
}













