C++程序  |  6027行  |  190.44 KB

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#ifdef WANT_XTI
#ifndef lint
char	nettest_xti_id[]="\
@(#)nettest_xti.c (c) Copyright 1995-2007 Hewlett-Packard Co. Version 2.4.3";
#else
#define DIRTY
#define WANT_HISTOGRAM
#define WANT_INTERVALS
#endif /* lint */
/****************************************************************/
/*								*/
/*	nettest_xti.c						*/
/*								*/
/*      the XTI args parsing routine...                         */
/*                                                              */
/*      scan_xti_args()                                         */
/*                                                              */
/*	the actual test routines...				*/
/*								*/
/*	send_xti_tcp_stream()	perform a tcp stream test	*/
/*	recv_xti_tcp_stream()					*/
/*	send_xti_tcp_rr()	perform a tcp request/response	*/
/*	recv_xti_tcp_rr()					*/
/*      send_xti_tcp_conn_rr()  an RR test including connect    */
/*      recv_xti_tcp_conn_rr()                                  */
/*	send_xti_udp_stream()	perform a udp stream test	*/
/*	recv_xti_udp_stream()					*/
/*	send_xti_udp_rr()	perform a udp request/response	*/
/*	recv_xti_udp_rr()					*/
/*								*/
/****************************************************************/

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif 

#include <sys/types.h>
#include <fcntl.h>
#ifndef WIN32
#include <sys/ipc.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <errno.h>
#include <signal.h>
#else /* WIN32 */
#include <process.h>
#include <winsock2.h>
#include <windows.h>
#endif /* WIN32 */
#include <stdio.h>
#include <time.h>
#include <malloc.h>
 /* xti.h should be included *after* in.h because there are name */
 /* conflicts!( Silly standards people... raj 2/95 fortuenately, the */
 /* confilcts are on IP_TOP and IP_TTL, whcih netperf does not yet use */
#include <xti.h>

#include "netlib.h"
#include "netsh.h"
#include "nettest_xti.h"

#ifdef WANT_HISTOGRAM
#ifdef __sgi
#include <sys/time.h>
#endif /* __sgi */
#include "hist.h"
#endif /* WANT_HISTOGRAM */



 /* these variables are specific to the XTI sockets tests. declare */
 /* them static to make them global only to this file. */

static int	
  rss_size,		/* remote socket send buffer size	*/
  rsr_size,		/* remote socket recv buffer size	*/
  lss_size,		/* local  socket send buffer size 	*/
  lsr_size,		/* local  socket recv buffer size 	*/
  req_size = 1,		/* request size                   	*/
  rsp_size = 1,		/* response size			*/
  send_size,		/* how big are individual sends		*/
  recv_size;		/* how big are individual receives	*/

static  int   confidence_iteration;
static  char  local_cpu_method;
static  char  remote_cpu_method;

 /* different options for the xti				*/

static int
  loc_nodelay,		/* don't/do use NODELAY	locally		*/
  rem_nodelay,		/* don't/do use NODELAY remotely	*/
  loc_sndavoid,		/* avoid send copies locally		*/
  loc_rcvavoid,		/* avoid recv copies locally		*/
  rem_sndavoid,		/* avoid send copies remotely		*/
  rem_rcvavoid;		/* avoid recv_copies remotely		*/

static struct t_info info_struct;

#ifdef WANT_HISTOGRAM
#ifdef HAVE_GETHRTIME
hrtime_t time_one;
hrtime_t time_two;
#else
static struct timeval time_one;
static struct timeval time_two;
#endif /* HAVE_GETHRTIME */
static HIST time_hist;
#endif /* WANT_HISTOGRAM */

static char loc_xti_device[32] = "/dev/tcp";
static char rem_xti_device[32] = "/dev/tcp";

static int  xti_flags = 0;

char xti_usage[] = "\n\
Usage: netperf [global options] -- [test options] \n\
\n\
TCP/UDP XTI API Test Options:\n\
    -D [L][,R]        Set XTI_TCP_NODELAY locally and/or remotely (XTI_TCP_*)\n\
    -h                Display this text\n\
    -m bytes          Set the send size (XTI_TCP_STREAM, XTI_UDP_STREAM)\n\
    -M bytes          Set the recv size (XTI_TCP_STREAM, XTI_UDP_STREAM)\n\
    -r bytes          Set request size (XTI_TCP_RR, XTI_UDP_RR)\n\
    -R bytes          Set response size (XTI_TCP_RR, XTI_UDP_RR)\n\
    -s send[,recv]    Set local socket send/recv buffer sizes\n\
    -S send[,recv]    Set remote socket send/recv buffer sizes\n\
    -X dev[,dev]      Set the local/remote XTI device file name\n\
\n\
For those options taking two parms, at least one must be specified;\n\
specifying one value without a comma will set both parms to that\n\
value, specifying a value with a leading comma will set just the second\n\
parm, a value with a trailing comma will set just the first. To set\n\
each parm to unique values, specify both and separate them with a\n\
comma.\n"; 
     

 /* This routine is intended to retrieve interesting aspects of tcp */
 /* for the data connection. at first, it attempts to retrieve the */
 /* maximum segment size. later, it might be modified to retrieve */
 /* other information, but it must be information that can be */
 /* retrieved quickly as it is called during the timing of the test. */
 /* for that reason, a second routine may be created that can be */
 /* called outside of the timing loop */
void
get_xti_info(socket, info_struct)
     int socket;
     struct t_info *info_struct;
{

}


 /* This routine will create a data (listen) socket with the apropriate */
 /* options set and return it to the caller. this replaces all the */
 /* duplicate code in each of the test routines and should help make */
 /* things a little easier to understand. since this routine can be */
 /* called by either the netperf or netserver programs, all output */
 /* should be directed towards "where." family is generally AF_INET, */
 /* and type will be either SOCK_STREAM or SOCK_DGRAM */
SOCKET
create_xti_endpoint(char *name)
{

  SOCKET temp_socket;

  struct t_optmgmt *opt_req;  /* we request an option */
  struct t_optmgmt *opt_ret;  /* it tells us what we got */

  /* we use this to pass-in BSD-like socket options through t_optmgmt. */
  /* it ends up being about as clear as mud. raj 2/95 */
  struct sock_option {
    struct t_opthdr myopthdr;
    long value;
  } *sock_option;

  if (debug) {
    fprintf(where,"create_xti_endpoint: attempting to open %s\n",
	    name);
    fflush(where);
  }

  /*set up the data socket                        */
  temp_socket = t_open(name,O_RDWR,NULL);
  
  if (temp_socket == INVALID_SOCKET){
    fprintf(where,
	    "netperf: create_xti_endpoint: t_open %s: errno %d t_errno %d\n",
	    name,
	    errno,
	    t_errno);
    fflush(where);
    exit(1);
  }
  
  if (debug) {
    fprintf(where,"create_xti_endpoint: socket %d obtained...\n",temp_socket);
    fflush(where);
  }
  
  /* allocate what we need for option mgmt */
  if ((opt_req = (struct t_optmgmt *)t_alloc(temp_socket,T_OPTMGMT,T_ALL)) == 
      NULL) {
    fprintf(where,
	    "netperf: create_xti_endpoint: t_alloc: opt_req errno %d\n",
	    errno);
    fflush(where);
    exit(1);
  }

  if (debug) {
    fprintf(where,
	    "create_xti_endpoint: opt_req->opt.buf %x maxlen %d len %d\n",
	    opt_req->opt.buf,
	    opt_req->opt.maxlen,
	    opt_req->opt.len);
    
    fflush(where);
  }

  if ((opt_ret = (struct t_optmgmt *) t_alloc(temp_socket,T_OPTMGMT,T_ALL)) ==
      NULL) {
    fprintf(where,
	    "netperf: create_xti_endpoint: t_alloc: opt_ret errno %d\n",
	    errno);
    fflush(where);
    exit(1);
  }

  if (debug) {
    fprintf(where,
	    "create_xti_endpoint: opt_ret->opt.buf %x maxlen %d len %d\n",
	    opt_ret->opt.buf,
	    opt_ret->opt.maxlen,
	    opt_ret->opt.len);
    fflush(where);
  }

  /* Modify the local socket size. The reason we alter the send buffer */
  /* size here rather than when the connection is made is to take care */
  /* of decreases in buffer size. Decreasing the window size after */
  /* connection establishment is a TCP no-no. Also, by setting the */
  /* buffer (window) size before the connection is established, we can */
  /* control the TCP MSS (segment size). The MSS is never more that 1/2 */
  /* the minimum receive buffer size at each half of the connection. */
  /* This is why we are altering the receive buffer size on the sending */
  /* size of a unidirectional transfer. If the user has not requested */
  /* that the socket buffers be altered, we will try to find-out what */
  /* their values are. If we cannot touch the socket buffer in any way, */
  /* we will set the values to -1 to indicate that.  */

#ifdef XTI_SNDBUF
  if (lss_size > 0) {
    /* we want to "negotiate" the option */
    opt_req->flags = T_NEGOTIATE;
  }
  else {
    /* we want to accept the default, and know what it is. I assume */
    /* that when nothing has been changed, that T_CURRENT will return */
    /* the same as T_DEFAULT raj 3/95 */
    opt_req->flags = T_CURRENT;
  }

  /* the first part is for the netbuf that holds the option we want */
  /* to negotiate or check */
  /* the buffer of the netbuf points at the socket options structure */
  
  /* we assume that the t_alloc call allocated a buffer that started */
  /* on a proper alignment */
  sock_option = (struct sock_option *)opt_req->opt.buf;
  
  /* and next, set the fields in the sock_option structure */
  sock_option->myopthdr.level = XTI_GENERIC;
  sock_option->myopthdr.name  = XTI_SNDBUF;
  sock_option->myopthdr.len   = sizeof(struct t_opthdr) + sizeof(long);
  sock_option->value        = lss_size;
  
  opt_req->opt.len          = sizeof(struct t_opthdr) + sizeof(long);
  
  /* now, set-up the stuff to return the value in the end */
  /* we assume that the t_alloc call allocated a buffer that started */
  /* on a proper alignment */
  sock_option = (struct sock_option *)opt_ret->opt.buf;
  
  /* finally, call t_optmgmt. clear as mud. */
  if (t_optmgmt(temp_socket,opt_req,opt_ret) == -1) {
    fprintf(where,
	    "netperf: create_xti_endpoint: XTI_SNDBUF option: t_errno %d\n",
	    t_errno);
    fflush(where);
    exit(1);
  }

  if (sock_option->myopthdr.status == T_SUCCESS) {
    lss_size = sock_option->value;
  }
  else {
    fprintf(where,"create_xti_endpoint: XTI_SNDBUF option status 0x%.4x",
	    sock_option->myopthdr.status);
    fprintf(where," value %d\n",
	    sock_option->value);
    fflush(where);
    lss_size = -1;
  }

  if (lsr_size > 0) {
    /* we want to "negotiate" the option */
    opt_req->flags = T_NEGOTIATE;
  }
  else {
    /* we want to accept the default, and know what it is. I assume */
    /* that when nothing has been changed, that T_CURRENT will return */
    /* the same as T_DEFAULT raj 3/95 */
    opt_req->flags = T_CURRENT;
  }
  
  /* the first part is for the netbuf that holds the option we want */
  /* to negotiate or check */
  /* the buffer of the netbuf points at the socket options structure */
  
  /* we assume that the t_alloc call allocated a buffer that started */
  /* on a proper alignment */
  sock_option = (struct sock_option *)opt_req->opt.buf;
  
  /* and next, set the fields in the sock_option structure */
  sock_option->myopthdr.level = XTI_GENERIC;
  sock_option->myopthdr.name  = XTI_RCVBUF;
  sock_option->myopthdr.len   = sizeof(struct t_opthdr) + sizeof(long);
  sock_option->value        = lsr_size;
  
  opt_req->opt.len          = sizeof(struct t_opthdr) + sizeof(long);
  
  /* now, set-up the stuff to return the value in the end */
  /* we assume that the t_alloc call allocated a buffer that started */
  /* on a proper alignment */
  sock_option = (struct sock_option *)opt_ret->opt.buf;
  
  /* finally, call t_optmgmt. clear as mud. */
  if (t_optmgmt(temp_socket,opt_req,opt_ret) == -1) {
    fprintf(where,
	    "netperf: create_xti_endpoint: XTI_RCVBUF option: t_errno %d\n",
	    t_errno);
    fflush(where);
    exit(1);
  }
  lsr_size = sock_option->value;

  /* this needs code */

  if (debug) {
    fprintf(where,"netperf: create_xti_endpoint: socket sizes determined...\n");
    fprintf(where,"                       send: %d recv: %d\n",
	    lss_size,lsr_size);
    fflush(where);
  }
  
#else /* XTI_SNDBUF */
  
  lss_size = -1;
  lsr_size = -1;
  
#endif /* XTI_SNDBUF */

  /* now, we may wish to enable the copy avoidance features on the */
  /* local system. of course, this may not be possible... */
  
  if (loc_rcvavoid) {
    fprintf(where,
	    "netperf: create_xti_endpoint: Could not enable receive copy avoidance");
    fflush(where);
    loc_rcvavoid = 0;
  }

  if (loc_sndavoid) {
    fprintf(where,
	    "netperf: create_xti_endpoint: Could not enable send copy avoidance");
    fflush(where);
    loc_sndavoid = 0;
  }
  
  /* Now, we will see about setting the TCP_NODELAY flag on the local */
  /* socket. We will only do this for those systems that actually */
  /* support the option. If it fails, note the fact, but keep going. */
  /* If the user tries to enable TCP_NODELAY on a UDP socket, this */
  /* will cause an error to be displayed */
  
#ifdef TCP_NODELAY
  if ((strcmp(test_name,"XTI_TCP_STREAM") == 0) ||
      (strcmp(test_name,"XTI_TCP_RR") == 0) ||
      (strcmp(test_name,"XTI_TCP_CRR") == 0)) {
    if (loc_nodelay) {
      /* we want to "negotiate" the option */
      opt_req->flags = T_NEGOTIATE;
    }
    else {
      /* we want to accept the default, and know what it is. I assume */
      /* that when nothing has been changed, that T_CURRENT will return */
      /* the same as T_DEFAULT raj 3/95 */
      opt_req->flags = T_CURRENT;
    }
    
    /* the first part is for the netbuf that holds the option we want */
    /* to negotiate or check the buffer of the netbuf points at the */
    /* socket options structure */ 
    
    /* we assume that the t_alloc call allocated a buffer that started */
    /* on a proper alignment */
    sock_option = (struct sock_option *)opt_req->opt.buf;
    
    /* and next, set the fields in the sock_option structure */
    sock_option->myopthdr.level = INET_TCP;
    sock_option->myopthdr.name  = TCP_NODELAY;
    sock_option->myopthdr.len   = sizeof(struct t_opthdr) + sizeof(long);
    sock_option->value          = T_YES;
    
    opt_req->opt.len          = sizeof(struct t_opthdr) + sizeof(long);
    
    /* now, set-up the stuff to return the value in the end */
    /* we assume that the t_alloc call allocated a buffer that started */
    /* on a proper alignment */
    sock_option = (struct sock_option *)opt_ret->opt.buf;
    
    /* finally, call t_optmgmt. clear as mud. */
    if (t_optmgmt(temp_socket,opt_req,opt_ret) == -1) {
      fprintf(where,
	      "create_xti_endpoint: TCP_NODELAY option: errno %d t_errno %d\n",
	      errno,
	      t_errno);
      fflush(where);
      exit(1);
    }
    loc_nodelay = sock_option->value;
  }
#else /* TCP_NODELAY */
  
  loc_nodelay = 0;
  
#endif /* TCP_NODELAY */

  return(temp_socket);

}


/* This routine implements the TCP unidirectional data transfer test */
/* (a.k.a. stream) for the xti interface. It receives its */
/* parameters via global variables from the shell and writes its */
/* output to the standard output. */


void 
send_xti_tcp_stream(char remote_host[])
{
  
  char *tput_title = "\
Recv   Send    Send                          \n\
Socket Socket  Message  Elapsed              \n\
Size   Size    Size     Time     Throughput  \n\
bytes  bytes   bytes    secs.    %s/sec  \n\n";
  
  char *tput_fmt_0 =
    "%7.2f\n";
  
  char *tput_fmt_1 =
    "%6d %6d %6d    %-6.2f   %7.2f   \n";
  
  char *cpu_title = "\
Recv   Send    Send                          Utilization       Service Demand\n\
Socket Socket  Message  Elapsed              Send     Recv     Send    Recv\n\
Size   Size    Size     Time     Throughput  local    remote   local   remote\n\
bytes  bytes   bytes    secs.    %-8.8s/s  %% %c      %% %c      us/KB   us/KB\n\n";
  
  char *cpu_fmt_0 =
    "%6.3f %c\n";

  char *cpu_fmt_1 =
    "%6d %6d %6d    %-6.2f     %7.2f   %-6.2f   %-6.2f   %-6.3f  %-6.3f\n";
  
  char *ksink_fmt = "\n\
Alignment      Offset         %-8.8s %-8.8s    Sends   %-8.8s Recvs\n\
Local  Remote  Local  Remote  Xfered   Per                 Per\n\
Send   Recv    Send   Recv             Send (avg)          Recv (avg)\n\
%5d   %5d  %5d   %5d %6.4g  %6.2f    %6d   %6.2f %6d\n";

  char *ksink_fmt2 = "\n\
Maximum\n\
Segment\n\
Size (bytes)\n\
%6d\n";
  
  
  float			elapsed_time;
  
#ifdef WANT_INTERVALS
  int interval_count;
  sigset_t signal_set;
#endif
  
  /* what we want is to have a buffer space that is at least one */
  /* send-size greater than our send window. this will insure that we */
  /* are never trying to re-use a buffer that may still be in the hands */
  /* of the transport. This buffer will be malloc'd after we have found */
  /* the size of the local senc socket buffer. We will want to deal */
  /* with alignment and offset concerns as well. */
  
  int	*message_int_ptr;

  struct ring_elt *send_ring;
  
  int len;
  unsigned int nummessages;
  SOCKET send_socket;
  int bytes_remaining;
  int tcp_mss = -1;  /* possibly uninitialized on printf far below */

  /* with links like fddi, one can send > 32 bits worth of bytes */
  /* during a test... ;-) at some point, this should probably become a */
  /* 64bit integral type, but those are not entirely common yet */

  double	bytes_sent;
  
  float	local_cpu_utilization;
  float	local_service_demand;
  float	remote_cpu_utilization;
  float	remote_service_demand;

  double	thruput;
  
  /* some addressing information */
  struct	hostent	        *hp;
  struct	sockaddr_in	server;
  unsigned      int             addr;

  struct t_call server_call;
  
  struct	xti_tcp_stream_request_struct	*xti_tcp_stream_request;
  struct	xti_tcp_stream_response_struct	*xti_tcp_stream_response;
  struct	xti_tcp_stream_results_struct	*xti_tcp_stream_result;
  
  xti_tcp_stream_request  = 
    (struct xti_tcp_stream_request_struct *)netperf_request.content.test_specific_data;
  xti_tcp_stream_response =
    (struct xti_tcp_stream_response_struct *)netperf_response.content.test_specific_data;
  xti_tcp_stream_result   = 
    (struct xti_tcp_stream_results_struct *)netperf_response.content.test_specific_data;
  
#ifdef WANT_HISTOGRAM
  time_hist = HIST_new();
#endif /* WANT_HISTOGRAM */
  /* since we are now disconnected from the code that established the */
  /* control socket, and since we want to be able to use different */
  /* protocols and such, we are passed the name of the remote host and */
  /* must turn that into the test specific addressing information. */
  
  bzero((char *)&server,
	sizeof(server));
  
  /* it would seem that while HP-UX will allow an IP address (as a */
  /* string) in a call to gethostbyname, other, less enlightened */
  /* systems do not. fix from awjacks@ca.sandia.gov raj 10/95 */  
  /* order changed to check for IP address first. raj 7/96 */

  if ((addr = inet_addr(remote_host)) == SOCKET_ERROR) {
    /* it was not an IP address, try it as a name */
    if ((hp = gethostbyname(remote_host)) == NULL) {
      /* we have no idea what it is */
      fprintf(where,
	      "establish_control: could not resolve the destination %s\n",
	      remote_host);
      fflush(where);
      exit(1);
    }
    else {
      /* it was a valid remote_host */
      bcopy(hp->h_addr,
	    (char *)&server.sin_addr,
	    hp->h_length);
      server.sin_family = hp->h_addrtype;
    }
  }
  else {
    /* it was a valid IP address */
    server.sin_addr.s_addr = addr;
    server.sin_family = AF_INET;
  }    
  
  if ( print_headers ) {
    /* we want to have some additional, interesting information in */
    /* the headers. we know some of it here, but not all, so we will */
    /* only print the test title here and will print the results */
    /* titles after the test is finished */
    fprintf(where,"XTI TCP STREAM TEST");
    fprintf(where," to %s", remote_host);
    if (iteration_max > 1) {
      fprintf(where,
	      " : +/-%3.1f%% @ %2d%% conf.",
	      interval/0.02,
	      confidence_level);
      }
    if (loc_nodelay || rem_nodelay) {
      fprintf(where," : nodelay");
    }
    if (loc_sndavoid || 
	loc_rcvavoid ||
	rem_sndavoid ||
	rem_rcvavoid) {
      fprintf(where," : copy avoidance");
    }
#ifdef WANT_HISTOGRAM
    fprintf(where," : histogram");
#endif /* WANT_HISTOGRAM */
#ifdef WANT_INTERVALS
    fprintf(where," : interval");
#endif /* WANT_INTERVALS */
#ifdef DIRTY 
    fprintf(where," : dirty data");
#endif /* DIRTY */
    fprintf(where,"\n");
  }

  send_ring = NULL;
  confidence_iteration = 1;
  init_stat();

  /* we have a great-big while loop which controls the number of times */
  /* we run a particular test. this is for the calculation of a */
  /* confidence interval (I really should have stayed awake during */
  /* probstats :). If the user did not request confidence measurement */
  /* (no confidence is the default) then we will only go though the */
  /* loop once. the confidence stuff originates from the folks at IBM */

  while (((confidence < 0) && (confidence_iteration < iteration_max)) ||
	 (confidence_iteration <= iteration_min)) {

    /* initialize a few counters. we have to remember that we might be */
    /* going through the loop more than once. */
    
    nummessages    =	0;
    bytes_sent     =	0.0;
    times_up       = 	0;
    
    /*set up the data socket                        */
    send_socket = create_xti_endpoint(loc_xti_device);
    
    if (send_socket == INVALID_SOCKET) {
      perror("netperf: send_xti_tcp_stream: tcp stream data socket");
      exit(1);
    }
    
    if (debug) {
      fprintf(where,"send_xti_tcp_stream: send_socket obtained...\n");
    }
    
    /* it would seem that with XTI, there is no implicit bind on a */
    /* connect, so we have to make a call to t_bind. this is not */
    /* terribly convenient, but I suppose that "standard is better */
    /* than better" :) raj 2/95 */

    if (t_bind(send_socket, NULL, NULL) == SOCKET_ERROR) {
      t_error("send_xti_tcp_stream: t_bind");
      exit(1);
    }
      
    /* at this point, we have either retrieved the socket buffer sizes, */
    /* or have tried to set them, so now, we may want to set the send */
    /* size based on that (because the user either did not use a -m */
    /* option, or used one with an argument of 0). If the socket buffer */
    /* size is not available, we will set the send size to 4KB - no */
    /* particular reason, just arbitrary... */
    if (send_size == 0) {
      if (lss_size > 0) {
	send_size = lss_size;
      }
      else {
	send_size = 4096;
      }
    }
    
    /* set-up the data buffer ring with the requested alignment and offset. */
    /* note also that we have allocated a quantity */
    /* of memory that is at least one send-size greater than our socket */
    /* buffer size. We want to be sure that there are at least two */
    /* buffers allocated - this can be a bit of a problem when the */
    /* send_size is bigger than the socket size, so we must check... the */
    /* user may have wanted to explicitly set the "width" of our send */
    /* buffers, we should respect that wish... */

    if (send_width == 0) {
      send_width = (lss_size/send_size) + 1;
      if (send_width == 1) send_width++;
    }
    
    if (send_ring == NULL) {
      /* only allocate the send ring once. this is a networking test, */
      /* not a memory allocation test. this way, we do not need a */
      /* deallocate_buffer_ring() routine, and I don't feel like */
      /* writing one anyway :) raj 11/94 */
      send_ring = allocate_buffer_ring(send_width,
				       send_size,
				       local_send_align,
				       local_send_offset);
    }

    /* If the user has requested cpu utilization measurements, we must */
    /* calibrate the cpu(s). We will perform this task within the tests */
    /* themselves. If the user has specified the cpu rate, then */
    /* calibrate_local_cpu will return rather quickly as it will have */
    /* nothing to do. If local_cpu_rate is zero, then we will go through */
    /* all the "normal" calibration stuff and return the rate back. */
    
    if (local_cpu_usage) {
      local_cpu_rate = calibrate_local_cpu(local_cpu_rate);
    }
    
    /* Tell the remote end to do a listen. The server alters the socket */
    /* paramters on the other side at this point, hence the reason for */
    /* all the values being passed in the setup message. If the user did */
    /* not specify any of the parameters, they will be passed as 0, which */
    /* will indicate to the remote that no changes beyond the system's */
    /* default should be used. Alignment is the exception, it will */
    /* default to 1, which will be no alignment alterations. */
    
    netperf_request.content.request_type          = DO_XTI_TCP_STREAM;
    xti_tcp_stream_request->send_buf_size  = rss_size;
    xti_tcp_stream_request->recv_buf_size  = rsr_size;
    xti_tcp_stream_request->receive_size   = recv_size;
    xti_tcp_stream_request->no_delay       = rem_nodelay;
    xti_tcp_stream_request->recv_alignment = remote_recv_align;
    xti_tcp_stream_request->recv_offset    = remote_recv_offset;
    xti_tcp_stream_request->measure_cpu    = remote_cpu_usage;
    xti_tcp_stream_request->cpu_rate       = remote_cpu_rate;
    if (test_time) {
      xti_tcp_stream_request->test_length  = test_time;
    }
    else {
      xti_tcp_stream_request->test_length  = test_bytes;
    }
    xti_tcp_stream_request->so_rcvavoid    = rem_rcvavoid;
    xti_tcp_stream_request->so_sndavoid    = rem_sndavoid;

    strcpy(xti_tcp_stream_request->xti_device, rem_xti_device);
  
#ifdef __alpha
  
    /* ok - even on a DEC box, strings are strings. I didn't really want */
    /* to ntohl the words of a string. since I don't want to teach the */
    /* send_ and recv_ _request and _response routines about the types, */
    /* I will put "anti-ntohl" calls here. I imagine that the "pure" */
    /* solution would be to use XDR, but I am still leary of being able */
    /* to find XDR libs on all platforms I want running netperf. raj */
    {
      int *charword;
      int *initword;
      int *lastword;
      
      initword = (int *) xti_tcp_stream_request->xti_device;
      lastword = initword + ((strlen(rem_xti_device) + 3) / 4);
      
      for (charword = initword;
	   charword < lastword;
	   charword++) {
	
	*charword = ntohl(*charword);
      }
    }
#endif /* __alpha */
    
#ifdef DIRTY
    xti_tcp_stream_request->dirty_count         = rem_dirty_count;
    xti_tcp_stream_request->clean_count         = rem_clean_count;
#endif /* DIRTY */
    
    
    if (debug > 1) {
      fprintf(where,
              "netperf: send_xti_tcp_stream: requesting TCP stream test\n");
    }
    
    send_request();
    
    /* The response from the remote will contain all of the relevant    */
    /* socket parameters for this test type. We will put them back into */
    /* the variables here so they can be displayed if desired.  The     */
    /* remote will have calibrated CPU if necessary, and will have done */
    /* all the needed set-up we will have calibrated the cpu locally    */
    /* before sending the request, and will grab the counter value right*/
    /* after the connect returns. The remote will grab the counter right*/
    /* after the accept call. This saves the hassle of extra messages   */
    /* being sent for the TCP tests.                                    */
    
    recv_response();
    
    if (!netperf_response.content.serv_errno) {
      if (debug)
        fprintf(where,"remote listen done.\n");
      rsr_size         = xti_tcp_stream_response->recv_buf_size;
      rss_size         = xti_tcp_stream_response->send_buf_size;
      rem_nodelay      = xti_tcp_stream_response->no_delay;
      remote_cpu_usage = xti_tcp_stream_response->measure_cpu;
      remote_cpu_rate  = xti_tcp_stream_response->cpu_rate;

      /* we have to make sure that the server port number is in */
      /* network order */
      server.sin_port   = (short)xti_tcp_stream_response->data_port_number;
      server.sin_port   = htons(server.sin_port); 
      rem_rcvavoid      = xti_tcp_stream_response->so_rcvavoid;
      rem_sndavoid      = xti_tcp_stream_response->so_sndavoid;
    }
    else {
      Set_errno(netperf_response.content.serv_errno);
      perror("netperf: remote error");
      
      exit(1);
    }
    
    /*Connect up to the remote port on the data socket  */
    memset (&server_call, 0, sizeof(server_call));
    server_call.addr.maxlen = sizeof(struct sockaddr_in);
    server_call.addr.len    = sizeof(struct sockaddr_in);
    server_call.addr.buf    = (char *)&server;

    if (t_connect(send_socket, 
		  &server_call,
		  NULL) == INVALID_SOCKET){
      t_error("netperf: send_xti_tcp_stream: data socket connect failed");
      printf(" port: %d\n",ntohs(server.sin_port));
      exit(1);
    }
    
    /* Data Socket set-up is finished. If there were problems, either */
    /* the connect would have failed, or the previous response would */
    /* have indicated a problem. I failed to see the value of the */
    /* extra  message after the accept on the remote. If it failed, */
    /* we'll see it here. If it didn't, we might as well start pumping */
    /* data. */ 
    
    /* Set-up the test end conditions. For a stream test, they can be */
    /* either time or byte-count based. */
    
    if (test_time) {
      /* The user wanted to end the test after a period of time. */
      times_up = 0;
      bytes_remaining = 0;
      /* in previous revisions, we had the same code repeated throught */
      /* all the test suites. this was unnecessary, and meant more */
      /* work for me when I wanted to switch to POSIX signals, so I */
      /* have abstracted this out into a routine in netlib.c. if you */
      /* are experiencing signal problems, you might want to look */
      /* there. raj 11/94 */
      start_timer(test_time);
    }
    else {
      /* The tester wanted to send a number of bytes. */
      bytes_remaining = test_bytes;
      times_up = 1;
    }
    
    /* The cpu_start routine will grab the current time and possibly */
    /* value of the idle counter for later use in measuring cpu */
    /* utilization and/or service demand and thruput. */
    
    cpu_start(local_cpu_usage);
    
#ifdef WANT_INTERVALS
    if ((interval_burst) || (demo_mode)) {
      /* zero means that we never pause, so we never should need the */
      /* interval timer, unless we are in demo_mode */
      start_itimer(interval_wate);
    }
    interval_count = interval_burst;
    /* get the signal set for the call to sigsuspend */
    if (sigprocmask(SIG_BLOCK, (sigset_t *)NULL, &signal_set) != 0) {
      fprintf(where,
              "send_xti_tcp_stream: unable to get sigmask errno %d\n",
              errno);
      fflush(where);
      exit(1);
    }
#endif /* WANT_INTERVALS */

    /* before we start, initialize a few variables */

    /* We use an "OR" to control test execution. When the test is */
    /* controlled by time, the byte count check will always return false. */
    /* When the test is controlled by byte count, the time test will */
    /* always return false. When the test is finished, the whole */
    /* expression will go false and we will stop sending data. */
    
    while ((!times_up) || (bytes_remaining > 0)) {
      
#ifdef DIRTY
      /* we want to dirty some number of consecutive integers in the buffer */
      /* we are about to send. we may also want to bring some number of */
      /* them cleanly into the cache. The clean ones will follow any dirty */
      /* ones into the cache. at some point, we might want to replace */
      /* the rand() call with something from a table to reduce our call */
      /* overhead during the test, but it is not a high priority item. */
      access_buffer(send_ring->buffer_ptr,
		    send_size,
		    loc_dirty_count,
		    loc_clean_count);
#endif /* DIRTY */
      
#ifdef WANT_HISTOGRAM
      /* timestamp just before we go into send and then again just after */
      /* we come out raj 8/94 */
      HIST_timestamp(&time_one);
#endif /* WANT_HISTOGRAM */
      
      if((len=t_snd(send_socket,
		    send_ring->buffer_ptr,
		    send_size,
		    0)) != send_size) {
        if ((len >=0) || (errno == EINTR)) {
          /* the test was interrupted, must be the end of test */
          break;
        }
        fprintf(where,
		"send_xti_tcp_stream: t_snd: errno %d t_errno %d t_look 0x%.4x\n",
		errno,
		t_errno,
		t_look(send_socket));
	fflush(where);
        exit(1);
      }

#ifdef WANT_HISTOGRAM
      /* timestamp the exit from the send call and update the histogram */
      HIST_timestamp(&time_two);
      HIST_add(time_hist,delta_micro(&time_one,&time_two));
#endif /* WANT_HISTOGRAM */      

#ifdef WANT_INTERVALS      
      if (demo_mode) {
        units_this_tick += send_size;
      }
      /* in this case, the interval count is the count-down couter */
      /* to decide to sleep for a little bit */
      if ((interval_burst) && (--interval_count == 0)) {
        /* call sigsuspend and wait for the interval timer to get us */
        /* out */
        if (debug) {
          fprintf(where,"about to suspend\n");
          fflush(where);
        }
        if (sigsuspend(&signal_set) == EFAULT) {
          fprintf(where,
                  "send_xti_tcp_stream: fault with signal set!\n");
          fflush(where);
          exit(1);
        }
        interval_count = interval_burst;
      }
#endif /* WANT_INTERVALS */
      
      /* now we want to move our pointer to the next position in the */
      /* data buffer...we may also want to wrap back to the "beginning" */
      /* of the bufferspace, so we will mod the number of messages sent */
      /* by the send width, and use that to calculate the offset to add */
      /* to the base pointer. */
      nummessages++;          
      send_ring = send_ring->next;
      if (bytes_remaining) {
        bytes_remaining -= send_size;
      }
    }

    /* The test is over. Flush the buffers to the remote end. We do a */
    /* graceful release to insure that all data has been taken by the */
    /* remote. */ 

    /* but first, if the verbosity is greater than 1, find-out what */
    /* the TCP maximum segment_size was (if possible) */
    if (verbosity > 1) {
      tcp_mss = -1;
      get_xti_info(send_socket,info_struct);
    }
    
    if (t_sndrel(send_socket) == -1) {
      t_error("netperf: cannot shutdown tcp stream socket");
      exit(1);
    }
    
    /* hang a t_rcvrel() off the socket to block until the remote has */
    /* brought all the data up into the application. it will do a */
    /* t_sedrel to cause a FIN to be sent our way. We will assume that */
    /* any exit from the t_rcvrel() call is good... raj 2/95 */
    
    if (debug > 1) {
      fprintf(where,"about to hang a receive for graceful release.\n");
      fflush(where);
    }

    t_rcvrel(send_socket);
    
    /* this call will always give us the elapsed time for the test, and */
    /* will also store-away the necessaries for cpu utilization */
    
    cpu_stop(local_cpu_usage,&elapsed_time);    /* was cpu being */
                                                /* measured and how */
                                                /* long did we really */
                                                /* run? */
    
    /* Get the statistics from the remote end. The remote will have */
    /* calculated service demand and all those interesting things. If it */
    /* wasn't supposed to care, it will return obvious values. */
    
    recv_response();
    if (!netperf_response.content.serv_errno) {
      if (debug)
        fprintf(where,"remote results obtained\n");
    }
    else {
      Set_errno(netperf_response.content.serv_errno);
      perror("netperf: remote error");
      
      exit(1);
    }
    
    /* We now calculate what our thruput was for the test. In the future, */
    /* we may want to include a calculation of the thruput measured by */
    /* the remote, but it should be the case that for a TCP stream test, */
    /* that the two numbers should be *very* close... We calculate */
    /* bytes_sent regardless of the way the test length was controlled. */
    /* If it was time, we needed to, and if it was by bytes, the user may */
    /* have specified a number of bytes that wasn't a multiple of the */
    /* send_size, so we really didn't send what he asked for ;-) */
    
    bytes_sent  = xti_tcp_stream_result->bytes_received;

    thruput     = calc_thruput(bytes_sent);
    
    if (local_cpu_usage || remote_cpu_usage) {
      /* We must now do a little math for service demand and cpu */
      /* utilization for the system(s) */
      /* Of course, some of the information might be bogus because */
      /* there was no idle counter in the kernel(s). We need to make */
      /* a note of this for the user's benefit...*/
      if (local_cpu_usage) {
        
        local_cpu_utilization   = calc_cpu_util(0.0);
        local_service_demand    = calc_service_demand(bytes_sent,
                                                      0.0,
                                                      0.0,
						      0);
      }
      else {
        local_cpu_utilization   = -1.0;
        local_service_demand    = -1.0;
      }
      
      if (remote_cpu_usage) {
        
        remote_cpu_utilization  = xti_tcp_stream_result->cpu_util;
        remote_service_demand   = calc_service_demand(bytes_sent,
                                                      0.0,
                                                      remote_cpu_utilization,
						      xti_tcp_stream_result->num_cpus);
      }
      else {
        remote_cpu_utilization = -1.0;
        remote_service_demand  = -1.0;
      }
    }    
    else {
      /* we were not measuring cpu, for the confidence stuff, we */
      /* should make it -1.0 */
      local_cpu_utilization  = -1.0;
      local_service_demand   = -1.0;
      remote_cpu_utilization = -1.0;
      remote_service_demand  = -1.0;
    }

    /* at this point, we want to calculate the confidence information. */
    /* if debugging is on, calculate_confidence will print-out the */
    /* parameters we pass it */
    
    calculate_confidence(confidence_iteration,
                         elapsed_time,
                         thruput,
                         local_cpu_utilization,
                         remote_cpu_utilization,
                         local_service_demand,
                         remote_service_demand);
    
    
    confidence_iteration++;
  }

  /* at this point, we have finished making all the runs that we */
  /* will be making. so, we should extract what the calcuated values */
  /* are for all the confidence stuff. we could make the values */
  /* global, but that seemed a little messy, and it did not seem worth */
  /* all the mucking with header files. so, we create a routine much */
  /* like calcualte_confidence, which just returns the mean values. */
  /* raj 11/94 */

  retrieve_confident_values(&elapsed_time,
                            &thruput,
                            &local_cpu_utilization,
                            &remote_cpu_utilization,
                            &local_service_demand,
                            &remote_service_demand);

  /* We are now ready to print all the information. If the user */
  /* has specified zero-level verbosity, we will just print the */
  /* local service demand, or the remote service demand. If the */
  /* user has requested verbosity level 1, he will get the basic */
  /* "streamperf" numbers. If the user has specified a verbosity */
  /* of greater than 1, we will display a veritable plethora of */
  /* background information from outside of this block as it it */
  /* not cpu_measurement specific...  */

  if (confidence < 0) {
    /* we did not hit confidence, but were we asked to look for it? */
    if (iteration_max > 1) {
      display_confidence();
    }
  }

  if (local_cpu_usage || remote_cpu_usage) {
    local_cpu_method = format_cpu_method(cpu_method);
    remote_cpu_method = format_cpu_method(xti_tcp_stream_result->cpu_method);
    
    switch (verbosity) {
    case 0:
      if (local_cpu_usage) {
        fprintf(where,
                cpu_fmt_0,
                local_service_demand,
		local_cpu_method);
      }
      else {
	fprintf(where,
		cpu_fmt_0,
		remote_service_demand,
		remote_cpu_method);
      }
      break;
    case 1:
    case 2:
      if (print_headers) {
	fprintf(where,
		cpu_title,
		format_units(),
		local_cpu_method,
		remote_cpu_method);
      }
    
      fprintf(where,
	      cpu_fmt_1,		/* the format string */
	      rsr_size,		        /* remote recvbuf size */
	      lss_size,		        /* local sendbuf size */
	      send_size,		/* how large were the sends */
	      elapsed_time,		/* how long was the test */
	      thruput, 		        /* what was the xfer rate */
	      local_cpu_utilization,	/* local cpu */
	      remote_cpu_utilization,	/* remote cpu */
	      local_service_demand,	/* local service demand */
	      remote_service_demand);	/* remote service demand */
      break;
    }
  }
  else {
    /* The tester did not wish to measure service demand. */
    
    switch (verbosity) {
    case 0:
      fprintf(where,
	      tput_fmt_0,
	      thruput);
      break;
    case 1:
    case 2:
      if (print_headers) {
	fprintf(where,tput_title,format_units());
      }
      fprintf(where,
	      tput_fmt_1,		/* the format string */
	      rsr_size, 		/* remote recvbuf size */
	      lss_size, 		/* local sendbuf size */
	      send_size,		/* how large were the sends */
	      elapsed_time, 		/* how long did it take */
	      thruput);/* how fast did it go */
      break;
    }
  }
  
  /* it would be a good thing to include information about some of the */
  /* other parameters that may have been set for this test, but at the */
  /* moment, I do not wish to figure-out all the  formatting, so I will */
  /* just put this comment here to help remind me that it is something */
  /* that should be done at a later time. */
  
  if (verbosity > 1) {
    /* The user wanted to know it all, so we will give it to him. */
    /* This information will include as much as we can find about */
    /* TCP statistics, the alignments of the sends and receives */
    /* and all that sort of rot... */
   
    /* this stuff needs to be worked-out in the presence of confidence */
    /* intervals and multiple iterations of the test... raj 11/94 */
 
    fprintf(where,
	    ksink_fmt,
	    "Bytes",
	    "Bytes",
	    "Bytes",
	    local_send_align,
	    remote_recv_align,
	    local_send_offset,
	    remote_recv_offset,
	    bytes_sent,
	    bytes_sent / (double)nummessages,
	    nummessages,
	    bytes_sent / (double)xti_tcp_stream_result->recv_calls,
	    xti_tcp_stream_result->recv_calls);
    fprintf(where,
	    ksink_fmt2,
	    tcp_mss);
    fflush(where);
#ifdef WANT_HISTOGRAM
    fprintf(where,"\n\nHistogram of time spent in send() call.\n");
    fflush(where);
    HIST_report(time_hist);
#endif /* WANT_HISTOGRAM */
  }
  
}


/* This is the server-side routine for the tcp stream test. It is */
/* implemented as one routine. I could break things-out somewhat, but */
/* didn't feel it was necessary. */

void 
recv_xti_tcp_stream()
{
  
  struct sockaddr_in myaddr_in, peeraddr_in;
  struct t_bind      bind_req, bind_resp;
  struct t_call      call_req;

  SOCKET       s_listen,s_data;
  int           addrlen;
  int	        len;
  unsigned int	receive_calls;
  float	        elapsed_time;
  double        bytes_received;
  
  struct ring_elt *recv_ring;

  int   *message_int_ptr;
  int   i;
  
  struct xti_tcp_stream_request_struct	*xti_tcp_stream_request;
  struct xti_tcp_stream_response_struct	*xti_tcp_stream_response;
  struct xti_tcp_stream_results_struct	*xti_tcp_stream_results;
  
  xti_tcp_stream_request	= 
    (struct xti_tcp_stream_request_struct *)netperf_request.content.test_specific_data;
  xti_tcp_stream_response	= 
    (struct xti_tcp_stream_response_struct *)netperf_response.content.test_specific_data;
  xti_tcp_stream_results	= 
    (struct xti_tcp_stream_results_struct *)netperf_response.content.test_specific_data;
  
  if (debug) {
    fprintf(where,"netserver: recv_xti_tcp_stream: entered...\n");
    fflush(where);
  }
  
  /* We want to set-up the listen socket with all the desired */
  /* parameters and then let the initiator know that all is ready. If */
  /* socket size defaults are to be used, then the initiator will have */
  /* sent us 0's. If the socket sizes cannot be changed, then we will */
  /* send-back what they are. If that information cannot be determined, */
  /* then we send-back -1's for the sizes. If things go wrong for any */
  /* reason, we will drop back ten yards and punt. */
  
  /* If anything goes wrong, we want the remote to know about it. It */
  /* would be best if the error that the remote reports to the user is */
  /* the actual error we encountered, rather than some bogus unexpected */
  /* response type message. */
  
  if (debug) {
    fprintf(where,"recv_xti_tcp_stream: setting the response type...\n");
    fflush(where);
  }
  
  netperf_response.content.response_type = XTI_TCP_STREAM_RESPONSE;
  
  if (debug) {
    fprintf(where,"recv_xti_tcp_stream: the response type is set...\n");
    fflush(where);
  }
  
  /* We now alter the message_ptr variable to be at the desired */
  /* alignment with the desired offset. */
  
  if (debug) {
    fprintf(where,"recv_xti_tcp_stream: requested alignment of %d\n",
	    xti_tcp_stream_request->recv_alignment);
    fflush(where);
  }

  /* Let's clear-out our sockaddr for the sake of cleanlines. Then we */
  /* can put in OUR values !-) At some point, we may want to nail this */
  /* socket to a particular network-level address, but for now, */
  /* INADDR_ANY should be just fine. */
  
  bzero((char *)&myaddr_in,
	sizeof(myaddr_in));
  myaddr_in.sin_family      = AF_INET;
  myaddr_in.sin_addr.s_addr = INADDR_ANY;
  myaddr_in.sin_port        = 0;
  
  /* Grab a socket to listen on, and then listen on it. */
  
  if (debug) {
    fprintf(where,"recv_xti_tcp_stream: grabbing a socket...\n");
    fflush(where);
  }
  
  /* create_xti_endpoint expects to find some things in the global */
  /* variables, so set the globals based on the values in the request. */
  /* once the socket has been created, we will set the response values */
  /* based on the updated value of those globals. raj 7/94 */
  lss_size = xti_tcp_stream_request->send_buf_size;
  lsr_size = xti_tcp_stream_request->recv_buf_size;
  loc_nodelay = xti_tcp_stream_request->no_delay;
  loc_rcvavoid = xti_tcp_stream_request->so_rcvavoid;
  loc_sndavoid = xti_tcp_stream_request->so_sndavoid;

#ifdef __alpha
  
  /* ok - even on a DEC box, strings are strings. I din't really want */
  /* to ntohl the words of a string. since I don't want to teach the */
  /* send_ and recv_ _request and _response routines about the types, */
  /* I will put "anti-ntohl" calls here. I imagine that the "pure" */
  /* solution would be to use XDR, but I am still leary of being able */
  /* to find XDR libs on all platforms I want running netperf. raj */
  {
    int *charword;
    int *initword;
    int *lastword;
    
    initword = (int *) xti_tcp_stream_request->xti_device;
    lastword = initword + ((xti_tcp_stream_request->dev_name_len + 3) / 4);
    
    for (charword = initword;
	 charword < lastword;
	 charword++) {
      
      *charword = htonl(*charword);
    }
  }
  
#endif /* __alpha */

  s_listen = create_xti_endpoint(xti_tcp_stream_request->xti_device);
  
  if (s_listen == INVALID_SOCKET) {
    netperf_response.content.serv_errno = errno;
    send_response();
    exit(1);
  }
  
  /* Let's get an address assigned to this socket so we can tell the */
  /* initiator how to reach the data socket. There may be a desire to */
  /* nail this socket to a specific IP address in a multi-homed, */
  /* multi-connection situation, but for now, we'll ignore the issue */
  /* and concentrate on single connection testing. */
  
  bind_req.addr.maxlen = sizeof(struct sockaddr_in);
  bind_req.addr.len    = sizeof(struct sockaddr_in);
  bind_req.addr.buf    = (char *)&myaddr_in;
  bind_req.qlen        = 1;

  bind_resp.addr.maxlen = sizeof(struct sockaddr_in);
  bind_resp.addr.len    = sizeof(struct sockaddr_in);
  bind_resp.addr.buf    = (char *)&myaddr_in;
  bind_resp.qlen        = 1;

  if (t_bind(s_listen,
	     &bind_req,
	     &bind_resp) == SOCKET_ERROR) {
    netperf_response.content.serv_errno = t_errno;
    close(s_listen);
    send_response();
    
    exit(1);
  }

  if (debug) {
    fprintf(where,
	    "recv_xti_tcp_stream: t_bind complete port %d\n",
	    ntohs(myaddr_in.sin_port));
    fflush(where);
  }
  
  /* what sort of sizes did we end-up with? */
  if (xti_tcp_stream_request->receive_size == 0) {
    if (lsr_size > 0) {
      recv_size = lsr_size;
    }
    else {
      recv_size = 4096;
    }
  }
  else {
    recv_size = xti_tcp_stream_request->receive_size;
  }
  
  /* we want to set-up our recv_ring in a manner analagous to what we */
  /* do on the sending side. this is more for the sake of symmetry */
  /* than for the needs of say copy avoidance, but it might also be */
  /* more realistic - this way one could conceivably go with a */
  /* double-buffering scheme when taking the data an putting it into */
  /* the filesystem or something like that. raj 7/94 */

  if (recv_width == 0) {
    recv_width = (lsr_size/recv_size) + 1;
    if (recv_width == 1) recv_width++;
  }

  recv_ring = allocate_buffer_ring(recv_width,
				   recv_size,
				   xti_tcp_stream_request->recv_alignment,
				   xti_tcp_stream_request->recv_offset);

  if (debug) {
    fprintf(where,"recv_xti_tcp_stream: recv alignment and offset set...\n");
    fflush(where);
  }
  
  /* Now myaddr_in contains the port and the internet address this is */
  /* returned to the sender also implicitly telling the sender that the */
  /* socket buffer sizing has been done. */
  
  xti_tcp_stream_response->data_port_number = 
    (int) ntohs(myaddr_in.sin_port);
  netperf_response.content.serv_errno   = 0;
  
  /* But wait, there's more. If the initiator wanted cpu measurements, */
  /* then we must call the calibrate routine, which will return the max */
  /* rate back to the initiator. If the CPU was not to be measured, or */
  /* something went wrong with the calibration, we will return a -1 to */
  /* the initiator. */
  
  xti_tcp_stream_response->cpu_rate = 0.0; 	/* assume no cpu */
  if (xti_tcp_stream_request->measure_cpu) {
    xti_tcp_stream_response->measure_cpu = 1;
    xti_tcp_stream_response->cpu_rate = 
      calibrate_local_cpu(xti_tcp_stream_request->cpu_rate);
  }
  else {
    xti_tcp_stream_response->measure_cpu = 0;
  }
  
  /* before we send the response back to the initiator, pull some of */
  /* the socket parms from the globals */
  xti_tcp_stream_response->send_buf_size = lss_size;
  xti_tcp_stream_response->recv_buf_size = lsr_size;
  xti_tcp_stream_response->no_delay = loc_nodelay;
  xti_tcp_stream_response->so_rcvavoid = loc_rcvavoid;
  xti_tcp_stream_response->so_sndavoid = loc_sndavoid;
  xti_tcp_stream_response->receive_size = recv_size;

  send_response();
  
  /* Now, let's set-up the socket to listen for connections. for xti, */
  /* the t_listen call is blocking by default - this is different */
  /* semantics from BSD - probably has to do with being able to reject */
  /* a call before an accept */
  call_req.addr.maxlen = sizeof(struct sockaddr_in);
  call_req.addr.len    = sizeof(struct sockaddr_in);
  call_req.addr.buf    = (char *)&peeraddr_in;
  call_req.opt.maxlen  = 0;
  call_req.opt.len     = 0;
  call_req.opt.buf     = NULL;
  call_req.udata.maxlen= 0;
  call_req.udata.len   = 0;
  call_req.udata.buf   = 0;

  if (t_listen(s_listen, &call_req) == -1) {
    fprintf(where,
	    "recv_xti_tcp_stream: t_listen: errno %d t_errno %d\n",
	    errno,
	    t_errno);
    fflush(where);
    netperf_response.content.serv_errno = t_errno;
    close(s_listen);
    send_response();
    exit(1);
  }
  
  if (debug) {
    fprintf(where,
	    "recv_xti_tcp_stream: t_listen complete t_look 0x%.4x\n",
	    t_look(s_listen));
    fflush(where);
  }
  
  /* now just rubber stamp the thing. we want to use the same fd? so */
  /* we will just equate s_data with s_listen. this seems a little */
  /* hokey to me, but then I'm a BSD biggot still. raj 2/95 */
  s_data = s_listen;
  if (t_accept(s_listen,
	       s_data,
	       &call_req) == -1) {
    fprintf(where,
	    "recv_xti_tcp_stream: t_accept: errno %d t_errno %d\n",
	    errno,
	    t_errno);
    fflush(where);
    close(s_listen);
    exit(1);
  }
  
  if (debug) {
    fprintf(where,
	    "recv_xti_tcp_stream: t_accept complete t_look 0x%.4x\n",
	    t_look(s_data));
    fprintf(where,
	    "                     remote is %s port %d\n",
	    inet_ntoa(*(struct in_addr *)&peeraddr_in.sin_addr),
	    ntohs(peeraddr_in.sin_port));
    fflush(where);
  }

  /* Now it's time to start receiving data on the connection. We will */
  /* first grab the apropriate counters and then start grabbing. */
  
  cpu_start(xti_tcp_stream_request->measure_cpu);
  
  /* The loop will exit when the sender does a t_sndrel, which will */
  /* return T_LOOK error from the t_recv */
  
#ifdef DIRTY
    /* we want to dirty some number of consecutive integers in the buffer */
    /* we are about to recv. we may also want to bring some number of */
    /* them cleanly into the cache. The clean ones will follow any dirty */
    /* ones into the cache. */

  access_buffer(recv_ring->buffer_ptr,
		recv_size,
		xti_tcp_stream_request->dirty_count,
		xti_tcp_stream_request->clean_count);

#endif /* DIRTY */

  bytes_received = 0;
  receive_calls  = 0;

  while ((len = t_rcv(s_data,
		      recv_ring->buffer_ptr,
		      recv_size,
		      &xti_flags)) != -1) {
    bytes_received += len;
    receive_calls++;
    
    /* more to the next buffer in the recv_ring */
    recv_ring = recv_ring->next;
    
#ifdef DIRTY

  access_buffer(recv_ring->buffer_ptr,
		recv_size,
		xti_tcp_stream_request->dirty_count,
		xti_tcp_stream_request->clean_count);

#endif /* DIRTY */
  }
  
  if (t_look(s_data) == T_ORDREL) {
    /* this is a normal exit path */
    if (debug) {
      fprintf(where,
	      "recv_xti_tcp_stream: t_rcv T_ORDREL indicated\n");
      fflush(where);
    }
  }
  else {
    /* something went wrong */
    fprintf(where,
	    "recv_xti_tcp_stream: t_rcv: errno %d t_errno %d len %d",
	    errno,
	    t_errno,
	    len);
    fprintf(where,
	    " t_look 0x%.4x",
	    t_look(s_data));
    fflush(where);
    netperf_response.content.serv_errno = t_errno;
    send_response();
    exit(1);
  }
  
  /* receive the release and let the initiator know that we have */
  /* received all the data. raj 3/95 */

  if (t_rcvrel(s_data) == -1) {
    netperf_response.content.serv_errno = errno;
    send_response();
    exit(1);
  }    

  if (debug) {
    fprintf(where,
	    "recv_xti_tcp_stream: t_rcvrel complete\n");
    fflush(where);
  }

  if (t_sndrel(s_data) == -1) {
    netperf_response.content.serv_errno = errno;
    send_response();
    exit(1);
  }
  
  if (debug) {
    fprintf(where,
	    "recv_xti_tcp_stream: t_sndrel complete\n");
    fflush(where);
  }

  cpu_stop(xti_tcp_stream_request->measure_cpu,&elapsed_time);
  
  /* send the results to the sender			*/
  
  if (debug) {
    fprintf(where,
	    "recv_xti_tcp_stream: got %g bytes\n",
	    bytes_received);
    fprintf(where,
	    "recv_xti_tcp_stream: got %d recvs\n",
	    receive_calls);
    fflush(where);
  }
  
  xti_tcp_stream_results->bytes_received	= bytes_received;
  xti_tcp_stream_results->elapsed_time	= elapsed_time;
  xti_tcp_stream_results->recv_calls	= receive_calls;
  
  if (xti_tcp_stream_request->measure_cpu) {
    xti_tcp_stream_results->cpu_util	= calc_cpu_util(0.0);
  };
  
  if (debug) {
    fprintf(where,
	    "recv_xti_tcp_stream: test complete, sending results.\n");
    fprintf(where,
	    "                 bytes_received %g receive_calls %d\n",
	    bytes_received,
	    receive_calls);
    fprintf(where,
	    "                 len %d\n",
	    len);
    fflush(where);
  }
  
  xti_tcp_stream_results->cpu_method = cpu_method;
  send_response();

  /* we are now done with the socket */
  t_close(s_data);

}


 /* this routine implements the sending (netperf) side of the XTI_TCP_RR */
 /* test. */

void 
send_xti_tcp_rr(char remote_host[])
{
  
  char *tput_title = "\
Local /Remote\n\
Socket Size   Request  Resp.   Elapsed  Trans.\n\
Send   Recv   Size     Size    Time     Rate         \n\
bytes  Bytes  bytes    bytes   secs.    per sec   \n\n";
  
  char *tput_fmt_0 =
    "%7.2f\n";
  
  char *tput_fmt_1_line_1 = "\
%-6d %-6d %-6d   %-6d  %-6.2f   %7.2f   \n";
  char *tput_fmt_1_line_2 = "\
%-6d %-6d\n";
  
  char *cpu_title = "\
Local /Remote\n\
Socket Size   Request Resp.  Elapsed Trans.   CPU    CPU    S.dem   S.dem\n\
Send   Recv   Size    Size   Time    Rate     local  remote local   remote\n\
bytes  bytes  bytes   bytes  secs.   per sec  %% %c    %% %c    us/Tr   us/Tr\n\n";
  
  char *cpu_fmt_0 =
    "%6.3f %c\n";
  
  char *cpu_fmt_1_line_1 = "\
%-6d %-6d %-6d  %-6d %-6.2f  %-6.2f  %-6.2f %-6.2f %-6.3f  %-6.3f\n";
  
  char *cpu_fmt_1_line_2 = "\
%-6d %-6d\n";
  
  char *ksink_fmt = "\
Alignment      Offset\n\
Local  Remote  Local  Remote\n\
Send   Recv    Send   Recv\n\
%5d  %5d   %5d  %5d\n";
  
  
  int			timed_out = 0;
  float			elapsed_time;
  
  int	len;
  char	*temp_message_ptr;
  int	nummessages;
  SOCKET send_socket;
  int	trans_remaining;
  double	bytes_xferd;

  struct ring_elt *send_ring;
  struct ring_elt *recv_ring;
  
  int	rsp_bytes_left;
  int	rsp_bytes_recvd;
  
  float	local_cpu_utilization;
  float	local_service_demand;
  float	remote_cpu_utilization;
  float	remote_service_demand;
  double	thruput;
  
  struct	hostent	        *hp;
  struct	sockaddr_in	server;
  unsigned      int             addr;
  
  struct t_call server_call;

  struct	xti_tcp_rr_request_struct	*xti_tcp_rr_request;
  struct	xti_tcp_rr_response_struct	*xti_tcp_rr_response;
  struct	xti_tcp_rr_results_struct	*xti_tcp_rr_result;
  
#ifdef WANT_INTERVALS
  int	interval_count;
  sigset_t signal_set;
#endif /* WANT_INTERVALS */

  xti_tcp_rr_request = 
    (struct xti_tcp_rr_request_struct *)netperf_request.content.test_specific_data;
  xti_tcp_rr_response=
    (struct xti_tcp_rr_response_struct *)netperf_response.content.test_specific_data;
  xti_tcp_rr_result	=
    (struct xti_tcp_rr_results_struct *)netperf_response.content.test_specific_data;
  
#ifdef WANT_HISTOGRAM
  time_hist = HIST_new();
#endif /* WANT_HISTOGRAM */

  /* since we are now disconnected from the code that established the */
  /* control socket, and since we want to be able to use different */
  /* protocols and such, we are passed the name of the remote host and */
  /* must turn that into the test specific addressing information. */

  bzero((char *)&server,
	sizeof(server));
  
  /* it would seem that while HP-UX will allow an IP address (as a */
  /* string) in a call to gethostbyname, other, less enlightened */
  /* systems do not. fix from awjacks@ca.sandia.gov raj 10/95 */  
  /* order changed to check for IP address first. raj 7/96 */

  if ((addr = inet_addr(remote_host)) == SOCKET_ERROR) {
    /* it was not an IP address, try it as a name */
    if ((hp = gethostbyname(remote_host)) == NULL) {
      /* we have no idea what it is */
      fprintf(where,
	      "establish_control: could not resolve the destination %s\n",
	      remote_host);
      fflush(where);
      exit(1);
    }
    else {
      /* it was a valid remote_host */
      bcopy(hp->h_addr,
	    (char *)&server.sin_addr,
	    hp->h_length);
      server.sin_family = hp->h_addrtype;
    }
  }
  else {
    /* it was a valid IP address */
    server.sin_addr.s_addr = addr;
    server.sin_family = AF_INET;
  }    
  
  if ( print_headers ) {
    fprintf(where,"XTI TCP REQUEST/RESPONSE TEST");
    fprintf(where," to %s", remote_host);
    if (iteration_max > 1) {
      fprintf(where,
	      " : +/-%3.1f%% @ %2d%% conf.",
	      interval/0.02,
	      confidence_level);
      }
    if (loc_nodelay || rem_nodelay) {
      fprintf(where," : nodelay");
    }
    if (loc_sndavoid || 
	loc_rcvavoid ||
	rem_sndavoid ||
	rem_rcvavoid) {
      fprintf(where," : copy avoidance");
    }
#ifdef WANT_HISTOGRAM
    fprintf(where," : histogram");
#endif /* WANT_HISTOGRAM */
#ifdef WANT_INTERVALS
    fprintf(where," : interval");
#endif /* WANT_INTERVALS */
#ifdef DIRTY 
    fprintf(where," : dirty data");
#endif /* DIRTY */
    fprintf(where,"\n");
  }
  
  /* initialize a few counters */
  
  send_ring = NULL;
  recv_ring = NULL;
  confidence_iteration = 1;
  init_stat();

  /* we have a great-big while loop which controls the number of times */
  /* we run a particular test. this is for the calculation of a */
  /* confidence interval (I really should have stayed awake during */
  /* probstats :). If the user did not request confidence measurement */
  /* (no confidence is the default) then we will only go though the */
  /* loop once. the confidence stuff originates from the folks at IBM */

  while (((confidence < 0) && (confidence_iteration < iteration_max)) ||
	 (confidence_iteration <= iteration_min)) {

    /* initialize a few counters. we have to remember that we might be */
    /* going through the loop more than once. */

    nummessages     = 0;
    bytes_xferd     = 0.0;
    times_up        = 0;
    timed_out       = 0;
    trans_remaining = 0;

    /* set-up the data buffers with the requested alignment and offset. */
    /* since this is a request/response test, default the send_width and */
    /* recv_width to 1 and not two raj 7/94 */

    if (send_width == 0) send_width = 1;
    if (recv_width == 0) recv_width = 1;
  
    if (send_ring == NULL) {
      send_ring = allocate_buffer_ring(send_width,
				       req_size,
				       local_send_align,
				       local_send_offset);
    }

    if (recv_ring == NULL) {
      recv_ring = allocate_buffer_ring(recv_width,
				       rsp_size,
				       local_recv_align,
				       local_recv_offset);
    }
    
    /*set up the data socket                        */
    send_socket = create_xti_endpoint(loc_xti_device);
  
    if (send_socket == INVALID_SOCKET){
      perror("netperf: send_xti_tcp_rr: tcp stream data socket");
      exit(1);
    }
    
    if (debug) {
      fprintf(where,"send_xti_tcp_rr: send_socket obtained...\n");
    }

    /* it would seem that with XTI, there is no implicit bind on a */
    /* connect, so we have to make a call to t_bind. this is not */
    /* terribly convenient, but I suppose that "standard is better */
    /* than better" :) raj 2/95 */

    if (t_bind(send_socket, NULL, NULL) == SOCKET_ERROR) {
      t_error("send_xti_tcp_stream: t_bind");
      exit(1);
    }
  
    /* If the user has requested cpu utilization measurements, we must */
    /* calibrate the cpu(s). We will perform this task within the tests */
    /* themselves. If the user has specified the cpu rate, then */
    /* calibrate_local_cpu will return rather quickly as it will have */
    /* nothing to do. If local_cpu_rate is zero, then we will go through */
    /* all the "normal" calibration stuff and return the rate back.*/
    
    if (local_cpu_usage) {
      local_cpu_rate = calibrate_local_cpu(local_cpu_rate);
    }
    
    /* Tell the remote end to do a listen. The server alters the socket */
    /* paramters on the other side at this point, hence the reason for */
    /* all the values being passed in the setup message. If the user did */
    /* not specify any of the parameters, they will be passed as 0, which */
    /* will indicate to the remote that no changes beyond the system's */
    /* default should be used. Alignment is the exception, it will */
    /* default to 8, which will be no alignment alterations. */
    
    netperf_request.content.request_type	=	DO_XTI_TCP_RR;
    xti_tcp_rr_request->recv_buf_size	=	rsr_size;
    xti_tcp_rr_request->send_buf_size	=	rss_size;
    xti_tcp_rr_request->recv_alignment  =	remote_recv_align;
    xti_tcp_rr_request->recv_offset	=	remote_recv_offset;
    xti_tcp_rr_request->send_alignment  =	remote_send_align;
    xti_tcp_rr_request->send_offset	=	remote_send_offset;
    xti_tcp_rr_request->request_size	=	req_size;
    xti_tcp_rr_request->response_size	=	rsp_size;
    xti_tcp_rr_request->no_delay	=	rem_nodelay;
    xti_tcp_rr_request->measure_cpu	=	remote_cpu_usage;
    xti_tcp_rr_request->cpu_rate	=	remote_cpu_rate;
    xti_tcp_rr_request->so_rcvavoid	=	rem_rcvavoid;
    xti_tcp_rr_request->so_sndavoid	=	rem_sndavoid;
    if (test_time) {
      xti_tcp_rr_request->test_length	=	test_time;
    }
    else {
      xti_tcp_rr_request->test_length	=	test_trans * -1;
    }

    strcpy(xti_tcp_rr_request->xti_device, rem_xti_device);
  
#ifdef __alpha
  
    /* ok - even on a DEC box, strings are strings. I didn't really want */
    /* to ntohl the words of a string. since I don't want to teach the */
    /* send_ and recv_ _request and _response routines about the types, */
    /* I will put "anti-ntohl" calls here. I imagine that the "pure" */
    /* solution would be to use XDR, but I am still leary of being able */
    /* to find XDR libs on all platforms I want running netperf. raj */
    {
      int *charword;
      int *initword;
      int *lastword;
      
      initword = (int *) xti_tcp_rr_request->xti_device;
      lastword = initword + ((strlen(rem_xti_device) + 3) / 4);
      
      for (charword = initword;
	   charword < lastword;
	   charword++) {
	
	*charword = ntohl(*charword);
      }
    }
#endif /* __alpha */
    
    if (debug > 1) {
      fprintf(where,"netperf: send_xti_tcp_rr: requesting TCP rr test\n");
    }
    
    send_request();
    
    /* The response from the remote will contain all of the relevant 	*/
    /* socket parameters for this test type. We will put them back into */
    /* the variables here so they can be displayed if desired.  The	*/
    /* remote will have calibrated CPU if necessary, and will have done	*/
    /* all the needed set-up we will have calibrated the cpu locally	*/
    /* before sending the request, and will grab the counter value right*/
    /* after the connect returns. The remote will grab the counter right*/
    /* after the accept call. This saves the hassle of extra messages	*/
    /* being sent for the TCP tests.					*/
  
    recv_response();
  
    if (!netperf_response.content.serv_errno) {
      if (debug)
	fprintf(where,"remote listen done.\n");
      rsr_size          = xti_tcp_rr_response->recv_buf_size;
      rss_size          = xti_tcp_rr_response->send_buf_size;
      rem_nodelay       = xti_tcp_rr_response->no_delay;
      remote_cpu_usage  = xti_tcp_rr_response->measure_cpu;
      remote_cpu_rate   = xti_tcp_rr_response->cpu_rate;
      /* make sure that port numbers are in network order */
      server.sin_port   = (short)xti_tcp_rr_response->data_port_number;
      server.sin_port   = htons(server.sin_port);
    }
    else {
      Set_errno(netperf_response.content.serv_errno);
      perror("netperf: remote error");
      
      exit(1);
    }
    
    /*Connect up to the remote port on the data socket  */
    memset (&server_call, 0, sizeof(server_call));
    server_call.addr.maxlen = sizeof(struct sockaddr_in);
    server_call.addr.len    = sizeof(struct sockaddr_in);
    server_call.addr.buf    = (char *)&server;

    if (t_connect(send_socket, 
		  &server_call,
		  NULL) == INVALID_SOCKET){
      t_error("netperf: send_xti_tcp_rr: data socket connect failed");
      printf(" port: %d\n",ntohs(server.sin_port));
      exit(1);
    }

    /* Data Socket set-up is finished. If there were problems, either the */
    /* connect would have failed, or the previous response would have */
    /* indicated a problem. I failed to see the value of the extra */
    /* message after the accept on the remote. If it failed, we'll see it */
    /* here. If it didn't, we might as well start pumping data. */
    
    /* Set-up the test end conditions. For a request/response test, they */
    /* can be either time or transaction based. */
    
    if (test_time) {
      /* The user wanted to end the test after a period of time. */
      times_up = 0;
      trans_remaining = 0;
      start_timer(test_time);
    }
    else {
      /* The tester wanted to send a number of bytes. */
      trans_remaining = test_bytes;
      times_up = 1;
    }
    
    /* The cpu_start routine will grab the current time and possibly */
    /* value of the idle counter for later use in measuring cpu */
    /* utilization and/or service demand and thruput. */
    
    cpu_start(local_cpu_usage);

#ifdef WANT_INTERVALS
    if ((interval_burst) || (demo_mode)) {
      /* zero means that we never pause, so we never should need the */
      /* interval timer, unless we are in demo_mode */
      start_itimer(interval_wate);
    }
    interval_count = interval_burst;
    /* get the signal set for the call to sigsuspend */
    if (sigprocmask(SIG_BLOCK, (sigset_t *)NULL, &signal_set) != 0) {
      fprintf(where,
	      "send_xti_tcp_rr: unable to get sigmask errno %d\n",
	      errno);
      fflush(where);
      exit(1);
    }
#endif /* WANT_INTERVALS */
    
    /* We use an "OR" to control test execution. When the test is */
    /* controlled by time, the byte count check will always return false. */
    /* When the test is controlled by byte count, the time test will */
    /* always return false. When the test is finished, the whole */
    /* expression will go false and we will stop sending data. I think I */
    /* just arbitrarily decrement trans_remaining for the timed test, but */
    /* will not do that just yet... One other question is whether or not */
    /* the send buffer and the receive buffer should be the same buffer. */

    while ((!times_up) || (trans_remaining > 0)) {
      /* send the request. we assume that if we use a blocking socket, */
      /* the request will be sent at one shot. */
      
#ifdef WANT_HISTOGRAM
      /* timestamp just before our call to send, and then again just */
      /* after the receive raj 8/94 */
      HIST_timestamp(&time_one);
#endif /* WANT_HISTOGRAM */
      
      if((len=t_snd(send_socket,
		    send_ring->buffer_ptr,
		    req_size,
		    0)) != req_size) {
	if ((errno == EINTR) || (errno == 0)) {
	  /* we hit the end of a */
	  /* timed test. */
	  timed_out = 1;
	  break;
	}
        fprintf(where,
		"send_xti_tcp_rr: t_snd: errno %d t_errno %d t_look 0x%.4x\n",
		errno,
		t_errno,
		t_look(send_socket));
	fflush(where);
        exit(1);
      }
      send_ring = send_ring->next;
      
      /* receive the response */
      rsp_bytes_left = rsp_size;
      temp_message_ptr  = recv_ring->buffer_ptr;
      while(rsp_bytes_left > 0) {
	if((rsp_bytes_recvd=t_rcv(send_socket,
				  temp_message_ptr,
				  rsp_bytes_left,
				  &xti_flags)) == SOCKET_ERROR) {
	  if (errno == EINTR) {
	    /* We hit the end of a timed test. */
	    timed_out = 1;
	    break;
	  }
	  fprintf(where,
		  "send_xti_tcp_rr: t_rcv: errno %d t_errno %d t_look 0x%x\n",
		  errno,
		  t_errno,
		  t_look(send_socket));
	  fflush(where);
	  exit(1);
	}
	rsp_bytes_left -= rsp_bytes_recvd;
	temp_message_ptr  += rsp_bytes_recvd;
      }	
      recv_ring = recv_ring->next;
      
      if (timed_out) {
	/* we may have been in a nested while loop - we need */
	/* another call to break. */
	break;
      }
      
#ifdef WANT_HISTOGRAM
      HIST_timestamp(&time_two);
      HIST_add(time_hist,delta_micro(&time_one,&time_two));
#endif /* WANT_HISTOGRAM */
#ifdef WANT_INTERVALS      
      if (demo_mode) {
	units_this_tick += 1;
      }
      /* in this case, the interval count is the count-down couter */
      /* to decide to sleep for a little bit */
      if ((interval_burst) && (--interval_count == 0)) {
	/* call sigsuspend and wait for the interval timer to get us */
	/* out */
	if (debug) {
	  fprintf(where,"about to suspend\n");
	  fflush(where);
	}
	if (sigsuspend(&signal_set) == EFAULT) {
	  fprintf(where,
		  "send_xti_udp_rr: fault with signal set!\n");
	  fflush(where);
	  exit(1);
	}
	interval_count = interval_burst;
      }
#endif /* WANT_INTERVALS */
      
      nummessages++;          
      if (trans_remaining) {
	trans_remaining--;
      }
      
      if (debug > 3) {
	if ((nummessages % 100) == 0) {
	  fprintf(where,
		  "Transaction %d completed\n",
		  nummessages);
	  fflush(where);
	}
      }
    }

    
    /* this call will always give us the elapsed time for the test, and */
    /* will also store-away the necessaries for cpu utilization */
    
    cpu_stop(local_cpu_usage,&elapsed_time);	/* was cpu being */
						/* measured? how long */
						/* did we really run? */
    
    /* Get the statistics from the remote end. The remote will have */
    /* calculated service demand and all those interesting things. If it */
    /* wasn't supposed to care, it will return obvious values. */
    
    recv_response();
    if (!netperf_response.content.serv_errno) {
      if (debug)
	fprintf(where,"remote results obtained\n");
    }
    else {
      Set_errno(netperf_response.content.serv_errno);
      perror("netperf: remote error");
      
      exit(1);
    }
    
    /* We now calculate what our thruput was for the test. */
  
    bytes_xferd	= (req_size * nummessages) + (rsp_size * nummessages);
    thruput	= nummessages/elapsed_time;
  
    if (local_cpu_usage || remote_cpu_usage) {
      /* We must now do a little math for service demand and cpu */
      /* utilization for the system(s) */
      /* Of course, some of the information might be bogus because */
      /* there was no idle counter in the kernel(s). We need to make */
      /* a note of this for the user's benefit...*/
      if (local_cpu_usage) {
	local_cpu_utilization = calc_cpu_util(0.0);
	/* since calc_service demand is doing ms/Kunit we will */
	/* multiply the number of transaction by 1024 to get */
	/* "good" numbers */
	local_service_demand  = calc_service_demand((double) nummessages*1024,
						    0.0,
						    0.0,
						    0);
      }
      else {
	local_cpu_utilization	= -1.0;
	local_service_demand	= -1.0;
      }
      
      if (remote_cpu_usage) {
	remote_cpu_utilization = xti_tcp_rr_result->cpu_util;
	/* since calc_service demand is doing ms/Kunit we will */
	/* multiply the number of transaction by 1024 to get */
	/* "good" numbers */
	remote_service_demand = calc_service_demand((double) nummessages*1024,
						    0.0,
						    remote_cpu_utilization,
						    xti_tcp_rr_result->num_cpus);
      }
      else {
	remote_cpu_utilization = -1.0;
	remote_service_demand  = -1.0;
      }
      
    }
    else {
      /* we were not measuring cpu, for the confidence stuff, we */
      /* should make it -1.0 */
      local_cpu_utilization	= -1.0;
      local_service_demand	= -1.0;
      remote_cpu_utilization = -1.0;
      remote_service_demand  = -1.0;
    }

    /* at this point, we want to calculate the confidence information. */
    /* if debugging is on, calculate_confidence will print-out the */
    /* parameters we pass it */
    
    calculate_confidence(confidence_iteration,
			 elapsed_time,
			 thruput,
			 local_cpu_utilization,
			 remote_cpu_utilization,
			 local_service_demand,
			 remote_service_demand);
    
    
    confidence_iteration++;

    /* we are now done with the socket, so close it */
    t_close(send_socket);

  }

  retrieve_confident_values(&elapsed_time,
			    &thruput,
			    &local_cpu_utilization,
			    &remote_cpu_utilization,
			    &local_service_demand,
			    &remote_service_demand);

  /* We are now ready to print all the information. If the user */
  /* has specified zero-level verbosity, we will just print the */
  /* local service demand, or the remote service demand. If the */
  /* user has requested verbosity level 1, he will get the basic */
  /* "streamperf" numbers. If the user has specified a verbosity */
  /* of greater than 1, we will display a veritable plethora of */
  /* background information from outside of this block as it it */
  /* not cpu_measurement specific...  */

  if (confidence < 0) {
    /* we did not hit confidence, but were we asked to look for it? */
    if (iteration_max > 1) {
      display_confidence();
    }
  }

  if (local_cpu_usage || remote_cpu_usage) {
    local_cpu_method = format_cpu_method(cpu_method);
    remote_cpu_method = format_cpu_method(xti_tcp_rr_result->cpu_method);
    
    switch (verbosity) {
    case 0:
      if (local_cpu_usage) {
	fprintf(where,
		cpu_fmt_0,
		local_service_demand,
		local_cpu_method);
      }
      else {
	fprintf(where,
		cpu_fmt_0,
		remote_service_demand,
		remote_cpu_method);
      }
      break;
    case 1:
    case 2:
      if (print_headers) {
	fprintf(where,
		cpu_title,
		local_cpu_method,
		remote_cpu_method);
      }

      fprintf(where,
	      cpu_fmt_1_line_1,		/* the format string */
	      lss_size,		/* local sendbuf size */
	      lsr_size,
	      req_size,		/* how large were the requests */
	      rsp_size,		/* guess */
	      elapsed_time,		/* how long was the test */
	      thruput,
	      local_cpu_utilization,	/* local cpu */
	      remote_cpu_utilization,	/* remote cpu */
	      local_service_demand,	/* local service demand */
	      remote_service_demand);	/* remote service demand */
      fprintf(where,
	      cpu_fmt_1_line_2,
	      rss_size,
	      rsr_size);
      break;
    }
  }
  else {
    /* The tester did not wish to measure service demand. */
    
    switch (verbosity) {
    case 0:
      fprintf(where,
	      tput_fmt_0,
	      thruput);
      break;
    case 1:
    case 2:
      if (print_headers) {
	fprintf(where,tput_title,format_units());
      }

      fprintf(where,
	      tput_fmt_1_line_1,	/* the format string */
	      lss_size,
	      lsr_size,
	      req_size,		/* how large were the requests */
	      rsp_size,		/* how large were the responses */
	      elapsed_time, 		/* how long did it take */
	      thruput);
      fprintf(where,
	      tput_fmt_1_line_2,
	      rss_size, 		/* remote recvbuf size */
	      rsr_size);
      
      break;
    }
  }
  
  /* it would be a good thing to include information about some of the */
  /* other parameters that may have been set for this test, but at the */
  /* moment, I do not wish to figure-out all the  formatting, so I will */
  /* just put this comment here to help remind me that it is something */
  /* that should be done at a later time. */
  
  /* how to handle the verbose information in the presence of */
  /* confidence intervals is yet to be determined... raj 11/94 */
  if (verbosity > 1) {
    /* The user wanted to know it all, so we will give it to him. */
    /* This information will include as much as we can find about */
    /* TCP statistics, the alignments of the sends and receives */
    /* and all that sort of rot... */
    
    fprintf(where,
	    ksink_fmt,
	    local_send_align,
	    remote_recv_offset,
	    local_send_offset,
	    remote_recv_offset);

#ifdef WANT_HISTOGRAM
    fprintf(where,"\nHistogram of request/response times\n");
    fflush(where);
    HIST_report(time_hist);
#endif /* WANT_HISTOGRAM */

  }
  
}

void
send_xti_udp_stream(char remote_host[])
{
  /**********************************************************************/
  /*									*/
  /*               	UDP Unidirectional Send Test                    */
  /*									*/
  /**********************************************************************/
  char *tput_title = "\
Socket  Message  Elapsed      Messages                \n\
Size    Size     Time         Okay Errors   Throughput\n\
bytes   bytes    secs            #      #   %s/sec\n\n";
  
  char *tput_fmt_0 =
    "%7.2f\n";
  
  char *tput_fmt_1 = "\
%6d  %6d   %-7.2f   %7d %6d    %7.2f\n\
%6d           %-7.2f   %7d           %7.2f\n\n";
  
  
  char *cpu_title = "\
Socket  Message  Elapsed      Messages                   CPU      Service\n\
Size    Size     Time         Okay Errors   Throughput   Util     Demand\n\
bytes   bytes    secs            #      #   %s/sec %% %c%c     us/KB\n\n";
  
  char *cpu_fmt_0 =
    "%6.2f %c\n";
  
  char *cpu_fmt_1 = "\
%6d  %6d   %-7.2f   %7d %6d    %7.1f     %-6.2f   %-6.3f\n\
%6d           %-7.2f   %7d           %7.1f     %-6.2f   %-6.3f\n\n";
  
  unsigned int	messages_recvd;
  unsigned int 	messages_sent;
  unsigned int	failed_sends;

  float	elapsed_time,  
        recv_elapsed,
        local_cpu_utilization,
        remote_cpu_utilization;
  
  float	 local_service_demand, remote_service_demand;
  double local_thruput, remote_thruput;
  double bytes_sent;
  double bytes_recvd;
  
  
  int	len;
  int	*message_int_ptr;
  struct ring_elt *send_ring;
  SOCKET data_socket;
  
  unsigned int sum_messages_sent;
  unsigned int sum_messages_recvd;
  unsigned int sum_failed_sends;
  double sum_local_thruput;

#ifdef WANT_INTERVALS
  int	interval_count;
  sigset_t signal_set;
#endif /* WANT_INTERVALS */
  
  struct   hostent     *hp;
  struct   sockaddr_in server;
  unsigned int         addr;
  
  struct t_unitdata unitdata;
   
  struct xti_udp_stream_request_struct	*xti_udp_stream_request;
  struct xti_udp_stream_response_struct	*xti_udp_stream_response;
  struct xti_udp_stream_results_struct	*xti_udp_stream_results;
  
  xti_udp_stream_request  = 
    (struct xti_udp_stream_request_struct *)netperf_request.content.test_specific_data;
  xti_udp_stream_response = 
    (struct xti_udp_stream_response_struct *)netperf_response.content.test_specific_data;
  xti_udp_stream_results  = 
    (struct xti_udp_stream_results_struct *)netperf_response.content.test_specific_data;
  
#ifdef WANT_HISTOGRAM
  time_hist = HIST_new();
#endif /* WANT_HISTOGRAM */

  /* since we are now disconnected from the code that established the */
  /* control socket, and since we want to be able to use different */
  /* protocols and such, we are passed the name of the remote host and */
  /* must turn that into the test specific addressing information. */
  
  bzero((char *)&server,
	sizeof(server));
  
  /* it would seem that while HP-UX will allow an IP address (as a */
  /* string) in a call to gethostbyname, other, less enlightened */
  /* systems do not. fix from awjacks@ca.sandia.gov raj 10/95 */  
  /* order changed to check for IP address first. raj 7/96 */

  if ((addr = inet_addr(remote_host)) == SOCKET_ERROR) {
    /* it was not an IP address, try it as a name */
    if ((hp = gethostbyname(remote_host)) == NULL) {
      /* we have no idea what it is */
      fprintf(where,
	      "establish_control: could not resolve the destination %s\n",
	      remote_host);
      fflush(where);
      exit(1);
    }
    else {
      /* it was a valid remote_host */
      bcopy(hp->h_addr,
	    (char *)&server.sin_addr,
	    hp->h_length);
      server.sin_family = hp->h_addrtype;
    }
  }
  else {
    /* it was a valid IP address */
    server.sin_addr.s_addr = addr;
    server.sin_family = AF_INET;
  }    
  
  if ( print_headers ) {
    fprintf(where,"UDP UNIDIRECTIONAL SEND TEST");
    fprintf(where," to %s", remote_host);
    if (iteration_max > 1) {
      fprintf(where,
	      " : +/-%3.1f%% @ %2d%% conf.",
	      interval/0.02,
	      confidence_level);
      }
    if (loc_sndavoid || 
	loc_rcvavoid ||
	rem_sndavoid ||
	rem_rcvavoid) {
      fprintf(where," : copy avoidance");
    }
#ifdef WANT_HISTOGRAM
    fprintf(where," : histogram");
#endif /* WANT_HISTOGRAM */
#ifdef WANT_INTERVALS
    fprintf(where," : interval");
#endif /* WANT_INTERVALS */
#ifdef DIRTY 
    fprintf(where," : dirty data");
#endif /* DIRTY */
    fprintf(where,"\n");
  }	
  
  send_ring            = NULL;
  confidence_iteration = 1;
  init_stat();
  sum_messages_sent    = 0;
  sum_messages_recvd   = 0;
  sum_failed_sends     = 0;
  sum_local_thruput    = 0.0;

  /* we have a great-big while loop which controls the number of times */
  /* we run a particular test. this is for the calculation of a */
  /* confidence interval (I really should have stayed awake during */
  /* probstats :). If the user did not request confidence measurement */
  /* (no confidence is the default) then we will only go though the */
  /* loop once. the confidence stuff originates from the folks at IBM */

  while (((confidence < 0) && (confidence_iteration < iteration_max)) ||
	 (confidence_iteration <= iteration_min)) {
    
    /* initialize a few counters. we have to remember that we might be */
    /* going through the loop more than once. */
    messages_sent  = 0;
    messages_recvd = 0;
    failed_sends   = 0;
    times_up       = 0;
    
    /*set up the data socket			*/
    data_socket = create_xti_endpoint(loc_xti_device);
    
    if (data_socket == INVALID_SOCKET) {
      perror("send_xti_udp_stream: create_xti_endpoint");
      exit(1);
    }

    if (t_bind(data_socket, NULL, NULL) == SOCKET_ERROR) {
      t_error("send_xti_udp_stream: t_bind");
      exit(1);
    }

    /* now, we want to see if we need to set the send_size */
    if (send_size == 0) {
      if (lss_size > 0) {
	send_size = lss_size;
      }
      else {
	send_size = 4096;
      }
    }
    
    /* set-up the data buffer with the requested alignment and offset, */
    /* most of the numbers here are just a hack to pick something nice */
    /* and big in an attempt to never try to send a buffer a second time */
    /* before it leaves the node...unless the user set the width */
    /* explicitly. */
    if (send_width == 0) send_width = 32;
    
    if (send_ring == NULL ) {
      send_ring = allocate_buffer_ring(send_width,
				       send_size,
				       local_send_align,
				       local_send_offset);
    }
    
    
    /* if the user supplied a cpu rate, this call will complete rather */
    /* quickly, otherwise, the cpu rate will be retured to us for */
    /* possible display. The Library will keep it's own copy of this data */
    /* for use elsewhere. We will only display it. (Does that make it */
    /* "opaque" to us?) */
    
    if (local_cpu_usage)
      local_cpu_rate = calibrate_local_cpu(local_cpu_rate);
    
    /* Tell the remote end to set up the data connection. The server */
    /* sends back the port number and alters the socket parameters there. */
    /* Of course this is a datagram service so no connection is actually */
    /* set up, the server just sets up the socket and binds it. */
    
    netperf_request.content.request_type      = DO_XTI_UDP_STREAM;
    xti_udp_stream_request->recv_buf_size  = rsr_size;
    xti_udp_stream_request->message_size   = send_size;
    xti_udp_stream_request->recv_alignment = remote_recv_align;
    xti_udp_stream_request->recv_offset    = remote_recv_offset;
    xti_udp_stream_request->measure_cpu    = remote_cpu_usage;
    xti_udp_stream_request->cpu_rate       = remote_cpu_rate;
    xti_udp_stream_request->test_length    = test_time;
    xti_udp_stream_request->so_rcvavoid    = rem_rcvavoid;
    xti_udp_stream_request->so_sndavoid    = rem_sndavoid;
    
    strcpy(xti_udp_stream_request->xti_device, rem_xti_device);
  
#ifdef __alpha
  
    /* ok - even on a DEC box, strings are strings. I didn't really want */
    /* to ntohl the words of a string. since I don't want to teach the */
    /* send_ and recv_ _request and _response routines about the types, */
    /* I will put "anti-ntohl" calls here. I imagine that the "pure" */
    /* solution would be to use XDR, but I am still leary of being able */
    /* to find XDR libs on all platforms I want running netperf. raj */
    {
      int *charword;
      int *initword;
      int *lastword;
      
      initword = (int *) xti_udp_stream_request->xti_device;
      lastword = initword + ((strlen(rem_xti_device) + 3) / 4);
      
      for (charword = initword;
	   charword < lastword;
	   charword++) {
	
	*charword = ntohl(*charword);
      }
    }
#endif /* __alpha */

    send_request();
    
    recv_response();
    
    if (!netperf_response.content.serv_errno) {
      if (debug)
	fprintf(where,"send_xti_udp_stream: remote data connection done.\n");
    }
    else {
      Set_errno(netperf_response.content.serv_errno);
      perror("send_xti_udp_stream: error on remote");
      exit(1);
    }
    
    /* Place the port number returned by the remote into the sockaddr */
    /* structure so our sends can be sent to the correct place. Also get */
    /* some of the returned socket buffer information for user display. */
    
    /* make sure that port numbers are in the proper order */
    server.sin_port = (short)xti_udp_stream_response->data_port_number;
    server.sin_port = htons(server.sin_port);
    rsr_size        = xti_udp_stream_response->recv_buf_size;
    rss_size        = xti_udp_stream_response->send_buf_size;
    remote_cpu_rate = xti_udp_stream_response->cpu_rate;
    
    /* it would seem that XTI does not allow the expedient of */
    /* "connecting" a UDP end-point the way BSD does. so, we will do */
    /* everything with t_sndudata and t_rcvudata. Our "virtual" */
    /* connect here will be to assign the destination portion of the */
    /* t_unitdata struct here, where we would have otherwise called */
    /* t_connect() raj 3/95 */
    
    memset (&unitdata, 0, sizeof(unitdata));
    unitdata.addr.maxlen = sizeof(struct sockaddr_in);
    unitdata.addr.len    = sizeof(struct sockaddr_in);
    unitdata.addr.buf    = (char *)&server;

    /* we don't use any options, so might as well set that part here */
    /* too */

    unitdata.opt.maxlen = 0;
    unitdata.opt.len    = 0;
    unitdata.opt.buf    = NULL;

    /* we need to initialize the send buffer for the first time as */
    /* well since we move to the next pointer after the send call. */

    unitdata.udata.maxlen = send_size;
    unitdata.udata.len    = send_size;
    unitdata.udata.buf    = send_ring->buffer_ptr;

    /* set up the timer to call us after test_time. one of these days, */
    /* it might be nice to figure-out a nice reliable way to have the */
    /* test controlled by a byte count as well, but since UDP is not */
    /* reliable, that could prove difficult. so, in the meantime, we */
    /* only allow a XTI_UDP_STREAM test to be a timed test. */
    
    if (test_time) {
      times_up = 0;
      start_timer(test_time);
    }
    else {
      fprintf(where,"Sorry, XTI_UDP_STREAM tests must be timed.\n");
      fflush(where);
      exit(1);
    }
    
    /* Get the start count for the idle counter and the start time */
    
    cpu_start(local_cpu_usage);
    
#ifdef WANT_INTERVALS
    if ((interval_burst) || (demo_mode)) {
      /* zero means that we never pause, so we never should need the */
      /* interval timer, unless we are in demo_mode */
      start_itimer(interval_wate);
    }
    interval_count = interval_burst;
    /* get the signal set for the call to sigsuspend */
    if (sigprocmask(SIG_BLOCK, (sigset_t *)NULL, &signal_set) != 0) {
      fprintf(where,
	      "send_xti_udp_stream: unable to get sigmask errno %d\n",
	      errno);
      fflush(where);
      exit(1);
    }
#endif /* WANT_INTERVALS */
    
    /* Send datagrams like there was no tomorrow. at somepoint it might */
    /* be nice to set this up so that a quantity of bytes could be sent, */
    /* but we still need some sort of end of test trigger on the receive */
    /* side. that could be a select with a one second timeout, but then */
    /* if there is a test where none of the data arrives for awile and */
    /* then starts again, we would end the test too soon. something to */
    /* think about... */
    while (!times_up) {
      
#ifdef DIRTY
      /* we want to dirty some number of consecutive integers in the buffer */
      /* we are about to send. we may also want to bring some number of */
      /* them cleanly into the cache. The clean ones will follow any dirty */
      /* ones into the cache. */

      access_buffer(send_ring->buffer_ptr,
		    send_size,
		    loc_dirty_count,
		    loc_clean_count);

#endif /* DIRTY */
      
#ifdef WANT_HISTOGRAM
      HIST_timestamp(&time_one);
#endif /* WANT_HISTOGRAM */
      
      if ((t_sndudata(data_socket,
		      &unitdata))  != 0) {
	if (errno == EINTR)
	  break;
	if (errno == ENOBUFS) {
	  failed_sends++;
	  continue;
	}
	perror("xti_udp_send: data send error");
	t_error("xti_udp_send: data send error");
	exit(1);
      }
      messages_sent++;          
      
      /* now we want to move our pointer to the next position in the */
      /* data buffer...and update the unitdata structure */
      
      send_ring          = send_ring->next;
      unitdata.udata.buf = send_ring->buffer_ptr;
      
#ifdef WANT_HISTOGRAM
      /* get the second timestamp */
      HIST_timestamp(&time_two);
      HIST_add(time_hist,delta_micro(&time_one,&time_two));
#endif /* WANT_HISTOGRAM */
#ifdef WANT_INTERVALS      
      if (demo_mode) {
	units_this_tick += send_size;
      }
      /* in this case, the interval count is the count-down couter */
      /* to decide to sleep for a little bit */
      if ((interval_burst) && (--interval_count == 0)) {
	/* call sigsuspend and wait for the interval timer to get us */
	/* out */
	if (debug) {
	  fprintf(where,"about to suspend\n");
	  fflush(where);
	}
	if (sigsuspend(&signal_set) == EFAULT) {
	  fprintf(where,
		  "send_xti_udp_stream: fault with signal set!\n");
	  fflush(where);
	  exit(1);
	}
	interval_count = interval_burst;
      }
#endif /* WANT_INTERVALS */
      
    }
    
    /* This is a timed test, so the remote will be returning to us after */
    /* a time. We should not need to send any "strange" messages to tell */
    /* the remote that the test is completed, unless we decide to add a */
    /* number of messages to the test. */
    
    /* the test is over, so get stats and stuff */
    cpu_stop(local_cpu_usage,	
	     &elapsed_time);
    
    /* Get the statistics from the remote end	*/
    recv_response();
    if (!netperf_response.content.serv_errno) {
      if (debug)
	fprintf(where,"send_xti_udp_stream: remote results obtained\n");
    }
    else {
      Set_errno(netperf_response.content.serv_errno);
      perror("send_xti_udp_stream: error on remote");
      exit(1);
    }
    
    bytes_sent    = (double) send_size * (double) messages_sent;
    local_thruput = calc_thruput(bytes_sent);
    
    messages_recvd = xti_udp_stream_results->messages_recvd;
    bytes_recvd    = (double) send_size * (double) messages_recvd;
    
    /* we asume that the remote ran for as long as we did */
    
    remote_thruput = calc_thruput(bytes_recvd);
    
    /* print the results for this socket and message size */
    
    if (local_cpu_usage || remote_cpu_usage) {
      /* We must now do a little math for service demand and cpu */
      /* utilization for the system(s) We pass zeros for the local */
      /* cpu utilization and elapsed time to tell the routine to use */
      /* the libraries own values for those. */
      if (local_cpu_usage) {
	local_cpu_utilization	= calc_cpu_util(0.0);
	/* shouldn't this really be based on bytes_recvd, since that is */
	/* the effective throughput of the test? I think that it should, */
	/* so will make the change raj 11/94 */
	local_service_demand	= calc_service_demand(bytes_recvd,
						      0.0,
						      0.0,
						      0);
      }
      else {
	local_cpu_utilization	= -1.0;
	local_service_demand	= -1.0;
      }
      
      /* The local calculations could use variables being kept by */
      /* the local netlib routines. The remote calcuations need to */
      /* have a few things passed to them. */
      if (remote_cpu_usage) {
	remote_cpu_utilization	= xti_udp_stream_results->cpu_util;
	remote_service_demand	= calc_service_demand(bytes_recvd,
						      0.0,
						      remote_cpu_utilization,
						      xti_udp_stream_results->num_cpus);
      }
      else {
	remote_cpu_utilization	= -1.0;
	remote_service_demand	= -1.0;
      }
    }
    else {
      /* we were not measuring cpu, for the confidence stuff, we */
      /* should make it -1.0 */
      local_cpu_utilization  = -1.0;
      local_service_demand   = -1.0;
      remote_cpu_utilization = -1.0;
      remote_service_demand  = -1.0;
    }
    
    /* at this point, we want to calculate the confidence information. */
    /* if debugging is on, calculate_confidence will print-out the */
    /* parameters we pass it */
    
    calculate_confidence(confidence_iteration,
			 elapsed_time,
			 remote_thruput,
			 local_cpu_utilization,
			 remote_cpu_utilization,
			 local_service_demand,
			 remote_service_demand);
    
    /* since the routine calculate_confidence is rather generic, and */
    /* we have a few other parms of interest, we will do a little work */
    /* here to caclulate their average. */
    sum_messages_sent  += messages_sent;
    sum_messages_recvd += messages_recvd;
    sum_failed_sends   += failed_sends;
    sum_local_thruput  += local_thruput;
    
    confidence_iteration++;

    /* this datapoint is done, so we don't need the socket any longer */
    close(data_socket);

  }

  /* we should reach this point once the test is finished */

  retrieve_confident_values(&elapsed_time,
			    &remote_thruput,
			    &local_cpu_utilization,
			    &remote_cpu_utilization,
			    &local_service_demand,
			    &remote_service_demand);

  /* some of the interesting values aren't covered by the generic */
  /* confidence routine */
  messages_sent    = sum_messages_sent / (confidence_iteration -1);
  messages_recvd   = sum_messages_recvd / (confidence_iteration -1);
  failed_sends     = sum_failed_sends / (confidence_iteration -1);
  local_thruput    = sum_local_thruput / (confidence_iteration -1);

  /* We are now ready to print all the information. If the user */
  /* has specified zero-level verbosity, we will just print the */
  /* local service demand, or the remote service demand. If the */
  /* user has requested verbosity level 1, he will get the basic */
  /* "streamperf" numbers. If the user has specified a verbosity */
  /* of greater than 1, we will display a veritable plethora of */
  /* background information from outside of this block as it it */
  /* not cpu_measurement specific...  */
    
  
  if (confidence < 0) {
    /* we did not hit confidence, but were we asked to look for it? */
    if (iteration_max > 1) {
      display_confidence();
    }
  }

  if (local_cpu_usage || remote_cpu_usage) {
    local_cpu_method = format_cpu_method(cpu_method);
    remote_cpu_method = format_cpu_method(xti_udp_stream_results->cpu_method);
    
    switch (verbosity) {
    case 0:
      if (local_cpu_usage) {
	fprintf(where,
		cpu_fmt_0,
		local_service_demand,
		local_cpu_method);
      }
      else {
	fprintf(where,
		cpu_fmt_0,
		remote_service_demand,
		local_cpu_method);
      }
      break;
    case 1:
    case 2:
      if (print_headers) {
	fprintf(where,
		cpu_title,
		format_units(),
		local_cpu_method,
		remote_cpu_method);
      }

      fprintf(where,
	      cpu_fmt_1,		/* the format string */
	      lss_size,		        /* local sendbuf size */
	      send_size,		/* how large were the sends */
	      elapsed_time,		/* how long was the test */
	      messages_sent,
	      failed_sends,
	      local_thruput, 		/* what was the xfer rate */
	      local_cpu_utilization,	/* local cpu */
	      local_service_demand,	/* local service demand */
	      rsr_size,
	      elapsed_time,
	      messages_recvd,
	      remote_thruput,
	      remote_cpu_utilization,	/* remote cpu */
	      remote_service_demand);	/* remote service demand */
      break;
    }
  }
  else {
    /* The tester did not wish to measure service demand. */
    switch (verbosity) {
    case 0:
      fprintf(where,
	      tput_fmt_0,
	      local_thruput);
      break;
    case 1:
    case 2:
      if (print_headers) {
	fprintf(where,tput_title,format_units());
      }
      fprintf(where,
	      tput_fmt_1,		/* the format string */
	      lss_size, 		/* local sendbuf size */
	      send_size,		/* how large were the sends */
	      elapsed_time, 		/* how long did it take */
	      messages_sent,
	      failed_sends,
	      local_thruput,
	      rsr_size, 		/* remote recvbuf size */
	      elapsed_time,
	      messages_recvd,
	      remote_thruput);
      break;
    }
  }

  fflush(where);
#ifdef WANT_HISTOGRAM
  if (verbosity > 1) {
    fprintf(where,"\nHistogram of time spent in send() call\n");
    fflush(where);
    HIST_report(time_hist);
  }
#endif /* WANT_HISTOGRAM */

}


 /* this routine implements the receive side (netserver) of the */
 /* XTI_UDP_STREAM performance test. */

void
recv_xti_udp_stream()
{
  struct ring_elt *recv_ring;

  struct t_bind bind_req, bind_resp;
  struct t_unitdata unitdata;
  int	            flags = 0;

  struct sockaddr_in myaddr_in;
  struct sockaddr_in fromaddr_in;

  SOCKET s_data;
  int 	addrlen;
  unsigned int	bytes_received = 0;
  float	elapsed_time;
  
  unsigned int	message_size;
  unsigned int	messages_recvd = 0;
  
  struct xti_udp_stream_request_struct	*xti_udp_stream_request;
  struct xti_udp_stream_response_struct	*xti_udp_stream_response;
  struct xti_udp_stream_results_struct	*xti_udp_stream_results;
  
  xti_udp_stream_request  = 
    (struct xti_udp_stream_request_struct *)netperf_request.content.test_specific_data;
  xti_udp_stream_response = 
    (struct xti_udp_stream_response_struct *)netperf_response.content.test_specific_data;
  xti_udp_stream_results  = 
    (struct xti_udp_stream_results_struct *)netperf_response.content.test_specific_data;
  
  if (debug) {
    fprintf(where,"netserver: recv_xti_udp_stream: entered...\n");
    fflush(where);
  }
  
  /* We want to set-up the listen socket with all the desired */
  /* parameters and then let the initiator know that all is ready. If */
  /* socket size defaults are to be used, then the initiator will have */
  /* sent us 0's. If the socket sizes cannot be changed, then we will */
  /* send-back what they are. If that information cannot be determined, */
  /* then we send-back -1's for the sizes. If things go wrong for any */
  /* reason, we will drop back ten yards and punt. */
  
  /* If anything goes wrong, we want the remote to know about it. It */
  /* would be best if the error that the remote reports to the user is */
  /* the actual error we encountered, rather than some bogus unexpected */
  /* response type message. */
  
  if (debug > 1) {
    fprintf(where,"recv_xti_udp_stream: setting the response type...\n");
    fflush(where);
  }
  
  netperf_response.content.response_type = XTI_UDP_STREAM_RESPONSE;
  
  if (debug > 2) {
    fprintf(where,"recv_xti_udp_stream: the response type is set...\n");
    fflush(where);
  }
  
  /* We now alter the message_ptr variable to be at the desired */
  /* alignment with the desired offset. */
  
  if (debug > 1) {
    fprintf(where,"recv_xti_udp_stream: requested alignment of %d\n",
	    xti_udp_stream_request->recv_alignment);
    fflush(where);
  }

  if (recv_width == 0) recv_width = 1;

  recv_ring = allocate_buffer_ring(recv_width,
				   xti_udp_stream_request->message_size,
				   xti_udp_stream_request->recv_alignment,
				   xti_udp_stream_request->recv_offset);

  if (debug > 1) {
    fprintf(where,"recv_xti_udp_stream: receive alignment and offset set...\n");
    fflush(where);
  }
  
  /* Let's clear-out our sockaddr for the sake of cleanlines. Then we */
  /* can put in OUR values !-) At some point, we may want to nail this */
  /* socket to a particular network-level address, but for now, */
  /* INADDR_ANY should be just fine. */
  
  bzero((char *)&myaddr_in,
	sizeof(myaddr_in));
  myaddr_in.sin_family      = AF_INET;
  myaddr_in.sin_addr.s_addr = INADDR_ANY;
  myaddr_in.sin_port        = 0;
  
  /* Grab a socket to listen on, and then listen on it. */
  
  if (debug > 1) {
    fprintf(where,"recv_xti_udp_stream: grabbing a socket...\n");
    fflush(where);
  }

  /* create_xti_endpoint expects to find some things in the global */
  /* variables, so set the globals based on the values in the request. */
  /* once the socket has been created, we will set the response values */
  /* based on the updated value of those globals. raj 7/94 */
  lsr_size = xti_udp_stream_request->recv_buf_size;
  loc_rcvavoid = xti_udp_stream_request->so_rcvavoid;
  loc_sndavoid = xti_udp_stream_request->so_sndavoid;

#ifdef __alpha
  
  /* ok - even on a DEC box, strings are strings. I din't really want */
  /* to ntohl the words of a string. since I don't want to teach the */
  /* send_ and recv_ _request and _response routines about the types, */
  /* I will put "anti-ntohl" calls here. I imagine that the "pure" */
  /* solution would be to use XDR, but I am still leary of being able */
  /* to find XDR libs on all platforms I want running netperf. raj */
  {
    int *charword;
    int *initword;
    int *lastword;
    
    initword = (int *) xti_udp_stream_request->xti_device;
    lastword = initword + ((xti_udp_stream_request->dev_name_len + 3) / 4);
    
    for (charword = initword;
	 charword < lastword;
	 charword++) {
      
      *charword = htonl(*charword);
    }
  }
  
#endif /* __alpha */
    
  s_data = create_xti_endpoint(xti_udp_stream_request->xti_device);
  
  if (s_data == INVALID_SOCKET) {
    netperf_response.content.serv_errno = errno;
    send_response();
    exit(1);
  }
  
  /* Let's get an address assigned to this socket so we can tell the */
  /* initiator how to reach the data socket. There may be a desire to */
  /* nail this socket to a specific IP address in a multi-homed, */
  /* multi-connection situation, but for now, we'll ignore the issue */
  /* and concentrate on single connection testing. */

  bind_req.addr.maxlen = sizeof(struct sockaddr_in);
  bind_req.addr.len    = sizeof(struct sockaddr_in);
  bind_req.addr.buf    = (char *)&myaddr_in;
  bind_req.qlen        = 1;

  bind_resp.addr.maxlen = sizeof(struct sockaddr_in);
  bind_resp.addr.len    = sizeof(struct sockaddr_in);
  bind_resp.addr.buf    = (char *)&myaddr_in;
  bind_resp.qlen        = 1;

  if (t_bind(s_data,
	     &bind_req,
	     &bind_resp) == SOCKET_ERROR) {
    netperf_response.content.serv_errno = t_errno;
    send_response();
    
    exit(1);
  }
  
  xti_udp_stream_response->test_length = 
    xti_udp_stream_request->test_length;
  
  /* Now myaddr_in contains the port and the internet address this is */
  /* returned to the sender also implicitly telling the sender that the */
  /* socket buffer sizing has been done. */
  
  xti_udp_stream_response->data_port_number = 
    (int) ntohs(myaddr_in.sin_port);
  netperf_response.content.serv_errno   = 0;
  
  /* But wait, there's more. If the initiator wanted cpu measurements, */
  /* then we must call the calibrate routine, which will return the max */
  /* rate back to the initiator. If the CPU was not to be measured, or */
  /* something went wrong with the calibration, we will return a -1 to */
  /* the initiator. */
  
  xti_udp_stream_response->cpu_rate    = 0.0; /* assume no cpu */
  xti_udp_stream_response->measure_cpu = 0;
  if (xti_udp_stream_request->measure_cpu) {
    /* We will pass the rate into the calibration routine. If the */
    /* user did not specify one, it will be 0.0, and we will do a */
    /* "real" calibration. Otherwise, all it will really do is */
    /* store it away... */
    xti_udp_stream_response->measure_cpu = 1;
    xti_udp_stream_response->cpu_rate = 
      calibrate_local_cpu(xti_udp_stream_request->cpu_rate);
  }
  
  message_size	= xti_udp_stream_request->message_size;
  test_time	= xti_udp_stream_request->test_length;
  
  /* before we send the response back to the initiator, pull some of */
  /* the socket parms from the globals */
  xti_udp_stream_response->send_buf_size = lss_size;
  xti_udp_stream_response->recv_buf_size = lsr_size;
  xti_udp_stream_response->so_rcvavoid = loc_rcvavoid;
  xti_udp_stream_response->so_sndavoid = loc_sndavoid;

  /* since we are going to call t_rcvudata() instead of t_rcv() we */
  /* need to init the unitdata structure raj 3/95 */

  unitdata.addr.maxlen = sizeof(fromaddr_in);
  unitdata.addr.len    = sizeof(fromaddr_in);
  unitdata.addr.buf    = (char *)&fromaddr_in;

  unitdata.opt.maxlen = 0;
  unitdata.opt.len    = 0;
  unitdata.opt.buf    = NULL;

  unitdata.udata.maxlen = xti_udp_stream_request->message_size;
  unitdata.udata.len    = xti_udp_stream_request->message_size;
  unitdata.udata.buf    = recv_ring->buffer_ptr;

  send_response();
  
  /* Now it's time to start receiving data on the connection. We will */
  /* first grab the apropriate counters and then start grabbing. */
  
  cpu_start(xti_udp_stream_request->measure_cpu);
  
  /* The loop will exit when the timer pops, or if we happen to recv a */
  /* message of less than send_size bytes... */
  
  times_up = 0;
  start_timer(test_time + PAD_TIME);
  
  if (debug) {
    fprintf(where,"recv_xti_udp_stream: about to enter inner sanctum.\n");
    fflush(where);
  }
  
  while (!times_up) {
#ifdef RAJ_DEBUG
    if (debug) {
      fprintf(where,"t_rcvudata, errno %d, t_errno %d",
	      errno,
	      t_errno);
      fprintf(where," after %d messages\n",messages_recvd);
      fprintf(where,"addrmax %d addrlen %d addrbuf %x\n",
	      unitdata.addr.maxlen,
	      unitdata.addr.len,
	      unitdata.addr.buf);
      fprintf(where,"optmax %d optlen %d optbuf %x\n",
	      unitdata.opt.maxlen,
	      unitdata.opt.len,
	      unitdata.opt.buf);
      fprintf(where,"udatamax %d udatalen %d udatabuf %x\n",
	      unitdata.udata.maxlen,
	      unitdata.udata.len,
	      unitdata.udata.buf);
      fflush(where);
    }
#endif /* RAJ_DEBUG */
    if (t_rcvudata(s_data, 
		   &unitdata,
		   &flags) != 0) {
      if (errno == TNODATA) {
	continue;
      }
      if (errno != EINTR) {
	netperf_response.content.serv_errno = t_errno;
	send_response();
	exit(1);
      }
      break;
    }
    messages_recvd++;
    recv_ring = recv_ring->next;
    unitdata.udata.buf = recv_ring->buffer_ptr;
  }
  
  if (debug) {
    fprintf(where,"recv_xti_udp_stream: got %d messages.\n",messages_recvd);
    fflush(where);
  }
  
  
  /* The loop now exits due timer or < send_size bytes received. */
  
  cpu_stop(xti_udp_stream_request->measure_cpu,&elapsed_time);
  
  if (times_up) {
    /* we ended on a timer, subtract the PAD_TIME */
    elapsed_time -= (float)PAD_TIME;
  }
  else {
    stop_timer();
  }
  
  if (debug) {
    fprintf(where,"recv_xti_udp_stream: test ended in %f seconds.\n",elapsed_time);
    fflush(where);
  }
  
  bytes_received = (messages_recvd * message_size);
  
  /* send the results to the sender			*/
  
  if (debug) {
    fprintf(where,
	    "recv_xti_udp_stream: got %d bytes\n",
	    bytes_received);
    fflush(where);
  }
  
  netperf_response.content.response_type	= XTI_UDP_STREAM_RESULTS;
  xti_udp_stream_results->bytes_received	= bytes_received;
  xti_udp_stream_results->messages_recvd	= messages_recvd;
  xti_udp_stream_results->elapsed_time	= elapsed_time;
  xti_udp_stream_results->cpu_method        = cpu_method;
  if (xti_udp_stream_request->measure_cpu) {
    xti_udp_stream_results->cpu_util	= calc_cpu_util(elapsed_time);
  }
  else {
    xti_udp_stream_results->cpu_util	= -1.0;
  }
  
  if (debug > 1) {
    fprintf(where,
	    "recv_xti_udp_stream: test complete, sending results.\n");
    fflush(where);
  }
  
  send_response();
  
}

void send_xti_udp_rr(char remote_host[])
{
  
  char *tput_title = "\
Local /Remote\n\
Socket Size   Request  Resp.   Elapsed  Trans.\n\
Send   Recv   Size     Size    Time     Rate         \n\
bytes  Bytes  bytes    bytes   secs.    per sec   \n\n";
  
  char *tput_fmt_0 =
    "%7.2f\n";
  
  char *tput_fmt_1_line_1 = "\
%-6d %-6d %-6d   %-6d  %-6.2f   %7.2f   \n";
  char *tput_fmt_1_line_2 = "\
%-6d %-6d\n";
  
  char *cpu_title = "\
Local /Remote\n\
Socket Size   Request Resp.  Elapsed Trans.   CPU    CPU    S.dem   S.dem\n\
Send   Recv   Size    Size   Time    Rate     local  remote local   remote\n\
bytes  bytes  bytes   bytes  secs.   per sec  %% %c    %% %c    us/Tr   us/Tr\n\n";
  
  char *cpu_fmt_0 =
    "%6.3f %c\n";
  
  char *cpu_fmt_1_line_1 = "\
%-6d %-6d %-6d  %-6d %-6.2f  %-6.2f   %-6.2f %-6.2f %-6.3f  %-6.3f\n";
  
  char *cpu_fmt_1_line_2 = "\
%-6d %-6d\n";
  
  char *ksink_fmt = "\
Alignment      Offset\n\
Local  Remote  Local  Remote\n\
Send   Recv    Send   Recv\n\
%5d  %5d   %5d  %5d\n";
  
  
  float			elapsed_time;
  
  struct ring_elt *send_ring;
  struct ring_elt *recv_ring;

  struct t_bind bind_req, bind_resp;
  struct t_unitdata unitdata;
  struct t_unitdata send_unitdata;
  struct t_unitdata recv_unitdata;
  int	            flags = 0;

  int	len;
  int	nummessages;
  SOCKET send_socket;
  int	trans_remaining;
  int	bytes_xferd;
  
  int	rsp_bytes_recvd;
  
  float	local_cpu_utilization;
  float	local_service_demand;
  float	remote_cpu_utilization;
  float	remote_service_demand;
  double thruput;
  
  struct	hostent	        *hp;
  struct	sockaddr_in	server, myaddr_in;
  unsigned      int             addr;
  int	                        addrlen;
  
  struct	xti_udp_rr_request_struct	*xti_udp_rr_request;
  struct	xti_udp_rr_response_struct	*xti_udp_rr_response;
  struct	xti_udp_rr_results_struct	*xti_udp_rr_result;

#ifdef WANT_INTERVALS
  int	interval_count;
  sigset_t signal_set;
#endif /* WANT_INTERVALS */
  
  xti_udp_rr_request  =
    (struct xti_udp_rr_request_struct *)netperf_request.content.test_specific_data;
  xti_udp_rr_response =
    (struct xti_udp_rr_response_struct *)netperf_response.content.test_specific_data;
  xti_udp_rr_result	 =
    (struct xti_udp_rr_results_struct *)netperf_response.content.test_specific_data;
  
#ifdef WANT_HISTOGRAM
  time_hist = HIST_new();
#endif
  
  /* since we are now disconnected from the code that established the */
  /* control socket, and since we want to be able to use different */
  /* protocols and such, we are passed the name of the remote host and */
  /* must turn that into the test specific addressing information. */
  
  bzero((char *)&server,
	sizeof(server));
  
  /* it would seem that while HP-UX will allow an IP address (as a */
  /* string) in a call to gethostbyname, other, less enlightened */
  /* systems do not. fix from awjacks@ca.sandia.gov raj 10/95 */  
  /* order changed to check for IP address first. raj 7/96 */

  if ((addr = inet_addr(remote_host)) == SOCKET_ERROR) {
    /* it was not an IP address, try it as a name */
    if ((hp = gethostbyname(remote_host)) == NULL) {
      /* we have no idea what it is */
      fprintf(where,
	      "establish_control: could not resolve the destination %s\n",
	      remote_host);
      fflush(where);
      exit(1);
    }
    else {
      /* it was a valid remote_host */
      bcopy(hp->h_addr,
	    (char *)&server.sin_addr,
	    hp->h_length);
      server.sin_family = hp->h_addrtype;
    }
  }
  else {
    /* it was a valid IP address */
    server.sin_addr.s_addr = addr;
    server.sin_family = AF_INET;
  }    
  
  if ( print_headers ) {
    fprintf(where,"XTI UDP REQUEST/RESPONSE TEST");
        fprintf(where," to %s", remote_host);
    if (iteration_max > 1) {
      fprintf(where,
	      " : +/-%3.1f%% @ %2d%% conf.",
	      interval/0.02,
	      confidence_level);
      }
    if (loc_sndavoid || 
	loc_rcvavoid ||
	rem_sndavoid ||
	rem_rcvavoid) {
      fprintf(where," : copy avoidance");
    }
#ifdef WANT_HISTOGRAM
    fprintf(where," : histogram");
#endif /* WANT_HISTOGRAM */
#ifdef WANT_INTERVALS
    fprintf(where," : interval");
#endif /* WANT_INTERVALS */
#ifdef DIRTY 
    fprintf(where," : dirty data");
#endif /* DIRTY */
    fprintf(where,"\n");
  }
  
  /* initialize a few counters */
  
  send_ring     = NULL;
  recv_ring     = NULL;
  nummessages	= 0;
  bytes_xferd	= 0;
  times_up 	= 0;
  confidence_iteration = 1;
  init_stat();


  /* we have a great-big while loop which controls the number of times */
  /* we run a particular test. this is for the calculation of a */
  /* confidence interval (I really should have stayed awake during */
  /* probstats :). If the user did not request confidence measurement */
  /* (no confidence is the default) then we will only go though the */
  /* loop once. the confidence stuff originates from the folks at IBM */

  while (((confidence < 0) && (confidence_iteration < iteration_max)) ||
	 (confidence_iteration <= iteration_min)) {
    
    nummessages     = 0;
    bytes_xferd     = 0.0;
    times_up        = 0;
    trans_remaining = 0;
    
    /* set-up the data buffers with the requested alignment and offset */
    
    if (send_width == 0) send_width = 1;
    if (recv_width == 0) recv_width = 1;
    
    if (send_ring == NULL) {
      send_ring = allocate_buffer_ring(send_width,
				       req_size,
				       local_send_align,
				       local_send_offset);
    }
    
    if (recv_ring == NULL) {
      recv_ring = allocate_buffer_ring(recv_width,
				       rsp_size,
				       local_recv_align,
				       local_recv_offset);
    }
    
  /* since we are going to call t_rcvudata() instead of t_rcv() we */
  /* need to init the unitdata structure raj 8/95 */

    memset (&recv_unitdata, 0, sizeof(recv_unitdata));
    recv_unitdata.addr.maxlen = sizeof(struct sockaddr_in);
    recv_unitdata.addr.len    = sizeof(struct sockaddr_in);
    recv_unitdata.addr.buf    = (char *)&server;
    
    recv_unitdata.opt.maxlen = 0;
    recv_unitdata.opt.len    = 0;
    recv_unitdata.opt.buf    = NULL;
    
    recv_unitdata.udata.maxlen = rsp_size;
    recv_unitdata.udata.len    = rsp_size;
    recv_unitdata.udata.buf    = recv_ring->buffer_ptr;
    
    /* since we are going to call t_sndudata() instead of t_snd() we */
    /* need to init the unitdata structure raj 8/95 */
    
    memset (&send_unitdata, 0, sizeof(send_unitdata));
    send_unitdata.addr.maxlen = sizeof(struct sockaddr_in);
    send_unitdata.addr.len    = sizeof(struct sockaddr_in);
    send_unitdata.addr.buf    = (char *)&server;
    
    send_unitdata.opt.maxlen = 0;
    send_unitdata.opt.len    = 0;
    send_unitdata.opt.buf    = NULL;
    
    send_unitdata.udata.maxlen = req_size;
    send_unitdata.udata.len    = req_size;
    send_unitdata.udata.buf    = send_ring->buffer_ptr;

    /*set up the data socket                        */
    send_socket = create_xti_endpoint(loc_xti_device);
    
    if (send_socket == INVALID_SOCKET){
      perror("netperf: send_xti_udp_rr: udp rr data socket");
      exit(1);
    }
    
    if (debug) {
      fprintf(where,"send_xti_udp_rr: send_socket obtained...\n");
    }
    
    /* it would seem that with XTI, there is no implicit bind  */
    /* so we have to make a call to t_bind. this is not */
    /* terribly convenient, but I suppose that "standard is better */
    /* than better" :) raj 2/95 */

    if (t_bind(send_socket, NULL, NULL) == SOCKET_ERROR) {
      t_error("send_xti_tcp_stream: t_bind");
      exit(1);
    }

    /* If the user has requested cpu utilization measurements, we must */
    /* calibrate the cpu(s). We will perform this task within the tests */
    /* themselves. If the user has specified the cpu rate, then */
    /* calibrate_local_cpu will return rather quickly as it will have */
    /* nothing to do. If local_cpu_rate is zero, then we will go through */
    /* all the "normal" calibration stuff and return the rate back. If */
    /* there is no idle counter in the kernel idle loop, the */
    /* local_cpu_rate will be set to -1. */
    
    if (local_cpu_usage) {
      local_cpu_rate = calibrate_local_cpu(local_cpu_rate);
    }
    
    /* Tell the remote end to do a listen. The server alters the socket */
    /* paramters on the other side at this point, hence the reason for */
    /* all the values being passed in the setup message. If the user did */
    /* not specify any of the parameters, they will be passed as 0, which */
    /* will indicate to the remote that no changes beyond the system's */
    /* default should be used. Alignment is the exception, it will */
    /* default to 8, which will be no alignment alterations. */
    
    netperf_request.content.request_type	= DO_XTI_UDP_RR;
    xti_udp_rr_request->recv_buf_size	= rsr_size;
    xti_udp_rr_request->send_buf_size	= rss_size;
    xti_udp_rr_request->recv_alignment  = remote_recv_align;
    xti_udp_rr_request->recv_offset	= remote_recv_offset;
    xti_udp_rr_request->send_alignment  = remote_send_align;
    xti_udp_rr_request->send_offset	= remote_send_offset;
    xti_udp_rr_request->request_size	= req_size;
    xti_udp_rr_request->response_size	= rsp_size;
    xti_udp_rr_request->measure_cpu	= remote_cpu_usage;
    xti_udp_rr_request->cpu_rate	= remote_cpu_rate;
    xti_udp_rr_request->so_rcvavoid	= rem_rcvavoid;
    xti_udp_rr_request->so_sndavoid	= rem_sndavoid;
    if (test_time) {
      xti_udp_rr_request->test_length	= test_time;
    }
    else {
      xti_udp_rr_request->test_length	= test_trans * -1;
    }
    
    strcpy(xti_udp_rr_request->xti_device, rem_xti_device);
  
#ifdef __alpha
  
    /* ok - even on a DEC box, strings are strings. I didn't really want */
    /* to ntohl the words of a string. since I don't want to teach the */
    /* send_ and recv_ _request and _response routines about the types, */
    /* I will put "anti-ntohl" calls here. I imagine that the "pure" */
    /* solution would be to use XDR, but I am still leary of being able */
    /* to find XDR libs on all platforms I want running netperf. raj */
    {
      int *charword;
      int *initword;
      int *lastword;
      
      initword = (int *) xti_udp_rr_request->xti_device;
      lastword = initword + ((strlen(rem_xti_device) + 3) / 4);
      
      for (charword = initword;
	   charword < lastword;
	   charword++) {
	
	*charword = ntohl(*charword);
      }
    }
#endif /* __alpha */

    if (debug > 1) {
      fprintf(where,"netperf: send_xti_udp_rr: requesting UDP r/r test\n");
    }
    
    send_request();
    
    /* The response from the remote will contain all of the relevant 	*/
    /* socket parameters for this test type. We will put them back into */
    /* the variables here so they can be displayed if desired.  The	*/
    /* remote will have calibrated CPU if necessary, and will have done	*/
    /* all the needed set-up we will have calibrated the cpu locally	*/
    /* before sending the request, and will grab the counter value right*/
    /* after the connect returns. The remote will grab the counter right*/
    /* after the accept call. This saves the hassle of extra messages	*/
    /* being sent for the UDP tests.					*/
    
    recv_response();
    
    if (!netperf_response.content.serv_errno) {
      if (debug)
	fprintf(where,"remote listen done.\n");
      rsr_size	       =	xti_udp_rr_response->recv_buf_size;
      rss_size	       =	xti_udp_rr_response->send_buf_size;
      remote_cpu_usage =	xti_udp_rr_response->measure_cpu;
      remote_cpu_rate  = 	xti_udp_rr_response->cpu_rate;
      /* port numbers in proper order */
      server.sin_port  =	(short)xti_udp_rr_response->data_port_number;
      server.sin_port  = 	htons(server.sin_port);
    }
    else {
      Set_errno(netperf_response.content.serv_errno);
      perror("netperf: remote error");
      
      exit(1);
    }
    
    /* Data Socket set-up is finished. If there were problems, either the */
    /* connect would have failed, or the previous response would have */
    /* indicated a problem. I failed to see the value of the extra */
    /* message after the accept on the remote. If it failed, we'll see it */
    /* here. If it didn't, we might as well start pumping data. */
    
    /* Set-up the test end conditions. For a request/response test, they */
    /* can be either time or transaction based. */
    
    if (test_time) {
      /* The user wanted to end the test after a period of time. */
      times_up = 0;
      trans_remaining = 0;
      start_timer(test_time);
    }
    else {
      /* The tester wanted to send a number of bytes. */
      trans_remaining = test_bytes;
      times_up = 1;
    }
    
    /* The cpu_start routine will grab the current time and possibly */
    /* value of the idle counter for later use in measuring cpu */
    /* utilization and/or service demand and thruput. */
    
    cpu_start(local_cpu_usage);

#ifdef WANT_INTERVALS
    if ((interval_burst) || (demo_mode)) {
      /* zero means that we never pause, so we never should need the */
      /* interval timer, unless we are in demo_mode */
      start_itimer(interval_wate);
    }
    interval_count = interval_burst;
    /* get the signal set for the call to sigsuspend */
    if (sigprocmask(SIG_BLOCK, (sigset_t *)NULL, &signal_set) != 0) {
      fprintf(where,
	      "send_xti_udp_rr: unable to get sigmask errno %d\n",
	      errno);
      fflush(where);
      exit(1);
    }
#endif /* WANT_INTERVALS */
    
    /* We use an "OR" to control test execution. When the test is */
    /* controlled by time, the byte count check will always return */
    /* false. When the test is controlled by byte count, the time test */
    /* will always return false. When the test is finished, the whole */
    /* expression will go false and we will stop sending data. I think */
    /* I just arbitrarily decrement trans_remaining for the timed */
    /* test, but will not do that just yet... One other question is */
    /* whether or not the send buffer and the receive buffer should be */
    /* the same buffer. */

    while ((!times_up) || (trans_remaining > 0)) {
      /* send the request */
#ifdef WANT_HISTOGRAM
      HIST_timestamp(&time_one);
#endif
      if((t_sndudata(send_socket,
		     &send_unitdata)) != 0) {
	if (errno == EINTR) {
	  /* We likely hit */
	  /* test-end time. */
	  break;
	}
        fprintf(where,
		"send_xti_udp_rr: t_sndudata: errno %d t_errno %d t_look 0x%.4x\n",
		errno,
		t_errno,
		t_look(send_socket));
	fflush(where);
	exit(1);
      }
      send_ring = send_ring->next;
      
      /* receive the response. with UDP we will get it all, or nothing */
      
      if((t_rcvudata(send_socket,
		     &recv_unitdata,
		     &flags)) != 0) {
	if (errno == TNODATA) {
	  continue;
	}
	if (errno == EINTR) {
	  /* Again, we have likely hit test-end time */
	  break;
	}
	fprintf(where,
		"send_xti_udp_rr: t_rcvudata: errno %d t_errno %d t_look 0x%x\n",
		errno,
		t_errno,
		t_look(send_socket));
	fprintf(where,
		"recv_unitdata.udata.buf %x\n",recv_unitdata.udata.buf);
	fprintf(where,
		"recv_unitdata.udata.maxlen %x\n",recv_unitdata.udata.maxlen);
	fprintf(where,
		"recv_unitdata.udata.len %x\n",recv_unitdata.udata.len);
	fprintf(where,
		"recv_unitdata.addr.buf %x\n",recv_unitdata.addr.buf);
	fprintf(where,
		"recv_unitdata.addr.maxlen %x\n",recv_unitdata.addr.maxlen);
	fprintf(where,
		"recv_unitdata.addr.len %x\n",recv_unitdata.addr.len);
	fflush(where);
	exit(1);
      }
      recv_ring = recv_ring->next;
      
#ifdef WANT_HISTOGRAM
      HIST_timestamp(&time_two);
      HIST_add(time_hist,delta_micro(&time_one,&time_two));
      
      /* at this point, we may wish to sleep for some period of */
      /* time, so we see how long that last transaction just took, */
      /* and sleep for the difference of that and the interval. We */
      /* will not sleep if the time would be less than a */
      /* millisecond.  */
#endif
#ifdef WANT_INTERVALS      
      if (demo_mode) {
	units_this_tick += 1;
      }
      /* in this case, the interval count is the count-down couter */
      /* to decide to sleep for a little bit */
      if ((interval_burst) && (--interval_count == 0)) {
	/* call sigsuspend and wait for the interval timer to get us */
	/* out */
	if (debug) {
	  fprintf(where,"about to suspend\n");
	  fflush(where);
	}
	if (sigsuspend(&signal_set) == EFAULT) {
	  fprintf(where,
		  "send_xti_udp_rr: fault with signal set!\n");
	  fflush(where);
	  exit(1);
	}
	interval_count = interval_burst;
      }
#endif /* WANT_INTERVALS */
      
      nummessages++;          
      if (trans_remaining) {
	trans_remaining--;
      }
      
      if (debug > 3) {
	if ((nummessages % 100) == 0) {
	  fprintf(where,"Transaction %d completed\n",nummessages);
	  fflush(where);
	}
      }
      
    }
    
    /* this call will always give us the elapsed time for the test, and */
    /* will also store-away the necessaries for cpu utilization */
    
    cpu_stop(local_cpu_usage,&elapsed_time);	/* was cpu being */
						/* measured? how long */
						/* did we really run? */
    
    /* Get the statistics from the remote end. The remote will have */
    /* calculated service demand and all those interesting things. If */
    /* it wasn't supposed to care, it will return obvious values. */
    
    recv_response();
    if (!netperf_response.content.serv_errno) {
      if (debug)
	fprintf(where,"remote results obtained\n");
    }
    else {
      Set_errno(netperf_response.content.serv_errno);
      perror("netperf: remote error");
      
      exit(1);
    }
    
    /* We now calculate what our thruput was for the test. In the */
    /* future, we may want to include a calculation of the thruput */
    /* measured by the remote, but it should be the case that for a */
    /* UDP rr test, that the two numbers should be *very* close... */
    /* We calculate bytes_sent regardless of the way the test length */
    /* was controlled.  */
    
    bytes_xferd	= (req_size * nummessages) + (rsp_size * nummessages);
    thruput	= nummessages / elapsed_time;
    
    if (local_cpu_usage || remote_cpu_usage) {

      /* We must now do a little math for service demand and cpu */
      /* utilization for the system(s) Of course, some of the */
      /* information might be bogus because there was no idle counter */
      /* in the kernel(s). We need to make a note of this for the */
      /* user's benefit by placing a code for the metod used in the */
      /* test banner */

      if (local_cpu_usage) {
	local_cpu_utilization = calc_cpu_util(0.0);
	
	/* since calc_service demand is doing ms/Kunit we will */
	/* multiply the number of transaction by 1024 to get */
	/* "good" numbers */
	
	local_service_demand  = calc_service_demand((double) nummessages*1024,
						    0.0,
						    0.0,
						    0);
      }
      else {
	local_cpu_utilization	= -1.0;
	local_service_demand	= -1.0;
      }
      
      if (remote_cpu_usage) {
	remote_cpu_utilization = xti_udp_rr_result->cpu_util;
	
	/* since calc_service demand is doing ms/Kunit we will */
	/* multiply the number of transaction by 1024 to get */
	/* "good" numbers */
	
	remote_service_demand  = calc_service_demand((double) nummessages*1024,
						     0.0,
						     remote_cpu_utilization,
						     xti_udp_rr_result->num_cpus);
      }
      else {
	remote_cpu_utilization = -1.0;
	remote_service_demand  = -1.0;
      }
    }
    else {
      /* we were not measuring cpu, for the confidence stuff, we */
      /* should make it -1.0 */
      local_cpu_utilization	= -1.0;
      local_service_demand	= -1.0;
      remote_cpu_utilization = -1.0;
      remote_service_demand  = -1.0;
    }
    
    /* at this point, we want to calculate the confidence information. */
    /* if debugging is on, calculate_confidence will print-out the */
    /* parameters we pass it */
    
    calculate_confidence(confidence_iteration,
			 elapsed_time,
			 thruput,
			 local_cpu_utilization,
			 remote_cpu_utilization,
			 local_service_demand,
			 remote_service_demand);
    
    
    confidence_iteration++;
    
    /* we are done with the socket */
    t_close(send_socket);
  }

  /* at this point, we have made all the iterations we are going to */
  /* make. */
  retrieve_confident_values(&elapsed_time,
			    &thruput,
			    &local_cpu_utilization,
			    &remote_cpu_utilization,
			    &local_service_demand,
			    &remote_service_demand);
  
  /* We are now ready to print all the information. If the user */
  /* has specified zero-level verbosity, we will just print the */
  /* local service demand, or the remote service demand. If the */
  /* user has requested verbosity level 1, he will get the basic */
  /* "streamperf" numbers. If the user has specified a verbosity */
  /* of greater than 1, we will display a veritable plethora of */
  /* background information from outside of this block as it it */
  /* not cpu_measurement specific...  */
  
  if (confidence < 0) {
    /* we did not hit confidence, but were we asked to look for it? */
    if (iteration_max > 1) {
      display_confidence();
    }
  }
  
  if (local_cpu_usage || remote_cpu_usage) {
    local_cpu_method = format_cpu_method(cpu_method);
    remote_cpu_method = format_cpu_method(xti_udp_rr_result->cpu_method);
    
    switch (verbosity) {
    case 0:
      if (local_cpu_usage) {
	fprintf(where,
		cpu_fmt_0,
		local_service_demand,
		local_cpu_method);
      }
      else {
	fprintf(where,
		cpu_fmt_0,
		remote_service_demand,
		remote_cpu_method);
      }
      break;
    case 1:
    case 2:
      if (print_headers) {
	fprintf(where,
		cpu_title,
		local_cpu_method,
		remote_cpu_method);
      }
    
      fprintf(where,
	      cpu_fmt_1_line_1,		/* the format string */
	      lss_size,		/* local sendbuf size */
	      lsr_size,
	      req_size,		/* how large were the requests */
	      rsp_size,		/* guess */
	      elapsed_time,		/* how long was the test */
	      nummessages/elapsed_time,
	      local_cpu_utilization,	/* local cpu */
	      remote_cpu_utilization,	/* remote cpu */
	      local_service_demand,	/* local service demand */
	      remote_service_demand);	/* remote service demand */
      fprintf(where,
	      cpu_fmt_1_line_2,
	      rss_size,
	      rsr_size);
      break;
    }
  }
  else {
    /* The tester did not wish to measure service demand. */
    switch (verbosity) {
    case 0:
      fprintf(where,
	      tput_fmt_0,
	      nummessages/elapsed_time);
      break;
    case 1:
    case 2:
      if (print_headers) {
	fprintf(where,tput_title,format_units());
      }
    
      fprintf(where,
	      tput_fmt_1_line_1,	/* the format string */
	      lss_size,
	      lsr_size,
	      req_size,		/* how large were the requests */
	      rsp_size,		/* how large were the responses */
	      elapsed_time, 		/* how long did it take */
	      nummessages/elapsed_time);
      fprintf(where,
	      tput_fmt_1_line_2,
	      rss_size, 		/* remote recvbuf size */
	      rsr_size);
      
      break;
    }
  }
  fflush(where);

  /* it would be a good thing to include information about some of the */
  /* other parameters that may have been set for this test, but at the */
  /* moment, I do not wish to figure-out all the  formatting, so I will */
  /* just put this comment here to help remind me that it is something */
  /* that should be done at a later time. */
  
  /* how to handle the verbose information in the presence of */
  /* confidence intervals is yet to be determined... raj 11/94 */

  if (verbosity > 1) {
    /* The user wanted to know it all, so we will give it to him. */
    /* This information will include as much as we can find about */
    /* UDP statistics, the alignments of the sends and receives */
    /* and all that sort of rot... */
    
#ifdef WANT_HISTOGRAM
    fprintf(where,"\nHistogram of request/reponse times.\n");
    fflush(where);
    HIST_report(time_hist);
#endif /* WANT_HISTOGRAM */
  }
}

 /* this routine implements the receive side (netserver) of a XTI_UDP_RR */
 /* test. */
void 
  recv_xti_udp_rr()
{
  
  struct ring_elt *recv_ring;
  struct ring_elt *send_ring;

  struct t_bind bind_req, bind_resp;
  struct t_unitdata send_unitdata;
  struct t_unitdata recv_unitdata;
  int	            flags = 0;

  struct sockaddr_in myaddr_in, peeraddr_in;
  SOCKET s_data;
  int 	addrlen;
  int	trans_received;
  int	trans_remaining;
  float	elapsed_time;
  
  struct	xti_udp_rr_request_struct	*xti_udp_rr_request;
  struct	xti_udp_rr_response_struct	*xti_udp_rr_response;
  struct	xti_udp_rr_results_struct	*xti_udp_rr_results;
  
  
  /* a little variable initialization */
  memset (&myaddr_in, 0, sizeof(struct sockaddr_in));
  myaddr_in.sin_family      = AF_INET;
  myaddr_in.sin_addr.s_addr = INADDR_ANY;
  myaddr_in.sin_port        = 0;
  memset (&peeraddr_in, 0, sizeof(struct sockaddr_in));

  /* and some not so paranoid :) */
  xti_udp_rr_request  = 
    (struct xti_udp_rr_request_struct *)netperf_request.content.test_specific_data;
  xti_udp_rr_response = 
    (struct xti_udp_rr_response_struct *)netperf_response.content.test_specific_data;
  xti_udp_rr_results  = 
    (struct xti_udp_rr_results_struct *)netperf_response.content.test_specific_data;
  
  if (debug) {
    fprintf(where,"netserver: recv_xti_udp_rr: entered...\n");
    fflush(where);
  }
  
  /* We want to set-up the listen socket with all the desired */
  /* parameters and then let the initiator know that all is ready. If */
  /* socket size defaults are to be used, then the initiator will have */
  /* sent us 0's. If the socket sizes cannot be changed, then we will */
  /* send-back what they are. If that information cannot be determined, */
  /* then we send-back -1's for the sizes. If things go wrong for any */
  /* reason, we will drop back ten yards and punt. */
  
  /* If anything goes wrong, we want the remote to know about it. It */
  /* would be best if the error that the remote reports to the user is */
  /* the actual error we encountered, rather than some bogus unexpected */
  /* response type message. */
  
  if (debug) {
    fprintf(where,"recv_xti_udp_rr: setting the response type...\n");
    fflush(where);
  }
  
  netperf_response.content.response_type = XTI_UDP_RR_RESPONSE;
  
  if (debug) {
    fprintf(where,"recv_xti_udp_rr: the response type is set...\n");
    fflush(where);
  }
  
  /* We now alter the message_ptr variables to be at the desired */
  /* alignments with the desired offsets. */
  
  if (debug) {
    fprintf(where,"recv_xti_udp_rr: requested recv alignment of %d offset %d\n",
	    xti_udp_rr_request->recv_alignment,
	    xti_udp_rr_request->recv_offset);
    fprintf(where,"recv_xti_udp_rr: requested send alignment of %d offset %d\n",
	    xti_udp_rr_request->send_alignment,
	    xti_udp_rr_request->send_offset);
    fflush(where);
  }

  if (send_width == 0) send_width = 1;
  if (recv_width == 0) recv_width = 1;

  recv_ring = allocate_buffer_ring(recv_width,
				   xti_udp_rr_request->request_size,
				   xti_udp_rr_request->recv_alignment,
				   xti_udp_rr_request->recv_offset);

  send_ring = allocate_buffer_ring(send_width,
				   xti_udp_rr_request->response_size,
				   xti_udp_rr_request->send_alignment,
				   xti_udp_rr_request->send_offset);

  if (debug) {
    fprintf(where,"recv_xti_udp_rr: receive alignment and offset set...\n");
    fflush(where);
  }
  
  /* create_xti_endpoint expects to find some things in the global */
  /* variables, so set the globals based on the values in the request. */
  /* once the socket has been created, we will set the response values */
  /* based on the updated value of those globals. raj 7/94 */
  lss_size = xti_udp_rr_request->send_buf_size;
  lsr_size = xti_udp_rr_request->recv_buf_size;
  loc_rcvavoid = xti_udp_rr_request->so_rcvavoid;
  loc_sndavoid = xti_udp_rr_request->so_sndavoid;

#ifdef __alpha
  
  /* ok - even on a DEC box, strings are strings. I din't really want */
  /* to ntohl the words of a string. since I don't want to teach the */
  /* send_ and recv_ _request and _response routines about the types, */
  /* I will put "anti-ntohl" calls here. I imagine that the "pure" */
  /* solution would be to use XDR, but I am still leary of being able */
  /* to find XDR libs on all platforms I want running netperf. raj */
  {
    int *charword;
    int *initword;
    int *lastword;
    
    initword = (int *) xti_udp_rr_request->xti_device;
    lastword = initword + ((xti_udp_rr_request->dev_name_len + 3) / 4);
    
    for (charword = initword;
	 charword < lastword;
	 charword++) {
      
      *charword = htonl(*charword);
    }
  }
  
#endif /* __alpha */
    
  s_data = create_xti_endpoint(xti_udp_rr_request->xti_device);
  
  if (s_data == INVALID_SOCKET) {
    netperf_response.content.serv_errno = errno;
    send_response();
    exit(1);
  }

  if (debug) {
    fprintf(where,"recv_xti_udp_rr: endpoint created...\n");
    fflush(where);
  }
  
  /* Let's get an address assigned to this socket so we can tell the */
  /* initiator how to reach the data socket. There may be a desire to */
  /* nail this socket to a specific IP address in a multi-homed, */
  /* multi-connection situation, but for now, we'll ignore the issue */
  /* and concentrate on single connection testing. */

  bind_req.addr.maxlen = sizeof(struct sockaddr_in);
  bind_req.addr.len    = sizeof(struct sockaddr_in);
  bind_req.addr.buf    = (char *)&myaddr_in;
  bind_req.qlen        = 1;

  bind_resp.addr.maxlen = sizeof(struct sockaddr_in);
  bind_resp.addr.len    = sizeof(struct sockaddr_in);
  bind_resp.addr.buf    = (char *)&myaddr_in;
  bind_resp.qlen        = 1;

  if (t_bind(s_data,
	     &bind_req,
	     &bind_resp) == SOCKET_ERROR) {
    if (debug) {
      fprintf(where,
	      "recv_xti_udp_rr: t_bind failed, t_errno %d errno %d\n",
	      t_errno,
	      errno);
      fflush(where);
    }

    netperf_response.content.serv_errno = t_errno;
    send_response();
    
    exit(1);
  }

  if (debug) {
    fprintf(where,
	    "recv_xti_udp_rr: endpoint bound to port %d...\n",
	    ntohs(myaddr_in.sin_port));
    fflush(where);
  }

  xti_udp_rr_response->test_length = 
    xti_udp_rr_request->test_length;

  
  /* Now myaddr_in contains the port and the internet address this is */
  /* returned to the sender also implicitly telling the sender that the */
  /* socket buffer sizing has been done. */
  
  xti_udp_rr_response->data_port_number = (int) ntohs(myaddr_in.sin_port);
  netperf_response.content.serv_errno   = 0;
  
  fprintf(where,"recv port number %d\n",myaddr_in.sin_port);
  fflush(where);
  
  /* But wait, there's more. If the initiator wanted cpu measurements, */
  /* then we must call the calibrate routine, which will return the max */
  /* rate back to the initiator. If the CPU was not to be measured, or */
  /* something went wrong with the calibration, we will return a 0.0 to */
  /* the initiator. */
  
  xti_udp_rr_response->cpu_rate    = 0.0; 	/* assume no cpu */
  xti_udp_rr_response->measure_cpu = 0;
  if (xti_udp_rr_request->measure_cpu) {
    xti_udp_rr_response->measure_cpu = 1;
    xti_udp_rr_response->cpu_rate = 
      calibrate_local_cpu(xti_udp_rr_request->cpu_rate);
  }
   
  /* before we send the response back to the initiator, pull some of */
  /* the socket parms from the globals */
  xti_udp_rr_response->send_buf_size = lss_size;
  xti_udp_rr_response->recv_buf_size = lsr_size;
  xti_udp_rr_response->so_rcvavoid   = loc_rcvavoid;
  xti_udp_rr_response->so_sndavoid   = loc_sndavoid;
 
  /* since we are going to call t_rcvudata() instead of t_rcv() we */
  /* need to init the unitdata structure raj 3/95 */

  memset (&recv_unitdata, 0, sizeof(recv_unitdata));
  recv_unitdata.addr.maxlen = sizeof(struct sockaddr_in);
  recv_unitdata.addr.len    = sizeof(struct sockaddr_in);
  recv_unitdata.addr.buf    = (char *)&peeraddr_in;

  recv_unitdata.opt.maxlen = 0;
  recv_unitdata.opt.len    = 0;
  recv_unitdata.opt.buf    = NULL;

  recv_unitdata.udata.maxlen = xti_udp_rr_request->request_size;
  recv_unitdata.udata.len    = xti_udp_rr_request->request_size;
  recv_unitdata.udata.buf    = recv_ring->buffer_ptr;

  /* since we are going to call t_sndudata() instead of t_snd() we */
  /* need to init the unitdata structure raj 8/95 */

  memset (&send_unitdata, 0, sizeof(send_unitdata));
  send_unitdata.addr.maxlen = sizeof(struct sockaddr_in);
  send_unitdata.addr.len    = sizeof(struct sockaddr_in);
  send_unitdata.addr.buf    = (char *)&peeraddr_in;

  send_unitdata.opt.maxlen = 0;
  send_unitdata.opt.len    = 0;
  send_unitdata.opt.buf    = NULL;

  send_unitdata.udata.maxlen = xti_udp_rr_request->response_size;
  send_unitdata.udata.len    = xti_udp_rr_request->response_size;
  send_unitdata.udata.buf    = send_ring->buffer_ptr;

  send_response();
  
  
  /* Now it's time to start receiving data on the connection. We will */
  /* first grab the apropriate counters and then start grabbing. */
  
  cpu_start(xti_udp_rr_request->measure_cpu);
  
  if (xti_udp_rr_request->test_length > 0) {
    times_up = 0;
    trans_remaining = 0;
    start_timer(xti_udp_rr_request->test_length + PAD_TIME);
  }
  else {
    times_up = 1;
    trans_remaining = xti_udp_rr_request->test_length * -1;
  }
  
  addrlen = sizeof(peeraddr_in);
  bzero((char *)&peeraddr_in, addrlen);
  
  trans_received = 0;

  while ((!times_up) || (trans_remaining > 0)) {
    
    /* receive the request from the other side */
    if (t_rcvudata(s_data,
		   &recv_unitdata,
		   &flags) != 0) {
      if (errno == TNODATA) {
	continue;
      }
      if (errno == EINTR) {
	/* we must have hit the end of test time. */
	break;
      }
      if (debug) {
	fprintf(where,
		"recv_xti_udp_rr: t_rcvudata failed, t_errno %d errno %d\n",
		t_errno,
		errno);
	fflush(where);
      }
      netperf_response.content.serv_errno = t_errno;
      send_response();
      exit(1);
    }
    recv_ring = recv_ring->next;
    recv_unitdata.udata.buf = recv_ring->buffer_ptr;

    /* Now, send the response to the remote */
    if (t_sndudata(s_data,
		   &send_unitdata) != 0) {
      if (errno == EINTR) {
	/* we have hit end of test time. */
	break;
      }
      if (debug) {
	fprintf(where,
		"recv_xti_udp_rr: t_sndudata failed, t_errno %d errno %d\n",
		t_errno,
		errno);
	fflush(where);
      }
      netperf_response.content.serv_errno = errno;
      send_response();
      exit(1);
    }
    send_ring = send_ring->next;
    send_unitdata.udata.buf = send_ring->buffer_ptr;
    
    trans_received++;
    if (trans_remaining) {
      trans_remaining--;
    }
    
    if (debug) {
      fprintf(where,
	      "recv_xti_udp_rr: Transaction %d complete.\n",
	      trans_received);
      fflush(where);
    }
    
  }
  
  
  /* The loop now exits due to timeout or transaction count being */
  /* reached */
  
  cpu_stop(xti_udp_rr_request->measure_cpu,&elapsed_time);
  
  if (times_up) {
    /* we ended the test by time, which was at least 2 seconds */
    /* longer than we wanted to run. so, we want to subtract */
    /* PAD_TIME from the elapsed_time. */
    elapsed_time -= PAD_TIME;
  }
  /* send the results to the sender			*/
  
  if (debug) {
    fprintf(where,
	    "recv_xti_udp_rr: got %d transactions\n",
	    trans_received);
    fflush(where);
  }
  
  xti_udp_rr_results->bytes_received = (trans_received * 
				    (xti_udp_rr_request->request_size + 
				     xti_udp_rr_request->response_size));
  xti_udp_rr_results->trans_received = trans_received;
  xti_udp_rr_results->elapsed_time	 = elapsed_time;
  xti_udp_rr_results->cpu_method     = cpu_method;
  if (xti_udp_rr_request->measure_cpu) {
    xti_udp_rr_results->cpu_util	= calc_cpu_util(elapsed_time);
  }
  
  if (debug) {
    fprintf(where,
	    "recv_xti_udp_rr: test complete, sending results.\n");
    fflush(where);
  }
  
  send_response();

  /* we are done with the socket now */
  close(s_data);

}

 /* this routine implements the receive (netserver) side of a XTI_TCP_RR */
 /* test */
void 
recv_xti_tcp_rr()
{
  
  struct ring_elt *send_ring;
  struct ring_elt *recv_ring;

  struct sockaddr_in  myaddr_in,  peeraddr_in;
  struct t_bind bind_req, bind_resp;
  struct t_call call_req;

  SOCKET s_listen,s_data;
  int 	addrlen;
  char	*temp_message_ptr;
  int	trans_received;
  int	trans_remaining;
  int	bytes_sent;
  int	request_bytes_recvd;
  int	request_bytes_remaining;
  int	timed_out = 0;
  float	elapsed_time;
  
  struct	xti_tcp_rr_request_struct	*xti_tcp_rr_request;
  struct	xti_tcp_rr_response_struct	*xti_tcp_rr_response;
  struct	xti_tcp_rr_results_struct	*xti_tcp_rr_results;
  
  xti_tcp_rr_request = 
    (struct xti_tcp_rr_request_struct *)netperf_request.content.test_specific_data;
  xti_tcp_rr_response =
    (struct xti_tcp_rr_response_struct *)netperf_response.content.test_specific_data;
  xti_tcp_rr_results =
    (struct xti_tcp_rr_results_struct *)netperf_response.content.test_specific_data;
  
  if (debug) {
    fprintf(where,"netserver: recv_xti_tcp_rr: entered...\n");
    fflush(where);
  }
  
  /* We want to set-up the listen socket with all the desired */
  /* parameters and then let the initiator know that all is ready. If */
  /* socket size defaults are to be used, then the initiator will have */
  /* sent us 0's. If the socket sizes cannot be changed, then we will */
  /* send-back what they are. If that information cannot be determined, */
  /* then we send-back -1's for the sizes. If things go wrong for any */
  /* reason, we will drop back ten yards and punt. */
  
  /* If anything goes wrong, we want the remote to know about it. It */
  /* would be best if the error that the remote reports to the user is */
  /* the actual error we encountered, rather than some bogus unexpected */
  /* response type message. */
  
  if (debug) {
    fprintf(where,"recv_xti_tcp_rr: setting the response type...\n");
    fflush(where);
  }
  
  netperf_response.content.response_type = XTI_TCP_RR_RESPONSE;
  
  if (debug) {
    fprintf(where,"recv_xti_tcp_rr: the response type is set...\n");
    fflush(where);
  }
  
  /* allocate the recv and send rings with the requested alignments */
  /* and offsets. raj 7/94 */
  if (debug) {
    fprintf(where,"recv_xti_tcp_rr: requested recv alignment of %d offset %d\n",
	    xti_tcp_rr_request->recv_alignment,
	    xti_tcp_rr_request->recv_offset);
    fprintf(where,"recv_xti_tcp_rr: requested send alignment of %d offset %d\n",
	    xti_tcp_rr_request->send_alignment,
	    xti_tcp_rr_request->send_offset);
    fflush(where);
  }

  /* at some point, these need to come to us from the remote system */
  if (send_width == 0) send_width = 1;
  if (recv_width == 0) recv_width = 1;

  send_ring = allocate_buffer_ring(send_width,
				   xti_tcp_rr_request->response_size,
				   xti_tcp_rr_request->send_alignment,
				   xti_tcp_rr_request->send_offset);

  recv_ring = allocate_buffer_ring(recv_width,
				   xti_tcp_rr_request->request_size,
				   xti_tcp_rr_request->recv_alignment,
				   xti_tcp_rr_request->recv_offset);

  
  /* Let's clear-out our sockaddr for the sake of cleanlines. Then we */
  /* can put in OUR values !-) At some point, we may want to nail this */
  /* socket to a particular network-level address, but for now, */
  /* INADDR_ANY should be just fine. */
  
  bzero((char *)&myaddr_in,
	sizeof(myaddr_in));
  myaddr_in.sin_family      = AF_INET;
  myaddr_in.sin_addr.s_addr = INADDR_ANY;
  myaddr_in.sin_port        = 0;
  
  /* Grab a socket to listen on, and then listen on it. */
  
  if (debug) {
    fprintf(where,"recv_xti_tcp_rr: grabbing a socket...\n");
    fflush(where);
  }

  /* create_xti_endpoint expects to find some things in the global */
  /* variables, so set the globals based on the values in the request. */
  /* once the socket has been created, we will set the response values */
  /* based on the updated value of those globals. raj 7/94 */
  lss_size = xti_tcp_rr_request->send_buf_size;
  lsr_size = xti_tcp_rr_request->recv_buf_size;
  loc_nodelay = xti_tcp_rr_request->no_delay;
  loc_rcvavoid = xti_tcp_rr_request->so_rcvavoid;
  loc_sndavoid = xti_tcp_rr_request->so_sndavoid;
  
#ifdef __alpha
  
  /* ok - even on a DEC box, strings are strings. I din't really want */
  /* to ntohl the words of a string. since I don't want to teach the */
  /* send_ and recv_ _request and _response routines about the types, */
  /* I will put "anti-ntohl" calls here. I imagine that the "pure" */
  /* solution would be to use XDR, but I am still leary of being able */
  /* to find XDR libs on all platforms I want running netperf. raj */
  {
    int *charword;
    int *initword;
    int *lastword;
    
    initword = (int *) xti_tcp_rr_request->xti_device;
    lastword = initword + ((xti_tcp_rr_request->dev_name_len + 3) / 4);
    
    for (charword = initword;
	 charword < lastword;
	 charword++) {
      
      *charword = htonl(*charword);
    }
  }
  
#endif /* __alpha */

  s_listen = create_xti_endpoint(xti_tcp_rr_request->xti_device);
  
  if (s_listen == INVALID_SOCKET) {
    netperf_response.content.serv_errno = errno;
    send_response();
    
    exit(1);
  }
  
  /* Let's get an address assigned to this socket so we can tell the */
  /* initiator how to reach the data socket. There may be a desire to */
  /* nail this socket to a specific IP address in a multi-homed, */
  /* multi-connection situation, but for now, we'll ignore the issue */
  /* and concentrate on single connection testing. */

  bind_req.addr.maxlen = sizeof(struct sockaddr_in);
  bind_req.addr.len    = sizeof(struct sockaddr_in);
  bind_req.addr.buf    = (char *)&myaddr_in;
  bind_req.qlen        = 1;

  bind_resp.addr.maxlen = sizeof(struct sockaddr_in);
  bind_resp.addr.len    = sizeof(struct sockaddr_in);
  bind_resp.addr.buf    = (char *)&myaddr_in;
  bind_resp.qlen        = 1;

  if (t_bind(s_listen,
	     &bind_req,
	     &bind_resp) == SOCKET_ERROR) {
    netperf_response.content.serv_errno = t_errno;
    close(s_listen);
    send_response();
    
    exit(1);
  }

  if (debug) {
    fprintf(where,
	    "recv_xti_tcp_rr: t_bind complete port %d\n",
	    ntohs(myaddr_in.sin_port));
    fflush(where);
  }
  
  /* Now myaddr_in contains the port and the internet address this is */
  /* returned to the sender also implicitly telling the sender that the */
  /* socket buffer sizing has been done. */
  
  xti_tcp_rr_response->data_port_number = (int) ntohs(myaddr_in.sin_port);
  netperf_response.content.serv_errno   = 0;
  
  /* But wait, there's more. If the initiator wanted cpu measurements, */
  /* then we must call the calibrate routine, which will return the max */
  /* rate back to the initiator. If the CPU was not to be measured, or */
  /* something went wrong with the calibration, we will return a 0.0 to */
  /* the initiator. */
  
  xti_tcp_rr_response->cpu_rate = 0.0; 	/* assume no cpu */
  xti_tcp_rr_response->measure_cpu = 0;

  if (xti_tcp_rr_request->measure_cpu) {
    xti_tcp_rr_response->measure_cpu = 1;
    xti_tcp_rr_response->cpu_rate = calibrate_local_cpu(xti_tcp_rr_request->cpu_rate);
  }
  
  
  /* before we send the response back to the initiator, pull some of */
  /* the socket parms from the globals */
  xti_tcp_rr_response->send_buf_size = lss_size;
  xti_tcp_rr_response->recv_buf_size = lsr_size;
  xti_tcp_rr_response->no_delay = loc_nodelay;
  xti_tcp_rr_response->so_rcvavoid = loc_rcvavoid;
  xti_tcp_rr_response->so_sndavoid = loc_sndavoid;
  xti_tcp_rr_response->test_length = xti_tcp_rr_request->test_length;
  send_response();
  
  /* Now, let's set-up the socket to listen for connections. for xti, */
  /* the t_listen call is blocking by default - this is different */
  /* semantics from BSD - probably has to do with being able to reject */
  /* a call before an accept */
  call_req.addr.maxlen = sizeof(struct sockaddr_in);
  call_req.addr.len    = sizeof(struct sockaddr_in);
  call_req.addr.buf    = (char *)&peeraddr_in;
  call_req.opt.maxlen  = 0;
  call_req.opt.len     = 0;
  call_req.opt.buf     = NULL;
  call_req.udata.maxlen= 0;
  call_req.udata.len   = 0;
  call_req.udata.buf   = 0;

  if (t_listen(s_listen, &call_req) == -1) {
    fprintf(where,
	    "recv_xti_tcp_rr: t_listen: errno %d t_errno %d\n",
	    errno,
	    t_errno);
    fflush(where);
    netperf_response.content.serv_errno = t_errno;
    close(s_listen);
    send_response();
    exit(1);
  }
  
  if (debug) {
    fprintf(where,
	    "recv_xti_tcp_rr: t_listen complete t_look 0x%.4x\n",
	    t_look(s_listen));
    fflush(where);
  }
  
  /* now just rubber stamp the thing. we want to use the same fd? so */
  /* we will just equate s_data with s_listen. this seems a little */
  /* hokey to me, but then I'm a BSD biggot still. raj 2/95 */
  s_data = s_listen;
  if (t_accept(s_listen,
	       s_data,
	       &call_req) == -1) {
    fprintf(where,
	    "recv_xti_tcp_rr: t_accept: errno %d t_errno %d\n",
	    errno,
	    t_errno);
    fflush(where);
    close(s_listen);
    exit(1);
  }
  
  if (debug) {
    fprintf(where,
	    "recv_xti_tcp_rr: t_accept complete t_look 0x%.4x",
	    t_look(s_data));
    fprintf(where,
	    " remote is %s port %d\n",
	    inet_ntoa(*(struct in_addr *)&peeraddr_in.sin_addr),
	    ntohs(peeraddr_in.sin_port));
    fflush(where);
  }
  
  /* Now it's time to start receiving data on the connection. We will */
  /* first grab the apropriate counters and then start grabbing. */
  
  cpu_start(xti_tcp_rr_request->measure_cpu);
  
  if (xti_tcp_rr_request->test_length > 0) {
    times_up = 0;
    trans_remaining = 0;
    start_timer(xti_tcp_rr_request->test_length + PAD_TIME);
  }
  else {
    times_up = 1;
    trans_remaining = xti_tcp_rr_request->test_length * -1;
  }

  trans_received = 0;
  
  while ((!times_up) || (trans_remaining > 0)) {
    temp_message_ptr = recv_ring->buffer_ptr;
    request_bytes_remaining	= xti_tcp_rr_request->request_size;
    while(request_bytes_remaining > 0) {
      if((request_bytes_recvd=t_rcv(s_data,
				    temp_message_ptr,
				    request_bytes_remaining,
				    &xti_flags)) == SOCKET_ERROR) {
	if (errno == EINTR) {
	  /* the timer popped */
	  timed_out = 1;
	  break;
	}
	fprintf(where,
		"recv_xti_tcp_rr: t_rcv: errno %d t_errno %d len %d",
		errno,
		t_errno,
		request_bytes_recvd);
	fprintf(where,
		" t_look 0x%x",
		t_look(s_data));
	fflush(where);
	netperf_response.content.serv_errno = t_errno;
	send_response();
	exit(1);
      }
      else {
	request_bytes_remaining -= request_bytes_recvd;
	temp_message_ptr  += request_bytes_recvd;
      }
    }

    recv_ring = recv_ring->next;

    if (timed_out) {
      /* we hit the end of the test based on time - lets */
      /* bail out of here now... */
      if (debug) {
	fprintf(where,"yo5\n");
	fflush(where);
      }						
      break;
    }
    
    /* Now, send the response to the remote */
    if((bytes_sent=t_snd(s_data,
			 send_ring->buffer_ptr,
			 xti_tcp_rr_request->response_size,
			 0)) == -1) {
      if (errno == EINTR) {
	/* the test timer has popped */
	timed_out = 1;
	if (debug) {
	  fprintf(where,"yo6\n");
	  fflush(where);						
	}
	break;
      }
      fprintf(where,
	      "recv_xti_tcp_rr: t_rcv: errno %d t_errno %d len %d",
	      errno,
	      t_errno,
	      bytes_sent);
      fprintf(where,
	      " t_look 0x%x",
	      t_look(s_data));
      fflush(where);
      netperf_response.content.serv_errno = t_errno;
      send_response();
      exit(1);
    }
    
    send_ring = send_ring->next;

    trans_received++;
    if (trans_remaining) {
      trans_remaining--;
    }
  }
  
  
  /* The loop now exits due to timeout or transaction count being */
  /* reached */
  
  cpu_stop(xti_tcp_rr_request->measure_cpu,&elapsed_time);
  
  stop_timer(); /* this is probably unnecessary, but it shouldn't hurt */

  if (timed_out) {
    /* we ended the test by time, which was at least 2 seconds */
    /* longer than we wanted to run. so, we want to subtract */
    /* PAD_TIME from the elapsed_time. */
    elapsed_time -= PAD_TIME;
  }

  /* send the results to the sender			*/
  
  if (debug) {
    fprintf(where,
	    "recv_xti_tcp_rr: got %d transactions\n",
	    trans_received);
    fflush(where);
  }
  
  xti_tcp_rr_results->bytes_received = (trans_received * 
					(xti_tcp_rr_request->request_size + 
					 xti_tcp_rr_request->response_size));
  xti_tcp_rr_results->trans_received = trans_received;
  xti_tcp_rr_results->elapsed_time   = elapsed_time;
  xti_tcp_rr_results->cpu_method     = cpu_method;
  if (xti_tcp_rr_request->measure_cpu) {
    xti_tcp_rr_results->cpu_util	= calc_cpu_util(elapsed_time);
  }
  
  if (debug) {
    fprintf(where,
	    "recv_xti_tcp_rr: test complete, sending results.\n");
    fflush(where);
  }
  
  /* we are done with the socket, free it */
  t_close(s_data);

  send_response();
  
}



 /* this test is intended to test the performance of establishing a */
 /* connection, exchanging a request/response pair, and repeating. it */
 /* is expected that this would be a good starting-point for */
 /* comparision of T/TCP with classic TCP for transactional workloads. */
 /* it will also look (can look) much like the communication pattern */
 /* of http for www access. */

void 
send_xti_tcp_conn_rr(char remote_host[])
{
  
  char *tput_title = "\
Local /Remote\n\
Socket Size   Request  Resp.   Elapsed  Trans.\n\
Send   Recv   Size     Size    Time     Rate         \n\
bytes  Bytes  bytes    bytes   secs.    per sec   \n\n";
  
  char *tput_fmt_0 =
    "%7.2f\n";
  
  char *tput_fmt_1_line_1 = "\
%-6d %-6d %-6d   %-6d  %-6.2f   %7.2f   \n";
  char *tput_fmt_1_line_2 = "\
%-6d %-6d\n";
  
  char *cpu_title = "\
Local /Remote\n\
Socket Size   Request Resp.  Elapsed Trans.   CPU    CPU    S.dem   S.dem\n\
Send   Recv   Size    Size   Time    Rate     local  remote local   remote\n\
bytes  bytes  bytes   bytes  secs.   per sec  %%      %%      us/Tr   us/Tr\n\n";
  
  char *cpu_fmt_0 =
    "%6.3f\n";
  
  char *cpu_fmt_1_line_1 = "\
%-6d %-6d %-6d  %-6d %-6.2f  %-6.2f   %-6.2f %-6.2f %-6.3f  %-6.3f\n";
  
  char *cpu_fmt_1_line_2 = "\
%-6d %-6d\n";
  
  char *ksink_fmt = "\
Alignment      Offset\n\
Local  Remote  Local  Remote\n\
Send   Recv    Send   Recv\n\
%5d  %5d   %5d  %5d\n";
  
  
  int 			one = 1;
  int			timed_out = 0;
  float			elapsed_time;
  
  int	len;
  struct ring_elt *send_ring;
  struct ring_elt *recv_ring;
  char	*temp_message_ptr;
  int	nummessages;
  SOCKET send_socket;
  int	trans_remaining;
  double	bytes_xferd;
  int	sock_opt_len = sizeof(int);
  int	rsp_bytes_left;
  int	rsp_bytes_recvd;
  
  float	local_cpu_utilization;
  float	local_service_demand;
  float	remote_cpu_utilization;
  float	remote_service_demand;
  double	thruput;
  
  struct	hostent	        *hp;
  struct	sockaddr_in	server;
  struct        sockaddr_in     *myaddr;
  unsigned      int             addr;
  int                           myport;

  struct	xti_tcp_conn_rr_request_struct	*xti_tcp_conn_rr_request;
  struct	xti_tcp_conn_rr_response_struct	*xti_tcp_conn_rr_response;
  struct	xti_tcp_conn_rr_results_struct	*xti_tcp_conn_rr_result;
  
  xti_tcp_conn_rr_request = 
    (struct xti_tcp_conn_rr_request_struct *)netperf_request.content.test_specific_data;
  xti_tcp_conn_rr_response = 
    (struct xti_tcp_conn_rr_response_struct *)netperf_response.content.test_specific_data;
  xti_tcp_conn_rr_result =
    (struct xti_tcp_conn_rr_results_struct *)netperf_response.content.test_specific_data;
  
  /* since we are now disconnected from the code that established the */
  /* control socket, and since we want to be able to use different */
  /* protocols and such, we are passed the name of the remote host and */
  /* must turn that into the test specific addressing information. */
  
  myaddr = (struct sockaddr_in *)malloc(sizeof(struct sockaddr_in));
  if (myaddr == NULL) {
    printf("malloc(%d) failed!\n", sizeof(struct sockaddr_in));
    exit(1);
  }

  bzero((char *)&server,
	sizeof(server));
  bzero((char *)myaddr,
	sizeof(struct sockaddr_in));
  myaddr->sin_family = AF_INET;

  /* it would seem that while HP-UX will allow an IP address (as a */
  /* string) in a call to gethostbyname, other, less enlightened */
  /* systems do not. fix from awjacks@ca.sandia.gov raj 10/95 */  
  /* order changed to check for IP address first. raj 7/96 */

  if ((addr = inet_addr(remote_host)) == SOCKET_ERROR) {
    /* it was not an IP address, try it as a name */
    if ((hp = gethostbyname(remote_host)) == NULL) {
      /* we have no idea what it is */
      fprintf(where,
	      "establish_control: could not resolve the destination %s\n",
	      remote_host);
      fflush(where);
      exit(1);
    }
    else {
      /* it was a valid remote_host */
      bcopy(hp->h_addr,
	    (char *)&server.sin_addr,
	    hp->h_length);
      server.sin_family = hp->h_addrtype;
    }
  }
  else {
    /* it was a valid IP address */
    server.sin_addr.s_addr = addr;
    server.sin_family = AF_INET;
  }    
  
  if ( print_headers ) {
    fprintf(where,"TCP Connect/Request/Response Test\n");
    if (local_cpu_usage || remote_cpu_usage)
      fprintf(where,cpu_title,format_units());
    else
      fprintf(where,tput_title,format_units());
  }
  
  /* initialize a few counters */
  
  nummessages	=	0;
  bytes_xferd	=	0.0;
  times_up 	= 	0;
  
  /* set-up the data buffers with the requested alignment and offset */
  if (send_width == 0) send_width = 1;
  if (recv_width == 0) recv_width = 1;

  send_ring = allocate_buffer_ring(send_width,
				   req_size,
				   local_send_align,
				   local_send_offset);

  recv_ring = allocate_buffer_ring(recv_width,
				   rsp_size,
				   local_recv_align,
				   local_recv_offset);


  if (debug) {
    fprintf(where,"send_xti_tcp_conn_rr: send_socket obtained...\n");
  }
  
  /* If the user has requested cpu utilization measurements, we must */
  /* calibrate the cpu(s). We will perform this task within the tests */
  /* themselves. If the user has specified the cpu rate, then */
  /* calibrate_local_cpu will return rather quickly as it will have */
  /* nothing to do. If local_cpu_rate is zero, then we will go through */
  /* all the "normal" calibration stuff and return the rate back.*/
  
  if (local_cpu_usage) {
    local_cpu_rate = calibrate_local_cpu(local_cpu_rate);
  }
  
  /* Tell the remote end to do a listen. The server alters the socket */
  /* paramters on the other side at this point, hence the reason for */
  /* all the values being passed in the setup message. If the user did */
  /* not specify any of the parameters, they will be passed as 0, which */
  /* will indicate to the remote that no changes beyond the system's */
  /* default should be used. Alignment is the exception, it will */
  /* default to 8, which will be no alignment alterations. */
  
  netperf_request.content.request_type	        =	DO_XTI_TCP_CRR;
  xti_tcp_conn_rr_request->recv_buf_size	=	rsr_size;
  xti_tcp_conn_rr_request->send_buf_size	=	rss_size;
  xti_tcp_conn_rr_request->recv_alignment	=	remote_recv_align;
  xti_tcp_conn_rr_request->recv_offset	=	remote_recv_offset;
  xti_tcp_conn_rr_request->send_alignment	=	remote_send_align;
  xti_tcp_conn_rr_request->send_offset	=	remote_send_offset;
  xti_tcp_conn_rr_request->request_size	=	req_size;
  xti_tcp_conn_rr_request->response_size	=	rsp_size;
  xti_tcp_conn_rr_request->no_delay	        =	rem_nodelay;
  xti_tcp_conn_rr_request->measure_cpu	=	remote_cpu_usage;
  xti_tcp_conn_rr_request->cpu_rate	        =	remote_cpu_rate;
  xti_tcp_conn_rr_request->so_rcvavoid	=	rem_rcvavoid;
  xti_tcp_conn_rr_request->so_sndavoid	=	rem_sndavoid;
  if (test_time) {
    xti_tcp_conn_rr_request->test_length	=	test_time;
  }
  else {
    xti_tcp_conn_rr_request->test_length	=	test_trans * -1;
  }
  
  if (debug > 1) {
    fprintf(where,"netperf: send_xti_tcp_conn_rr: requesting TCP crr test\n");
  }
  
  send_request();
  
  /* The response from the remote will contain all of the relevant 	*/
  /* socket parameters for this test type. We will put them back into 	*/
  /* the variables here so they can be displayed if desired.  The	*/
  /* remote will have calibrated CPU if necessary, and will have done	*/
  /* all the needed set-up we will have calibrated the cpu locally	*/
  /* before sending the request, and will grab the counter value right	*/
  /* after the connect returns. The remote will grab the counter right	*/
  /* after the accept call. This saves the hassle of extra messages	*/
  /* being sent for the TCP tests.					*/
  
  recv_response();
  
  if (!netperf_response.content.serv_errno) {
    rsr_size	=	xti_tcp_conn_rr_response->recv_buf_size;
    rss_size	=	xti_tcp_conn_rr_response->send_buf_size;
    rem_nodelay	=	xti_tcp_conn_rr_response->no_delay;
    remote_cpu_usage=	xti_tcp_conn_rr_response->measure_cpu;
    remote_cpu_rate = 	xti_tcp_conn_rr_response->cpu_rate;
    /* make sure that port numbers are in network order */
    server.sin_port	=	(short)xti_tcp_conn_rr_response->data_port_number;
    server.sin_port =	htons(server.sin_port);
    if (debug) {
      fprintf(where,"remote listen done.\n");
      fprintf(where,"remote port is %d\n",ntohs(server.sin_port));
      fflush(where);
    }
  }
  else {
    Set_errno(netperf_response.content.serv_errno);
    perror("netperf: remote error");
    
    exit(1);
  }
  
  /* Set-up the test end conditions. For a request/response test, they */
  /* can be either time or transaction based. */
  
  if (test_time) {
    /* The user wanted to end the test after a period of time. */
    times_up = 0;
    trans_remaining = 0;
    start_timer(test_time);
  }
  else {
    /* The tester wanted to send a number of bytes. */
    trans_remaining = test_bytes;
    times_up = 1;
  }
  
  /* The cpu_start routine will grab the current time and possibly */
  /* value of the idle counter for later use in measuring cpu */
  /* utilization and/or service demand and thruput. */
  
  cpu_start(local_cpu_usage);
  
  /* We use an "OR" to control test execution. When the test is */
  /* controlled by time, the byte count check will always return false. */
  /* When the test is controlled by byte count, the time test will */
  /* always return false. When the test is finished, the whole */
  /* expression will go false and we will stop sending data. I think I */
  /* just arbitrarily decrement trans_remaining for the timed test, but */
  /* will not do that just yet... One other question is whether or not */
  /* the send buffer and the receive buffer should be the same buffer. */

  /* just for grins, start the port numbers at 65530. this should */
  /* quickly flush-out those broken implementations of TCP which treat */
  /* the port number as a signed 16 bit quantity. */
  myport = 65530;
  myaddr->sin_port = htons(myport);
  
  while ((!times_up) || (trans_remaining > 0)) {

    /* set up the data socket */
    send_socket = create_xti_endpoint(loc_xti_device);
  
    if (send_socket == INVALID_SOCKET) {
      perror("netperf: send_xti_tcp_conn_rr: tcp stream data socket");
      exit(1);
    }

    /* we set SO_REUSEADDR on the premis that no unreserved port */
    /* number on the local system is going to be already connected to */
    /* the remote netserver's port number. we might still have a */
    /* problem if there is a port in the unconnected state. In that */
    /* case, we might want to throw-in a goto to the point where we */
    /* increment the port number by one and try again. of course, this */
    /* could lead to a big load of spinning. one thing that I might */
    /* try later is to have the remote actually allocate a couple of */
    /* port numbers and cycle through those as well. depends on if we */
    /* can get through all the unreserved port numbers in less than */
    /* the length of the TIME_WAIT state raj 8/94 */
    one = 1;
    if(setsockopt(send_socket, SOL_SOCKET, SO_REUSEADDR,
		  (char *)&one, sock_opt_len) == SOCKET_ERROR) {
      perror("netperf: send_xti_tcp_conn_rr: so_reuseaddr");
      exit(1);
    }

    /* we want to bind our socket to a particular port number. */
    if (bind(send_socket,
	     (struct sockaddr *)myaddr,
	     sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
      printf("netperf: send_xti_tcp_conn_rr: tried to bind to port %d\n",
	     ntohs(myaddr->sin_port));
      perror("netperf: send_xti_tcp_conn_rr: bind");
      exit(1);
    }

    /* Connect up to the remote port on the data socket  */
    if (connect(send_socket, 
		(struct sockaddr *)&server,
		sizeof(server)) == INVALID_SOCKET){
      if (errno == EINTR) {
	/* we hit the end of a */
	/* timed test. */
	timed_out = 1;
	break;
      }
      perror("netperf: data socket connect failed");
      printf("\tattempted to connect on socket %d to port %d",
	     send_socket,
	     ntohs(server.sin_port));
      printf(" from port %d \n",ntohs(myaddr->sin_port));
      exit(1);
    }

    /* send the request */
    if((len=send(send_socket,
		 send_ring->buffer_ptr,
		 req_size,
		 0)) != req_size) {
      if (errno == EINTR) {
	/* we hit the end of a */
	/* timed test. */
	timed_out = 1;
	break;
      }
      perror("send_xti_tcp_conn_rr: data send error");
      exit(1);
    }
    send_ring = send_ring->next;

    /* receive the response */
    rsp_bytes_left = rsp_size;
    temp_message_ptr  = recv_ring->buffer_ptr;
    while(rsp_bytes_left > 0) {
      if((rsp_bytes_recvd=recv(send_socket,
			       temp_message_ptr,
			       rsp_bytes_left,
			       0)) == SOCKET_ERROR) {
	if (errno == EINTR) {
	  /* We hit the end of a timed test. */
	  timed_out = 1;
	  break;
	}
	perror("send_xti_tcp_conn_rr: data recv error");
	exit(1);
      }
      rsp_bytes_left -= rsp_bytes_recvd;
      temp_message_ptr  += rsp_bytes_recvd;
    }	
    recv_ring = recv_ring->next;

    if (timed_out) {
      /* we may have been in a nested while loop - we need */
      /* another call to break. */
      break;
    }

    close(send_socket);

    nummessages++;          
    if (trans_remaining) {
      trans_remaining--;
    }
    
    if (debug > 3) {
      fprintf(where,
	      "Transaction %d completed on local port %d\n",
	      nummessages,
	      ntohs(myaddr->sin_port));
      fflush(where);
    }

newport:
    /* pick a new port number */
    myport = ntohs(myaddr->sin_port);
    myport++;
    /* we do not want to use the port number that the server is */
    /* sitting at - this would cause us to fail in a loopback test */

    if (myport == ntohs(server.sin_port)) myport++;

    /* wrap the port number when we get to 65535. NOTE, some broken */
    /* TCP's might treat the port number as a signed 16 bit quantity. */
    /* we aren't interested in testing such broekn implementations :) */
    /* raj 8/94  */
    if (myport == 65535) {
      myport = 5000;
    }
    myaddr->sin_port = htons(myport);

    if (debug) {
      if ((myport % 1000) == 0) {
	printf("port %d\n",myport);
      }
    }

  }
  
  /* this call will always give us the elapsed time for the test, and */
  /* will also store-away the necessaries for cpu utilization */

  cpu_stop(local_cpu_usage,&elapsed_time);	/* was cpu being measured? */
  /* how long did we really run? */
  
  /* Get the statistics from the remote end. The remote will have */
  /* calculated service demand and all those interesting things. If it */
  /* wasn't supposed to care, it will return obvious values. */
  
  recv_response();
  if (!netperf_response.content.serv_errno) {
    if (debug)
      fprintf(where,"remote results obtained\n");
  }
  else {
    Set_errno(netperf_response.content.serv_errno);
    perror("netperf: remote error");
    
    exit(1);
  }
  
  /* We now calculate what our thruput was for the test. In the future, */
  /* we may want to include a calculation of the thruput measured by */
  /* the remote, but it should be the case that for a TCP stream test, */
  /* that the two numbers should be *very* close... We calculate */
  /* bytes_sent regardless of the way the test length was controlled. */
  /* If it was time, we needed to, and if it was by bytes, the user may */
  /* have specified a number of bytes that wasn't a multiple of the */
  /* send_size, so we really didn't send what he asked for ;-) We use */
  /* Kbytes/s as the units of thruput for a TCP stream test, where K = */
  /* 1024. A future enhancement *might* be to choose from a couple of */
  /* unit selections. */ 
  
  bytes_xferd	= (req_size * nummessages) + (rsp_size * nummessages);
  thruput	= calc_thruput(bytes_xferd);
  
  if (local_cpu_usage || remote_cpu_usage) {
    /* We must now do a little math for service demand and cpu */
    /* utilization for the system(s) */
    /* Of course, some of the information might be bogus because */
    /* there was no idle counter in the kernel(s). We need to make */
    /* a note of this for the user's benefit...*/
    if (local_cpu_usage) {
      if (local_cpu_rate == 0.0) {
	fprintf(where,"WARNING WARNING WARNING  WARNING WARNING WARNING  WARNING!\n");
	fprintf(where,"Local CPU usage numbers based on process information only!\n");
	fflush(where);
      }
      local_cpu_utilization = calc_cpu_util(0.0);
      /* since calc_service demand is doing ms/Kunit we will */
      /* multiply the number of transaction by 1024 to get */
      /* "good" numbers */
      local_service_demand  = calc_service_demand((double) nummessages*1024,
						  0.0,
						  0.0,
						  0);
    }
    else {
      local_cpu_utilization	= -1.0;
      local_service_demand	= -1.0;
    }
    
    if (remote_cpu_usage) {
      if (remote_cpu_rate == 0.0) {
	fprintf(where,"DANGER  DANGER  DANGER    DANGER  DANGER  DANGER    DANGER!\n");
	fprintf(where,"Remote CPU usage numbers based on process information only!\n");
	fflush(where);
      }
      remote_cpu_utilization = xti_tcp_conn_rr_result->cpu_util;
      /* since calc_service demand is doing ms/Kunit we will */
      /* multiply the number of transaction by 1024 to get */
      /* "good" numbers */
      remote_service_demand = calc_service_demand((double) nummessages*1024,
						  0.0,
						  remote_cpu_utilization,
						  xti_tcp_conn_rr_result->num_cpus);
    }
    else {
      remote_cpu_utilization = -1.0;
      remote_service_demand  = -1.0;
    }
    
    /* We are now ready to print all the information. If the user */
    /* has specified zero-level verbosity, we will just print the */
    /* local service demand, or the remote service demand. If the */
    /* user has requested verbosity level 1, he will get the basic */
    /* "streamperf" numbers. If the user has specified a verbosity */
    /* of greater than 1, we will display a veritable plethora of */
    /* background information from outside of this block as it it */
    /* not cpu_measurement specific...  */
    
    switch (verbosity) {
    case 0:
      if (local_cpu_usage) {
	fprintf(where,
		cpu_fmt_0,
		local_service_demand);
      }
      else {
	fprintf(where,
		cpu_fmt_0,
		remote_service_demand);
      }
      break;
    case 1:
      fprintf(where,
	      cpu_fmt_1_line_1,		/* the format string */
	      lss_size,		/* local sendbuf size */
	      lsr_size,
	      req_size,		/* how large were the requests */
	      rsp_size,		/* guess */
	      elapsed_time,		/* how long was the test */
	      nummessages/elapsed_time,
	      local_cpu_utilization,	/* local cpu */
	      remote_cpu_utilization,	/* remote cpu */
	      local_service_demand,	/* local service demand */
	      remote_service_demand);	/* remote service demand */
      fprintf(where,
	      cpu_fmt_1_line_2,
	      rss_size,
	      rsr_size);
      break;
    }
  }
  else {
    /* The tester did not wish to measure service demand. */
    switch (verbosity) {
    case 0:
      fprintf(where,
	      tput_fmt_0,
	      nummessages/elapsed_time);
      break;
    case 1:
      fprintf(where,
	      tput_fmt_1_line_1,	/* the format string */
	      lss_size,
	      lsr_size,
	      req_size,		/* how large were the requests */
	      rsp_size,		/* how large were the responses */
	      elapsed_time, 		/* how long did it take */
	      nummessages/elapsed_time);
      fprintf(where,
	      tput_fmt_1_line_2,
	      rss_size, 		/* remote recvbuf size */
	      rsr_size);
      
      break;
    }
  }
  
  /* it would be a good thing to include information about some of the */
  /* other parameters that may have been set for this test, but at the */
  /* moment, I do not wish to figure-out all the  formatting, so I will */
  /* just put this comment here to help remind me that it is something */
  /* that should be done at a later time. */
  
  if (verbosity > 1) {
    /* The user wanted to know it all, so we will give it to him. */
    /* This information will include as much as we can find about */
    /* TCP statistics, the alignments of the sends and receives */
    /* and all that sort of rot... */
    
    fprintf(where,
	    ksink_fmt);
  }
  
}


void 
recv_xti_tcp_conn_rr()
{
  
  char  *message;
  struct	sockaddr_in        myaddr_in,
  peeraddr_in;
  SOCKET s_listen,s_data;
  int 	addrlen;
  char	*recv_message_ptr;
  char	*send_message_ptr;
  char	*temp_message_ptr;
  int	trans_received;
  int	trans_remaining;
  int	bytes_sent;
  int	request_bytes_recvd;
  int	request_bytes_remaining;
  int	timed_out = 0;
  float	elapsed_time;
  
  struct	xti_tcp_conn_rr_request_struct	*xti_tcp_conn_rr_request;
  struct	xti_tcp_conn_rr_response_struct	*xti_tcp_conn_rr_response;
  struct	xti_tcp_conn_rr_results_struct	*xti_tcp_conn_rr_results;
  
  xti_tcp_conn_rr_request = 
    (struct xti_tcp_conn_rr_request_struct *)netperf_request.content.test_specific_data;
  xti_tcp_conn_rr_response = 
    (struct xti_tcp_conn_rr_response_struct *)netperf_response.content.test_specific_data;
  xti_tcp_conn_rr_results = 
    (struct xti_tcp_conn_rr_results_struct *)netperf_response.content.test_specific_data;
  
  if (debug) {
    fprintf(where,"netserver: recv_xti_tcp_conn_rr: entered...\n");
    fflush(where);
  }
  
  /* We want to set-up the listen socket with all the desired */
  /* parameters and then let the initiator know that all is ready. If */
  /* socket size defaults are to be used, then the initiator will have */
  /* sent us 0's. If the socket sizes cannot be changed, then we will */
  /* send-back what they are. If that information cannot be determined, */
  /* then we send-back -1's for the sizes. If things go wrong for any */
  /* reason, we will drop back ten yards and punt. */
  
  /* If anything goes wrong, we want the remote to know about it. It */
  /* would be best if the error that the remote reports to the user is */
  /* the actual error we encountered, rather than some bogus unexpected */
  /* response type message. */
  
  if (debug) {
    fprintf(where,"recv_xti_tcp_conn_rr: setting the response type...\n");
    fflush(where);
  }
  
  netperf_response.content.response_type = XTI_TCP_CRR_RESPONSE;
  
  if (debug) {
    fprintf(where,"recv_xti_tcp_conn_rr: the response type is set...\n");
    fflush(where);
  }

  /* set-up the data buffer with the requested alignment and offset */
  message = (char *)malloc(DATABUFFERLEN);
  if (message == NULL) {
    printf("malloc(%d) failed!\n", DATABUFFERLEN);
    exit(1);
  }
  
  /* We now alter the message_ptr variables to be at the desired */
  /* alignments with the desired offsets. */
  
  if (debug) {
    fprintf(where,
	    "recv_xti_tcp_conn_rr: requested recv alignment of %d offset %d\n",
	    xti_tcp_conn_rr_request->recv_alignment,
	    xti_tcp_conn_rr_request->recv_offset);
    fprintf(where,
	    "recv_xti_tcp_conn_rr: requested send alignment of %d offset %d\n",
	    xti_tcp_conn_rr_request->send_alignment,
	    xti_tcp_conn_rr_request->send_offset);
    fflush(where);
  }

  recv_message_ptr = ALIGN_BUFFER(message, xti_tcp_conn_rr_request->recv_alignment, xti_tcp_conn_rr_request->recv_offset);
  
  send_message_ptr = ALIGN_BUFFER(message, xti_tcp_conn_rr_request->send_alignment, xti_tcp_conn_rr_request->send_offset);
  
  if (debug) {
    fprintf(where,"recv_xti_tcp_conn_rr: receive alignment and offset set...\n");
    fflush(where);
  }
  
  /* Let's clear-out our sockaddr for the sake of cleanlines. Then we */
  /* can put in OUR values !-) At some point, we may want to nail this */
  /* socket to a particular network-level address, but for now, */
  /* INADDR_ANY should be just fine. */
  
  bzero((char *)&myaddr_in,
	sizeof(myaddr_in));
  myaddr_in.sin_family      = AF_INET;
  myaddr_in.sin_addr.s_addr = INADDR_ANY;
  myaddr_in.sin_port        = 0;
  
  /* Grab a socket to listen on, and then listen on it. */
  
  if (debug) {
    fprintf(where,"recv_xti_tcp_conn_rr: grabbing a socket...\n");
    fflush(where);
  }

  /* create_xti_endpoint expects to find some things in the global */
  /* variables, so set the globals based on the values in the request. */
  /* once the socket has been created, we will set the response values */
  /* based on the updated value of those globals. raj 7/94 */
  lss_size = xti_tcp_conn_rr_request->send_buf_size;
  lsr_size = xti_tcp_conn_rr_request->recv_buf_size;
  loc_nodelay = xti_tcp_conn_rr_request->no_delay;
  loc_rcvavoid = xti_tcp_conn_rr_request->so_rcvavoid;
  loc_sndavoid = xti_tcp_conn_rr_request->so_sndavoid;
  
  s_listen = create_xti_endpoint(loc_xti_device);
  
  if (s_listen == INVALID_SOCKET) {
    netperf_response.content.serv_errno = errno;
    send_response();
    if (debug) {
      fprintf(where,"could not create data socket\n");
      fflush(where);
    }
    exit(1);
  }

  /* Let's get an address assigned to this socket so we can tell the */
  /* initiator how to reach the data socket. There may be a desire to */
  /* nail this socket to a specific IP address in a multi-homed, */
  /* multi-connection situation, but for now, we'll ignore the issue */
  /* and concentrate on single connection testing. */
  
  if (bind(s_listen,
	   (struct sockaddr *)&myaddr_in,
	   sizeof(myaddr_in)) == SOCKET_ERROR) {
    netperf_response.content.serv_errno = errno;
    close(s_listen);
    send_response();
    if (debug) {
      fprintf(where,"could not bind\n");
      fflush(where);
    }
    exit(1);
  }

  /* Now, let's set-up the socket to listen for connections */
  if (listen(s_listen, 5) == SOCKET_ERROR) {
    netperf_response.content.serv_errno = errno;
    close(s_listen);
    send_response();
    if (debug) {
      fprintf(where,"could not listen\n");
      fflush(where);
    }
    exit(1);
  }
  
  /* now get the port number assigned by the system  */
  addrlen = sizeof(myaddr_in);
  if (getsockname(s_listen,
		  (struct sockaddr *)&myaddr_in,
		  &addrlen) == SOCKET_ERROR){
    netperf_response.content.serv_errno = errno;
    close(s_listen);
    send_response();
    if (debug) {
      fprintf(where,"could not geetsockname\n");
      fflush(where);
    }
    exit(1);
  }
  
  /* Now myaddr_in contains the port and the internet address this is */
  /* returned to the sender also implicitly telling the sender that the */
  /* socket buffer sizing has been done. */
  
  xti_tcp_conn_rr_response->data_port_number = (int) ntohs(myaddr_in.sin_port);
  if (debug) {
    fprintf(where,"telling the remote to call me at %d\n",
	    xti_tcp_conn_rr_response->data_port_number);
    fflush(where);
  }
  netperf_response.content.serv_errno   = 0;
  
  /* But wait, there's more. If the initiator wanted cpu measurements, */
  /* then we must call the calibrate routine, which will return the max */
  /* rate back to the initiator. If the CPU was not to be measured, or */
  /* something went wrong with the calibration, we will return a 0.0 to */
  /* the initiator. */
  
  xti_tcp_conn_rr_response->cpu_rate = 0.0; 	/* assume no cpu */
  if (xti_tcp_conn_rr_request->measure_cpu) {
    xti_tcp_conn_rr_response->measure_cpu = 1;
    xti_tcp_conn_rr_response->cpu_rate = 
      calibrate_local_cpu(xti_tcp_conn_rr_request->cpu_rate);
  }
  

  
  /* before we send the response back to the initiator, pull some of */
  /* the socket parms from the globals */
  xti_tcp_conn_rr_response->send_buf_size = lss_size;
  xti_tcp_conn_rr_response->recv_buf_size = lsr_size;
  xti_tcp_conn_rr_response->no_delay = loc_nodelay;
  xti_tcp_conn_rr_response->so_rcvavoid = loc_rcvavoid;
  xti_tcp_conn_rr_response->so_sndavoid = loc_sndavoid;

  send_response();
  
  addrlen = sizeof(peeraddr_in);
  
  /* Now it's time to start receiving data on the connection. We will */
  /* first grab the apropriate counters and then start grabbing. */
  
  cpu_start(xti_tcp_conn_rr_request->measure_cpu);
  
  /* The loop will exit when the sender does a shutdown, which will */
  /* return a length of zero   */
  
  if (xti_tcp_conn_rr_request->test_length > 0) {
    times_up = 0;
    trans_remaining = 0;
    start_timer(xti_tcp_conn_rr_request->test_length + PAD_TIME);
  }
  else {
    times_up = 1;
    trans_remaining = xti_tcp_conn_rr_request->test_length * -1;
  }
  
  trans_received = 0;

  while ((!times_up) || (trans_remaining > 0)) {

    /* accept a connection from the remote */
    if ((s_data=accept(s_listen,
		       (struct sockaddr *)&peeraddr_in,
		       &addrlen)) == INVALID_SOCKET) {
      if (errno == EINTR) {
	/* the timer popped */
	timed_out = 1;
	break;
      }
      fprintf(where,"recv_xti_tcp_conn_rr: accept: errno = %d\n",errno);
      fflush(where);
      close(s_listen);
      
      exit(1);
    }
  
    if (debug) {
      fprintf(where,"recv_xti_tcp_conn_rr: accepted data connection.\n");
      fflush(where);
    }
  
    temp_message_ptr	= recv_message_ptr;
    request_bytes_remaining	= xti_tcp_conn_rr_request->request_size;
    
    /* receive the request from the other side */
    while(request_bytes_remaining > 0) {
      if((request_bytes_recvd=recv(s_data,
				   temp_message_ptr,
				   request_bytes_remaining,
				   0)) == SOCKET_ERROR) {
	if (errno == EINTR) {
	  /* the timer popped */
	  timed_out = 1;
	  break;
	}
	netperf_response.content.serv_errno = errno;
	send_response();
	exit(1);
      }
      else {
	request_bytes_remaining -= request_bytes_recvd;
	temp_message_ptr  += request_bytes_recvd;
      }
    }
    
    if (timed_out) {
      /* we hit the end of the test based on time - lets */
      /* bail out of here now... */
      fprintf(where,"yo5\n");
      fflush(where);						
      break;
    }
    
    /* Now, send the response to the remote */
    if((bytes_sent=send(s_data,
			send_message_ptr,
			xti_tcp_conn_rr_request->response_size,
			0)) == SOCKET_ERROR) {
      if (errno == EINTR) {
	/* the test timer has popped */
	timed_out = 1;
	fprintf(where,"yo6\n");
	fflush(where);						
	break;
      }
      netperf_response.content.serv_errno = 99;
      send_response();
      exit(1);
    }
    
    trans_received++;
    if (trans_remaining) {
      trans_remaining--;
    }
    
    if (debug) {
      fprintf(where,
	      "recv_xti_tcp_conn_rr: Transaction %d complete\n",
	      trans_received);
      fflush(where);
    }

    /* close the connection */
    close(s_data);

  }
  
  
  /* The loop now exits due to timeout or transaction count being */
  /* reached */
  
  cpu_stop(xti_tcp_conn_rr_request->measure_cpu,&elapsed_time);
  
  if (timed_out) {
    /* we ended the test by time, which was at least 2 seconds */
    /* longer than we wanted to run. so, we want to subtract */
    /* PAD_TIME from the elapsed_time. */
    elapsed_time -= PAD_TIME;
  }
  /* send the results to the sender			*/
  
  if (debug) {
    fprintf(where,
	    "recv_xti_tcp_conn_rr: got %d transactions\n",
	    trans_received);
    fflush(where);
  }
  
  xti_tcp_conn_rr_results->bytes_received	= (trans_received * 
					   (xti_tcp_conn_rr_request->request_size + 
					    xti_tcp_conn_rr_request->response_size));
  xti_tcp_conn_rr_results->trans_received	= trans_received;
  xti_tcp_conn_rr_results->elapsed_time	= elapsed_time;
  if (xti_tcp_conn_rr_request->measure_cpu) {
    xti_tcp_conn_rr_results->cpu_util	= calc_cpu_util(elapsed_time);
  }
  
  if (debug) {
    fprintf(where,
	    "recv_xti_tcp_conn_rr: test complete, sending results.\n");
    fflush(where);
  }
  
  send_response();
  
}

void
print_xti_usage()
{

  fwrite(xti_usage, sizeof(char), strlen(xti_usage), stdout);
  exit(1);

}

void
scan_xti_args(int argc, char *argv[])
{
#define XTI_ARGS "Dhm:M:r:s:S:Vw:W:X:"
  extern int	optind, opterrs;  /* index of first unused arg 	*/
  extern char	*optarg;	  /* pointer to option string	*/
  
  int		c;
  
  char	
    arg1[BUFSIZ],  /* argument holders		*/
    arg2[BUFSIZ];
  
  if (no_control) {
    fprintf(where,
	    "The XTI tests do not know how to run with no control connection\n");
    exit(-1);
  }

  /* Go through all the command line arguments and break them */
  /* out. For those options that take two parms, specifying only */
  /* the first will set both to that value. Specifying only the */
  /* second will leave the first untouched. To change only the */
  /* first, use the form "first," (see the routine break_args.. */
  
  while ((c= getopt(argc, argv, XTI_ARGS)) != EOF) {
    switch (c) {
    case '?':	
    case 'h':
      print_xti_usage();
      exit(1);
    case 'D':
      /* set the TCP nodelay flag */
      loc_nodelay = 1;
      rem_nodelay = 1;
      break;
    case 's':
      /* set local socket sizes */
      break_args(optarg,arg1,arg2);
      if (arg1[0])
	lss_size = convert(arg1);
      if (arg2[0])
	lsr_size = convert(arg2);
      break;
    case 'S':
      /* set remote socket sizes */
      break_args(optarg,arg1,arg2);
      if (arg1[0])
	rss_size = convert(arg1);
      if (arg2[0])
	rsr_size = convert(arg2);
      break;
    case 'r':
      /* set the request/response sizes */
      break_args(optarg,arg1,arg2);
      if (arg1[0])
	req_size = convert(arg1);
      if (arg2[0])	
	rsp_size = convert(arg2);
      break;
    case 'm':
      /* set the send size */
      send_size = convert(optarg);
      break;
    case 'M':
      /* set the recv size */
      recv_size = convert(optarg);
      break;
    case 'W':
      /* set the "width" of the user space data */
      /* buffer. This will be the number of */
      /* send_size buffers malloc'd in the */
      /* *_STREAM test. It may be enhanced to set */
      /* both send and receive "widths" but for now */
      /* it is just the sending *_STREAM. */
      send_width = convert(optarg);
      break;
    case 'V' :
      /* we want to do copy avoidance and will set */
      /* it for everything, everywhere, if we really */
      /* can. of course, we don't know anything */
      /* about the remote... */
#ifdef SO_SND_COPYAVOID
      loc_sndavoid = 1;
#else
      loc_sndavoid = 0;
      printf("Local send copy avoidance not available.\n");
#endif
#ifdef SO_RCV_COPYAVOID
      loc_rcvavoid = 1;
#else
      loc_rcvavoid = 0;
      printf("Local recv copy avoidance not available.\n");
#endif
      rem_sndavoid = 1;
      rem_rcvavoid = 1;
      break;
    case 'X':
      /* set the xti device file name(s) */
      break_args(optarg,arg1,arg2);
      if (arg1[0])
	strcpy(loc_xti_device,arg1);
      if (arg2[0])
	strcpy(rem_xti_device,arg2);
      break;
    };
  }
}
#endif /* WANT_XTI */