C++程序  |  3554行  |  109.99 KB

#ifndef lint
char	nettest_sdp[]="\
@(#)nettest_sdp.c (c) Copyright 2007 Hewlett-Packard Co. Version 2.4.4";
#else
#define DIRTY
#define WANT_HISTOGRAM
#define WANT_INTERVALS
#endif /* lint */

/****************************************************************/
/*								*/
/*	nettest_sdp.c						*/
/*								*/
/*                                                              */
/*      scan_sdp_args()        get the sdp command line args  */
/*                                                              */
/*	the actual test routines...				*/
/*								*/
/*	send_sdp_stream()	perform a sdp stream test	*/
/*	recv_sdp_stream()					*/
/*	send_sdp_rr()		perform a sdp request/response	*/
/*	recv_sdp_rr()						*/
/*								*/
/*      relies on create_data_socket in nettest_bsd.c           */
/****************************************************************/

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

#if defined(WANT_SDP)
     
#include <sys/types.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#ifdef NOSTDLIBH
#include <malloc.h>
#else /* NOSTDLIBH */
#include <stdlib.h>
#endif /* NOSTDLIBH */

#if !defined(__VMS)
#include <sys/ipc.h>
#endif /* !defined(__VMS) */
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <netdb.h>

/* would seem that not all sdp.h files define a MSG_EOF, but that
   MSG_EOF can be the same as MSG_FIN so lets work with that
   assumption.  initial find by Jon Pedersen. raj 2006-02-01 */
#ifndef MSG_EOF
#ifdef MSG_FIN
#define MSG_EOF MSG_FIN
#else
#error Must have either MSG_EOF or MSG_FIN defined
#endif
#endif 

#include "netlib.h"
#include "netsh.h"
/* get some of the functions from nettest_bsd.c */
#include "nettest_bsd.h"
#include "nettest_sdp.h"

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

#ifdef WANT_FIRST_BURST
extern int first_burst_size;
#endif /* WANT_FIRST_BURST */



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

static int	
  msg_count = 0,	/* number of messages to transmit on association */
  non_block = 0,	/* default to blocking sockets */
  num_associations = 1; /* number of associations on the endpoint */

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

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


char sdp_usage[] = "\n\
Usage: netperf [global options] -- [test options] \n\
\n\
SDP Sockets Test Options:\n\
    -b number         Send number requests at the start of _RR tests\n\
    -D [L][,R]        Set SDP_NODELAY locally and/or remotely\n\
    -h                Display this text\n\
    -H name,fam       Use name (or IP) and family as target of data connection\n\
    -L name,fam       Use name (or IP) and family as source of data connextion\n\
    -m bytes          Set the size of each sent message\n\
    -M bytes          Set the size of each received messages\n\
    -P local[,remote] Set the local/remote port for the data socket\n\
    -r req,[rsp]      Set request/response sizes (_RR tests)\n\
    -s send[,recv]    Set local socket send/recv buffer sizes\n\
    -S send[,recv]    Set remote socket send/recv buffer sizes\n\
    -V 		      Enable copy avoidance if supported\n\
    -4                Use AF_INET (eg IPv4) on both ends of the data conn\n\
    -6                Use AF_INET6 (eg IPv6) on both ends of the data conn\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 sdp */
 /* 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 */
static
void
get_sdp_info(int socket, int * mss)
{

#ifdef TCP_MAXSEG
  netperf_socklen_t sock_opt_len;

  sock_opt_len = sizeof(netperf_socklen_t);
  if (getsockopt(socket,
		 getprotobyname("tcp")->p_proto,	
		 TCP_MAXSEG,
		 (char *)mss,
		 &sock_opt_len) == SOCKET_ERROR) {
    fprintf(where,
	    "netperf: get_sdp_info: getsockopt TCP_MAXSEG: errno %d\n",
	    errno);
    fflush(where);
    *mss = -1;
  }
#else
  *mss = -1;
#endif /* TCP_MAXSEG */

}

void 
send_sdp_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 %s\n";
  
  char *tput_fmt_1 =
    "%6d %6d %6d    %-6.2f   %7.2f   %s\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 %s\n";

  char *cpu_fmt_1 =
    "%6d %6d %6d    %-6.2f     %7.2f   %-6.2f   %-6.2f   %-6.3f  %-6.3f %s\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;
  
  /* 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. */
  
  struct ring_elt *send_ring;
  
  int len;
  unsigned int nummessages = 0;
  SOCKET send_socket;
  int bytes_remaining;
  int sdp_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 */

  unsigned long long local_bytes_sent = 0;
  double	bytes_sent = 0.0;
  
  float	local_cpu_utilization;
  float	local_service_demand;
  float	remote_cpu_utilization;
  float	remote_service_demand;

  double	thruput;
  
  struct addrinfo *remote_res;
  struct addrinfo *local_res;
  
  struct	sdp_stream_request_struct	*sdp_stream_request;
  struct	sdp_stream_response_struct	*sdp_stream_response;
  struct	sdp_stream_results_struct	*sdp_stream_result;
  
  sdp_stream_request  = 
    (struct sdp_stream_request_struct *)netperf_request.content.test_specific_data;
  sdp_stream_response =
    (struct sdp_stream_response_struct *)netperf_response.content.test_specific_data;
  sdp_stream_result   = 
    (struct sdp_stream_results_struct *)netperf_response.content.test_specific_data;
  
#ifdef WANT_HISTOGRAM
  if (verbosity > 1) {
    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. */
  
  /* complete_addrinfos will either succede or exit the process */
  complete_addrinfos(&remote_res,
		     &local_res,
		     remote_host,
		     SOCK_STREAM,
		     IPPROTO_TCP,
		     0);
  
  if ( print_headers ) {
    print_top_test_header("SDP STREAM TEST",local_res,remote_res);
  }

  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                        */
    /* fake things out by changing local_res->ai_family to AF_INET_SDP */
    local_res->ai_family = AF_INET_SDP;
    local_res->ai_protocol = 0;
    send_socket = create_data_socket(local_res);
    
    if (send_socket == INVALID_SOCKET){
      perror("netperf: send_sdp_stream: sdp stream data socket");
      exit(1);
    }
    
    if (debug) {
      fprintf(where,"send_sdp_stream: send_socket obtained...\n");
    }
    
    /* 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);
    }
    
    if (!no_control) {
      /* 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_SDP_STREAM;
      sdp_stream_request->send_buf_size	=	rss_size_req;
      sdp_stream_request->recv_buf_size	=	rsr_size_req;
      sdp_stream_request->receive_size	=	recv_size;
      sdp_stream_request->no_delay	=	rem_nodelay;
      sdp_stream_request->recv_alignment	=	remote_recv_align;
      sdp_stream_request->recv_offset	=	remote_recv_offset;
      sdp_stream_request->measure_cpu	=	remote_cpu_usage;
      sdp_stream_request->cpu_rate	=	remote_cpu_rate;
      if (test_time) {
	sdp_stream_request->test_length	=	test_time;
      }
      else {
	sdp_stream_request->test_length	=	test_bytes;
      }
      sdp_stream_request->so_rcvavoid	=	rem_rcvavoid;
      sdp_stream_request->so_sndavoid	=	rem_sndavoid;
#ifdef DIRTY
      sdp_stream_request->dirty_count     =       rem_dirty_count;
      sdp_stream_request->clean_count     =       rem_clean_count;
#endif /* DIRTY */
      sdp_stream_request->port            =    atoi(remote_data_port);
      sdp_stream_request->ipfamily = af_to_nf(remote_res->ai_family);
      if (debug > 1) {
	fprintf(where,
		"netperf: send_sdp_stream: requesting SDP 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 SDP
         tests.  */
    
      recv_response();
    
      if (!netperf_response.content.serv_errno) {
	if (debug)
	  fprintf(where,"remote listen done.\n");
	rsr_size	      =	sdp_stream_response->recv_buf_size;
	rss_size	      =	sdp_stream_response->send_buf_size;
	rem_nodelay     =	sdp_stream_response->no_delay;
	remote_cpu_usage=	sdp_stream_response->measure_cpu;
	remote_cpu_rate = sdp_stream_response->cpu_rate;
	
	/* we have to make sure that the server port number is in
	   network order */
	set_port_number(remote_res,
			(short)sdp_stream_response->data_port_number);
	
	rem_rcvavoid	= sdp_stream_response->so_rcvavoid;
	rem_sndavoid	= sdp_stream_response->so_sndavoid;
      }
      else {
	Set_errno(netperf_response.content.serv_errno);
	fprintf(where,
		"netperf: remote error %d",
		netperf_response.content.serv_errno);
	perror("");
	fflush(where);
	
	exit(1);
      }
    }

#ifdef WANT_DEMO
    DEMO_STREAM_SETUP(lss_size,rsr_size)
#endif

    /*Connect up to the remote port on the data socket  */
    if (connect(send_socket, 
		remote_res->ai_addr,
		remote_res->ai_addrlen) == INVALID_SOCKET){
      perror("netperf: send_sdp_stream: data socket connect failed");
      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);

    /* we only start the interval timer if we are using the
       timer-timed intervals rather than the sit and spin ones. raj
       2006-02-06 */    
#if defined(WANT_INTERVALS)
    INTERVALS_INIT();
#endif /* WANT_INTERVALS */

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

#ifdef WANT_DEMO
      if (demo_mode) {
	HIST_timestamp(demo_one_ptr);
      }
#endif
      

    /* 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
      access_buffer(send_ring->buffer_ptr,
		    send_size,
		    loc_dirty_count,
		    loc_clean_count);
#endif /* DIRTY */
      
#ifdef WANT_HISTOGRAM
      if (verbosity > 1) {
	/* timestamp just before we go into send and then again just
	 after we come out raj 8/94 */
	/* but lets only do this if there is going to be a histogram
	   displayed */
	HIST_timestamp(&time_one);
      }
#endif /* WANT_HISTOGRAM */

      if((len=send(send_socket,
		   send_ring->buffer_ptr,
		   send_size,
		   0)) != send_size) {
      if ((len >=0) || SOCKET_EINTR(len)) {
	    /* the test was interrupted, must be the end of test */
	    break;
	  }
	perror("netperf: data send error");
	printf("len was %d\n",len);
	exit(1);
      }

      local_bytes_sent += send_size;

#ifdef WANT_HISTOGRAM
      if (verbosity > 1) {
	/* 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_DEMO
      DEMO_STREAM_INTERVAL(send_size)
#endif 

#if defined(WANT_INTERVALS)
      INTERVALS_WAIT();
#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 SDP maximum segment_size was (if possible) */
    if (verbosity > 1) {
      sdp_mss = -1;
      get_sdp_info(send_socket,&sdp_mss);
    }
    
    if (shutdown(send_socket,SHUT_WR) == SOCKET_ERROR) {
      perror("netperf: cannot shutdown sdp stream socket");
      exit(1);
    }
    
    /* hang a recv() off the socket to block until the remote has */
    /* brought all the data up into the application. it will do a */
    /* shutdown to cause a FIN to be sent our way. We will assume that */
    /* any exit from the recv() call is good... raj 4/93 */
    
    recv(send_socket, send_ring->buffer_ptr, send_size, 0);
    
    /* 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? */
    
    /* we are finished with the socket, so close it to prevent hitting */
    /* the limit on maximum open files. */

    close(send_socket);

    if (!no_control) {
      /* 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);
	fprintf(where,
		"netperf: remote error %d",
		netperf_response.content.serv_errno);
	perror("");
	fflush(where);
	
	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
	 SDP 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	= ntohd(sdp_stream_result->bytes_received);
    }
    else {
      bytes_sent = (double)local_bytes_sent;
    }

    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	= (float) -1.0;
	local_service_demand	= (float) -1.0;
      }
      
      if (remote_cpu_usage) {
	
	remote_cpu_utilization	= sdp_stream_result->cpu_util;
	remote_service_demand	= calc_service_demand(bytes_sent,
						      0.0,
						      remote_cpu_utilization,
						      sdp_stream_result->num_cpus);
      }
      else {
	remote_cpu_utilization = (float) -1.0;
	remote_service_demand  = (float) -1.0;
      }
    }    
    else {
      /* we were not measuring cpu, for the confidence stuff, we */
      /* should make it -1.0 */
      local_cpu_utilization	= (float) -1.0;
      local_service_demand	= (float) -1.0;
      remote_cpu_utilization = (float) -1.0;
      remote_service_demand  = (float) -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(sdp_stream_result->cpu_method);
    
    switch (verbosity) {
    case 0:
      if (local_cpu_usage) {
	fprintf(where,
		cpu_fmt_0,
		local_service_demand,
		local_cpu_method,
		((print_headers) || 
		 (result_brand == NULL)) ? "" : result_brand);
      }
      else {
	fprintf(where,
		cpu_fmt_0,
		remote_service_demand,
		remote_cpu_method,
		((print_headers) || 
		 (result_brand == NULL)) ? "" : result_brand);
      }
      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 */
	      ((print_headers) || 
	       (result_brand == NULL)) ? "" : result_brand);
      break;
    }
  }
  else {
    /* The tester did not wish to measure service demand. */
    
    switch (verbosity) {
    case 0:
      fprintf(where,
	      tput_fmt_0,
	      thruput,
	      ((print_headers) || 
	       (result_brand == NULL)) ? "" : result_brand);
      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 */
	      ((print_headers) || 
	       (result_brand == NULL)) ? "" : result_brand);
      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 */
    /* SDP 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)sdp_stream_result->recv_calls,
	    sdp_stream_result->recv_calls);
    fprintf(where,
	    ksink_fmt2,
	    sdp_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 routine implements the netperf-side SDP unidirectional data
   transfer test (a.k.a. stream) for the sockets interface where the
   data flow is from the netserver to the netperf.  It receives its
   parameters via global variables from the shell and writes its
   output to the standard output. */


void 
send_sdp_maerts(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 %s\n";
  
  char *tput_fmt_1 =
    "%6d %6d %6d    %-6.2f   %7.2f   \n %s";
  
  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 %s\n";

  char *cpu_fmt_1 =
    "%6d %6d %6d    %-6.2f     %7.2f   %-6.2f   %-6.2f   %-6.3f  %-6.3f %s\n";
  
  char *ksink_fmt = "\n\
Alignment      Offset         %-8.8s %-8.8s    Recvs   %-8.8s Sends\n\
Local  Remote  Local  Remote  Xfered   Per                 Per\n\
Recv   Send    Recv   Send             Recv (avg)          Send (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;
  
  /* what we want is to have a buffer space that is at least one */
  /* recv-size greater than our recv 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. */
  
  struct ring_elt *recv_ring;
  
  int len;
  unsigned int nummessages = 0;
  SOCKET recv_socket;
  int bytes_remaining;
  int sdp_mss = -1;  /* possibly uninitialized on printf far below */

  /* with links like fddi, one can recv > 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 = 0.0;
  unsigned long long local_bytes_recvd = 0;

  float	local_cpu_utilization;
  float	local_service_demand;
  float	remote_cpu_utilization;
  float	remote_service_demand;

  double	thruput;
  
  struct addrinfo *remote_res;
  struct addrinfo *local_res;
  
  struct	sdp_maerts_request_struct	*sdp_maerts_request;
  struct	sdp_maerts_response_struct	*sdp_maerts_response;
  struct	sdp_maerts_results_struct	*sdp_maerts_result;
  
  sdp_maerts_request  = 
    (struct sdp_maerts_request_struct *)netperf_request.content.test_specific_data;
  sdp_maerts_response =
    (struct sdp_maerts_response_struct *)netperf_response.content.test_specific_data;
  sdp_maerts_result   = 
    (struct sdp_maerts_results_struct *)netperf_response.content.test_specific_data;
  
#ifdef WANT_HISTOGRAM
  if (verbosity > 1) {
    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. */
  
  complete_addrinfos(&remote_res,
		     &local_res,
		     remote_host,
		     SOCK_STREAM,
		     IPPROTO_TCP,
		     0);
  
  if ( print_headers ) {
    print_top_test_header("SDP MAERTS TEST",local_res,remote_res);
  }

  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_sent     =	0.0;
    times_up       = 	0;
    
    /*set up the data socket                        */
    /* fake things out by changing local_res->ai_family to AF_INET_SDP */
    local_res->ai_family = AF_INET_SDP;
    local_res->ai_protocol = 0;
    recv_socket = create_data_socket(local_res);
    
    if (recv_socket == INVALID_SOCKET){
      perror("netperf: send_sdp_maerts: sdp stream data socket");
      exit(1);
    }
    
    if (debug) {
      fprintf(where,"send_sdp_maerts: recv_socket obtained...\n");
    }

    /* 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 recv */
    /* 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 recv size to 4KB - no */
    /* particular reason, just arbitrary... */
    if (recv_size == 0) {
      if (lsr_size > 0) {
	recv_size = lsr_size;
      }
      else {
	recv_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 recv-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 */
    /* recv_size is bigger than the socket size, so we must check... the */
    /* user may have wanted to explicitly set the "width" of our recv */
    /* buffers, we should respect that wish... */
    if (recv_width == 0) {
      recv_width = (lsr_size/recv_size) + 1;
      if (recv_width == 1) recv_width++;
    }
    
    if (recv_ring == NULL) {
      /* only allocate the recv 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 */
      recv_ring = allocate_buffer_ring(recv_width,
				       recv_size,
				       local_recv_align,
				       local_recv_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);
    }
    
    if (!no_control) {
      /* 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_SDP_MAERTS;
      sdp_maerts_request->send_buf_size	=	rss_size_req;
      sdp_maerts_request->recv_buf_size	=	rsr_size_req;
      sdp_maerts_request->send_size	=	send_size;
      sdp_maerts_request->no_delay	=	rem_nodelay;
      sdp_maerts_request->send_alignment	=	remote_send_align;
      sdp_maerts_request->send_offset	=	remote_send_offset;
      sdp_maerts_request->measure_cpu	=	remote_cpu_usage;
      sdp_maerts_request->cpu_rate	=	remote_cpu_rate;
      if (test_time) {
	sdp_maerts_request->test_length	=	test_time;
      }
      else {
	sdp_maerts_request->test_length	=	test_bytes;
      }
      sdp_maerts_request->so_rcvavoid	=	rem_rcvavoid;
      sdp_maerts_request->so_sndavoid	=	rem_sndavoid;
#ifdef DIRTY
      sdp_maerts_request->dirty_count       =       rem_dirty_count;
      sdp_maerts_request->clean_count       =       rem_clean_count;
#endif /* DIRTY */
      sdp_maerts_request->port            = atoi(remote_data_port);
      sdp_maerts_request->ipfamily        = af_to_nf(remote_res->ai_family);
      if (debug > 1) {
	fprintf(where,
		"netperf: send_sdp_maerts: requesting SDP maerts 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 SDP
	 tests.  */
      
      recv_response();
    
      if (!netperf_response.content.serv_errno) {
	if (debug)
	  fprintf(where,"remote listen done.\n");
	rsr_size	=	sdp_maerts_response->recv_buf_size;
	rss_size	=	sdp_maerts_response->send_buf_size;
	rem_nodelay     =	sdp_maerts_response->no_delay;
	remote_cpu_usage=	sdp_maerts_response->measure_cpu;
	remote_cpu_rate = sdp_maerts_response->cpu_rate;
	send_size       = sdp_maerts_response->send_size;
	
	/* we have to make sure that the server port number is in
	 network order */
      set_port_number(remote_res,
		      (short)sdp_maerts_response->data_port_number);
      rem_rcvavoid	= sdp_maerts_response->so_rcvavoid;
      rem_sndavoid	= sdp_maerts_response->so_sndavoid;
      }
      else {
	Set_errno(netperf_response.content.serv_errno);
	fprintf(where,
		"netperf: remote error %d",
		netperf_response.content.serv_errno);
	perror("");
	fflush(where);
	
	exit(1);
      }
    }

#ifdef WANT_DEMO
    DEMO_STREAM_SETUP(lsr_size,rss_size)
#endif

    /*Connect up to the remote port on the data socket  */
    if (connect(recv_socket, 
		remote_res->ai_addr,
		remote_res->ai_addrlen) == INVALID_SOCKET){
      perror("netperf: send_sdp_maerts: data socket connect failed");
      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 maerts 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 */
      if (!no_control) {
	/* this is a netperf to netserver test, netserver will close
	   to tell us the test is over, so use PAD_TIME to avoid
	   causing the netserver fits. */
	start_timer(test_time + PAD_TIME);
      }
      else {
	/* this is a netperf to data source test, no PAD_TIME */
	start_timer(test_time);
      }
    }
    else {
      /* The tester wanted to recv a number of bytes. we don't do that 
	 in a SDP_MAERTS test. sorry. raj 2002-06-21 */
      printf("netperf: send_sdp_maerts: test must be timed\n");
      exit(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
    INTERVALS_INIT();
#endif /* WANT_INTERVALS */

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

#ifdef WANT_DEMO
    if (demo_mode) {
      HIST_timestamp(demo_one_ptr);
    }
#endif

    /* the test will continue until we either get a zero-byte recv()
       on the socket or our failsafe timer expires. most of the time
       we trust that we get a zero-byte recieve from the socket. raj
       2002-06-21 */

#ifdef WANT_HISTOGRAM
    if (verbosity > 1) {
      /* timestamp just before we go into recv and then again just
	 after we come out raj 8/94 */
      /* but only if we are actually going to display a histogram. raj
	 2006-02-07 */
      HIST_timestamp(&time_one);
    }
#endif /* WANT_HISTOGRAM */
    
    while ((!times_up) && (len=recv(recv_socket,
				    recv_ring->buffer_ptr,
				    recv_size,
				    0)) > 0 ) {

#ifdef WANT_HISTOGRAM
      if (verbosity > 1) {
	/* timestamp the exit from the recv call and update the histogram */
	HIST_timestamp(&time_two);
	HIST_add(time_hist,delta_micro(&time_one,&time_two));
      }
#endif /* WANT_HISTOGRAM */      

#ifdef DIRTY
      access_buffer(recv_ring->buffer_ptr,
		    recv_size,
		    loc_dirty_count,
		    loc_clean_count);
#endif /* DIRTY */

#ifdef WANT_DEMO
      DEMO_STREAM_INTERVAL(len);
#endif

#ifdef WANT_INTERVALS      
      INTERVALS_WAIT();
#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 recv width, and use that to calculate the offset to add */
      /* to the base pointer. */
      nummessages++;          
      recv_ring = recv_ring->next;
      if (bytes_remaining) {
	bytes_remaining -= len;
      }

      local_bytes_recvd += len;

#ifdef WANT_HISTOGRAM
      if (verbosity > 1) {
	/* make sure we timestamp just before we go into recv  */
	/* raj 2004-06-15 */
	HIST_timestamp(&time_one);
      }
#endif /* WANT_HISTOGRAM */
    
    }

    /* an EINTR is to be expected when this is a no_control test */
    if (((len < 0) || SOCKET_EINTR(len)) && (!no_control)) {
      perror("send_sdp_maerts: data recv error");
      printf("len was %d\n",len);
      exit(1);
    }
    
    /* if we get here, it must mean we had a recv return of 0 before
       the watchdog timer expired, or the watchdog timer expired and
       this was a no_control test */

    /* The test is over. Flush the buffers to the remote end. We do a
       graceful release to tell the  remote we have all the data. */  

    /* but first, if the verbosity is greater than 1, find-out what */
    /* the SDP maximum segment_size was (if possible) */
    if (verbosity > 1) {
      sdp_mss = -1;
      get_sdp_info(recv_socket,&sdp_mss);
    }
    
    if (shutdown(recv_socket,SHUT_WR) == SOCKET_ERROR) {
      perror("netperf: cannot shutdown sdp maerts socket");
      exit(1);
    }

    stop_timer();
    
    /* this call will always give us the local 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? */
    
    /* we are finished with the socket, so close it to prevent hitting */
    /* the limit on maximum open files. */

    close(recv_socket);

    if (!no_control) {
      /* 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);
	fprintf(where,
		"netperf: remote error %d",
		netperf_response.content.serv_errno);
	perror("");
	fflush(where);
	
	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
	 SDP maerts 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 recv_size, so we
	 really didn't recv what he asked for ;-) */
    
      bytes_sent	= ntohd(sdp_maerts_result->bytes_sent);
    }
    else {
      bytes_sent = (double)local_bytes_recvd;
    }


    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	= (float) -1.0;
	local_service_demand	= (float) -1.0;
      }
      
      if (remote_cpu_usage) {
	
	remote_cpu_utilization	= sdp_maerts_result->cpu_util;
	remote_service_demand	= calc_service_demand(bytes_sent,
						      0.0,
						      remote_cpu_utilization,
						      sdp_maerts_result->num_cpus);
      }
      else {
	remote_cpu_utilization = (float) -1.0;
	remote_service_demand  = (float) -1.0;
      }
    }    
    else {
      /* we were not measuring cpu, for the confidence stuff, we */
      /* should make it -1.0 */
      local_cpu_utilization	= (float) -1.0;
      local_service_demand	= (float) -1.0;
      remote_cpu_utilization = (float) -1.0;
      remote_service_demand  = (float) -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(sdp_maerts_result->cpu_method);
    
    switch (verbosity) {
    case 0:
      if (local_cpu_usage) {
	fprintf(where,
		cpu_fmt_0,
		local_service_demand,
		local_cpu_method,
		((print_headers) || 
		 (result_brand == NULL)) ? "" : result_brand);
      }
      else {
	fprintf(where,
		cpu_fmt_0,
		remote_service_demand,
		remote_cpu_method,
		((print_headers) || 
		 (result_brand == NULL)) ? "" : result_brand);
      }
      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 recvs */
	      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 */
	      ((print_headers) || 
	       (result_brand == NULL)) ? "" : result_brand);
      break;
    }
  }
  else {
    /* The tester did not wish to measure service demand. */
    
    switch (verbosity) {
    case 0:
      fprintf(where,
	      tput_fmt_0,
	      thruput,
	      ((print_headers) || 
	       (result_brand == NULL)) ? "" : result_brand);
      break;
    case 1:
    case 2:
      if (print_headers) {
	fprintf(where,tput_title,format_units());
      }
      fprintf(where,
	      tput_fmt_1,		/* the format string */
	      lsr_size, 		/* local recvbuf size */
	      rss_size, 		/* remot sendbuf size */
	      send_size,		/* how large were the recvs */
	      elapsed_time, 		/* how long did it take */
	      thruput,                  /* how fast did it go */
	      ((print_headers) || 
	       (result_brand == NULL)) ? "" : result_brand);
      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 */
    /* SDP 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_recv_align,
	    remote_recv_align,
	    local_recv_offset,
	    remote_recv_offset,
	    bytes_sent,
	    bytes_sent / (double)nummessages,
	    nummessages,
	    bytes_sent / (double)sdp_maerts_result->send_calls,
	    sdp_maerts_result->send_calls);
    fprintf(where,
	    ksink_fmt2,
	    sdp_mss);
    fflush(where);
#ifdef WANT_HISTOGRAM
    fprintf(where,"\n\nHistogram of time spent in recv() call.\n");
    fflush(where);
    HIST_report(time_hist);
#endif /* WANT_HISTOGRAM */
  }
  
}
/* This is the server-side routine for the sdp stream test. It is */
/* implemented as one routine. I could break things-out somewhat, but */
/* didn't feel it was necessary. */

void
recv_sdp_stream()
{
  
  struct sockaddr_in myaddr_in, peeraddr_in;
  SOCKET s_listen,s_data;
  netperf_socklen_t addrlen;
  int	len;
  unsigned int	receive_calls;
  float	elapsed_time;
  double   bytes_received;
  
  struct ring_elt *recv_ring;

  struct addrinfo *local_res;
  char local_name[BUFSIZ];
  char port_buffer[PORTBUFSIZE];

#ifdef DO_SELECT
  fd_set readfds;
  struct timeval timeout;
#endif /* DO_SELECT */

  struct	sdp_stream_request_struct	*sdp_stream_request;
  struct	sdp_stream_response_struct	*sdp_stream_response;
  struct	sdp_stream_results_struct	*sdp_stream_results;
  
#ifdef DO_SELECT
  FD_ZERO(&readfds);
  timeout.tv_sec = 1;
  timeout.tv_usec = 0;
#endif /* DO_SELECT */

  sdp_stream_request	= 
    (struct sdp_stream_request_struct *)netperf_request.content.test_specific_data;
  sdp_stream_response	= 
    (struct sdp_stream_response_struct *)netperf_response.content.test_specific_data;
  sdp_stream_results	= 
    (struct sdp_stream_results_struct *)netperf_response.content.test_specific_data;
  
  if (debug) {
    fprintf(where,"netserver: recv_sdp_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_sdp_stream: setting the response type...\n");
    fflush(where);
  }
  
  netperf_response.content.response_type = SDP_STREAM_RESPONSE;
  
  if (debug) {
    fprintf(where,"recv_sdp_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_sdp_stream: requested alignment of %d\n",
	    sdp_stream_request->recv_alignment);
    fflush(where);
  }

  /* create_data_socket 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_req = sdp_stream_request->send_buf_size;
  lsr_size_req = sdp_stream_request->recv_buf_size;
  loc_nodelay  = sdp_stream_request->no_delay;
  loc_rcvavoid = sdp_stream_request->so_rcvavoid;
  loc_sndavoid = sdp_stream_request->so_sndavoid;

  set_hostname_and_port(local_name,
			port_buffer,
			nf_to_af(sdp_stream_request->ipfamily),
			sdp_stream_request->port);

  local_res = complete_addrinfo(local_name,
				local_name,
				port_buffer,
				nf_to_af(sdp_stream_request->ipfamily),
				SOCK_STREAM,
				IPPROTO_TCP,
				0);

  /* fake things out by changing local_res->ai_family to AF_INET_SDP */
  local_res->ai_family = AF_INET_SDP;
  local_res->ai_protocol = 0;
  s_listen = create_data_socket(local_res);
  
  if (s_listen == INVALID_SOCKET) {
    netperf_response.content.serv_errno = errno;
    send_response();
    exit(1);
  }
  
#ifdef WIN32
  /* The test timer can fire during operations on the listening socket,
     so to make the start_timer below work we have to move
     it to close s_listen while we are blocked on accept. */
  win_kludge_socket2 = s_listen;
#endif
  
  /* what sort of sizes did we end-up with? */
  if (sdp_stream_request->receive_size == 0) {
    if (lsr_size > 0) {
      recv_size = lsr_size;
    }
    else {
      recv_size = 4096;
    }
  }
  else {
    recv_size = sdp_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,
				   sdp_stream_request->recv_alignment,
				   sdp_stream_request->recv_offset);

  if (debug) {
    fprintf(where,"recv_sdp_stream: receive alignment and offset set...\n");
    fflush(where);
  }
  
  /* 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();
    
    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();
    
    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. */
  
  sdp_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. */
  
  sdp_stream_response->cpu_rate = (float)0.0; 	/* assume no cpu */
  if (sdp_stream_request->measure_cpu) {
    sdp_stream_response->measure_cpu = 1;
    sdp_stream_response->cpu_rate = 
      calibrate_local_cpu(sdp_stream_request->cpu_rate);
  }
  else {
    sdp_stream_response->measure_cpu = 0;
  }
  
  /* before we send the response back to the initiator, pull some of */
  /* the socket parms from the globals */
  sdp_stream_response->send_buf_size = lss_size;
  sdp_stream_response->recv_buf_size = lsr_size;
  sdp_stream_response->no_delay = loc_nodelay;
  sdp_stream_response->so_rcvavoid = loc_rcvavoid;
  sdp_stream_response->so_sndavoid = loc_sndavoid;
  sdp_stream_response->receive_size = recv_size;

  send_response();
  
  addrlen = sizeof(peeraddr_in);
  
  if ((s_data=accept(s_listen,
		     (struct sockaddr *)&peeraddr_in,
		     &addrlen)) == INVALID_SOCKET) {
    /* Let's just punt. The remote will be given some information */
    close(s_listen);
    exit(1);
  }

#ifdef KLUDGE_SOCKET_OPTIONS
  /* this is for those systems which *INCORRECTLY* fail to pass */
  /* attributes across an accept() call. Including this goes against */
  /* my better judgement :( raj 11/95 */

  kludge_socket_options(s_data);

#endif /* KLUDGE_SOCKET_OPTIONS */
  
  /* Now it's time to start receiving data on the connection. We will */
  /* first grab the apropriate counters and then start grabbing. */
  
  cpu_start(sdp_stream_request->measure_cpu);
  
  /* The loop will exit when the sender does a shutdown, which will */
  /* return a length of zero   */
  
  /* there used to be an #ifdef DIRTY call to access_buffer() here,
     but we have switched from accessing the buffer before the recv()
     call to accessing the buffer after the recv() call.  The
     accessing before was, IIRC, related to having dirty data when
     doing page-flipping copy avoidance. */

  bytes_received = 0;
  receive_calls  = 0;

  while ((len = recv(s_data, recv_ring->buffer_ptr, recv_size, 0)) != 0) {
    if (len == SOCKET_ERROR )
	{
      netperf_response.content.serv_errno = errno;
      send_response();
      exit(1);
    }
    bytes_received += len;
    receive_calls++;

#ifdef DIRTY
    /* we access the buffer after the recv() call now, rather than before */
    access_buffer(recv_ring->buffer_ptr,
		  recv_size,
		  sdp_stream_request->dirty_count,
		  sdp_stream_request->clean_count);
#endif /* DIRTY */


    /* move to the next buffer in the recv_ring */
    recv_ring = recv_ring->next;

#ifdef PAUSE
    sleep(1);
#endif /* PAUSE */

#ifdef DO_SELECT
	FD_SET(s_data,&readfds);
	select(s_data+1,&readfds,NULL,NULL,&timeout);
#endif /* DO_SELECT */

  }
  
  /* perform a shutdown to signal the sender that */
  /* we have received all the data sent. raj 4/93 */

  if (shutdown(s_data,SHUT_WR) == SOCKET_ERROR) {
      netperf_response.content.serv_errno = errno;
      send_response();
      exit(1);
    }
  
  cpu_stop(sdp_stream_request->measure_cpu,&elapsed_time);
  
  /* send the results to the sender			*/
  
  if (debug) {
    fprintf(where,
	    "recv_sdp_stream: got %g bytes\n",
	    bytes_received);
    fprintf(where,
	    "recv_sdp_stream: got %d recvs\n",
	    receive_calls);
    fflush(where);
  }
  
  sdp_stream_results->bytes_received	= htond(bytes_received);
  sdp_stream_results->elapsed_time	= elapsed_time;
  sdp_stream_results->recv_calls	= receive_calls;
  
  sdp_stream_results->cpu_method = cpu_method;
  sdp_stream_results->num_cpus   = lib_num_loc_cpus;
  
  if (sdp_stream_request->measure_cpu) {
    sdp_stream_results->cpu_util	= calc_cpu_util(0.0);
  };
  
  if (debug) {
    fprintf(where,
	    "recv_sdp_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);
  }

  send_response();

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

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

void
recv_sdp_maerts()
{
  
  struct sockaddr_in myaddr_in, peeraddr_in;
  struct addrinfo *local_res;
  char  local_name[BUFSIZ];
  char  port_buffer[PORTBUFSIZE];

  SOCKET	s_listen,s_data;
  netperf_socklen_t 	addrlen;
  int	len;
  unsigned int	send_calls;
  float	elapsed_time;
  double   bytes_sent = 0.0 ;
  
  struct ring_elt *send_ring;

  struct	sdp_maerts_request_struct	*sdp_maerts_request;
  struct	sdp_maerts_response_struct	*sdp_maerts_response;
  struct	sdp_maerts_results_struct	*sdp_maerts_results;
  
  sdp_maerts_request	= 
    (struct sdp_maerts_request_struct *)netperf_request.content.test_specific_data;
  sdp_maerts_response	= 
    (struct sdp_maerts_response_struct *)netperf_response.content.test_specific_data;
  sdp_maerts_results	= 
    (struct sdp_maerts_results_struct *)netperf_response.content.test_specific_data;
  
  if (debug) {
    fprintf(where,"netserver: recv_sdp_maerts: 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_sdp_maerts: setting the response type...\n");
    fflush(where);
  }
  
  netperf_response.content.response_type = SDP_MAERTS_RESPONSE;
  
  if (debug) {
    fprintf(where,"recv_sdp_maerts: 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_sdp_maerts: requested alignment of %d\n",
	    sdp_maerts_request->send_alignment);
    fflush(where);
  }

  /* Grab a socket to listen on, and then listen on it. */
  
  if (debug) {
    fprintf(where,"recv_sdp_maerts: grabbing a socket...\n");
    fflush(where);
  }
  
  /* create_data_socket 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_req = sdp_maerts_request->send_buf_size;
  lsr_size_req = sdp_maerts_request->recv_buf_size;
  loc_nodelay = sdp_maerts_request->no_delay;
  loc_rcvavoid = sdp_maerts_request->so_rcvavoid;
  loc_sndavoid = sdp_maerts_request->so_sndavoid;

  set_hostname_and_port(local_name,
			port_buffer,
			nf_to_af(sdp_maerts_request->ipfamily),
			sdp_maerts_request->port);

  local_res = complete_addrinfo(local_name,
				local_name,
				port_buffer,
				nf_to_af(sdp_maerts_request->ipfamily),
				SOCK_STREAM,
				IPPROTO_TCP,
				0);

  /* fake things out by changing local_res->ai_family to AF_INET_SDP */
  local_res->ai_family = AF_INET_SDP;
  local_res->ai_protocol = 0;
  s_listen = create_data_socket(local_res);
  
  if (s_listen == INVALID_SOCKET) {
    netperf_response.content.serv_errno = errno;
    send_response();
    exit(1);
  }
  
#ifdef WIN32
  /* The test timer can fire during operations on the listening socket,
     so to make the start_timer below work we have to move
     it to close s_listen while we are blocked on accept. */
  win_kludge_socket2 = s_listen;
#endif

  
  /* what sort of sizes did we end-up with? */
  if (sdp_maerts_request->send_size == 0) {
    if (lss_size > 0) {
      send_size = lss_size;
    }
    else {
      send_size = 4096;
    }
  }
  else {
    send_size = sdp_maerts_request->send_size;
  }
  
  /* we want to set-up our recv_ring in a manner analagous to what we */
  /* do on the recving 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 (send_width == 0) {
    send_width = (lsr_size/send_size) + 1;
    if (send_width == 1) send_width++;
  }

  send_ring = allocate_buffer_ring(send_width,
				   send_size,
				   sdp_maerts_request->send_alignment,
				   sdp_maerts_request->send_offset);

  if (debug) {
    fprintf(where,"recv_sdp_maerts: receive alignment and offset set...\n");
    fflush(where);
  }
  
  /* 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();
    
    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();
    
    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. */
  
  sdp_maerts_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. */
  
  sdp_maerts_response->cpu_rate = (float)0.0; 	/* assume no cpu */
  if (sdp_maerts_request->measure_cpu) {
    sdp_maerts_response->measure_cpu = 1;
    sdp_maerts_response->cpu_rate = 
      calibrate_local_cpu(sdp_maerts_request->cpu_rate);
  }
  else {
    sdp_maerts_response->measure_cpu = 0;
  }
  
  /* before we send the response back to the initiator, pull some of */
  /* the socket parms from the globals */
  sdp_maerts_response->send_buf_size = lss_size;
  sdp_maerts_response->recv_buf_size = lsr_size;
  sdp_maerts_response->no_delay = loc_nodelay;
  sdp_maerts_response->so_rcvavoid = loc_rcvavoid;
  sdp_maerts_response->so_sndavoid = loc_sndavoid;
  sdp_maerts_response->send_size = send_size;

  send_response();
  
  addrlen = sizeof(peeraddr_in);

  /* we will start the timer before the accept() to be somewhat
     analagous to the starting of the timer before the connect() call
     in the SDP_STREAM test. raj 2002-06-21 */

  start_timer(sdp_maerts_request->test_length);

  /* Now it's time to start receiving data on the connection. We will
     first grab the apropriate counters and then start grabbing. */
  
  cpu_start(sdp_maerts_request->measure_cpu);
  

  if ((s_data=accept(s_listen,
		     (struct sockaddr *)&peeraddr_in,
		     &addrlen)) == INVALID_SOCKET) {
    /* Let's just punt. The remote will be given some information */
    close(s_listen);
    exit(1);
  }

#ifdef KLUDGE_SOCKET_OPTIONS
  
  /* this is for those systems which *INCORRECTLY* fail to pass
     attributes across an accept() call. Including this goes against
     my better judgement :( raj 11/95 */

  kludge_socket_options(s_data);

#endif /* KLUDGE_SOCKET_OPTIONS */
  
  /* The loop will exit when the sender does a shutdown, which will */
  /* return a length of zero   */
  
  bytes_sent = 0.0;
  send_calls  = 0;

  len = 0;   /* nt-lint; len is not initialized (printf far below) if
		times_up initially true.*/
  times_up = 0; /* must remember to initialize this little beauty */
  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,
		sdp_maerts_request->dirty_count,
		sdp_maerts_request->clean_count);

#endif /* DIRTY */

    if((len=send(s_data,
		 send_ring->buffer_ptr,
		 send_size,
		 0)) != send_size) {
		if ((len >=0) || SOCKET_EINTR(len)) {
	      /* the test was interrupted, must be the end of test */
	      break;
		}
      netperf_response.content.serv_errno = errno;
      send_response();
      exit(1);
    }

    bytes_sent += len;
    send_calls++;

    /* more to the next buffer in the send_ring */
    send_ring = send_ring->next;

  }
  
  /* perform a shutdown to signal the sender that */
  /* we have received all the data sent. raj 4/93 */

  if (shutdown(s_data,SHUT_WR) == SOCKET_ERROR) {
      netperf_response.content.serv_errno = errno;
      send_response();
      exit(1);
    }

  /* hang a recv() off the socket to block until the remote has
     brought all the data up into the application. it will do a
     shutdown to cause a FIN to be sent our way. We will assume that
     any exit from the recv() call is good... raj 4/93 */
    
  recv(s_data, send_ring->buffer_ptr, send_size, 0);
    
  
  cpu_stop(sdp_maerts_request->measure_cpu,&elapsed_time);
  
  /* send the results to the sender			*/
  
  if (debug) {
    fprintf(where,
	    "recv_sdp_maerts: got %g bytes\n",
	    bytes_sent);
    fprintf(where,
	    "recv_sdp_maerts: got %d sends\n",
	    send_calls);
    fflush(where);
  }
  
  sdp_maerts_results->bytes_sent	= htond(bytes_sent);
  sdp_maerts_results->elapsed_time	= elapsed_time;
  sdp_maerts_results->send_calls	= send_calls;
  
  if (sdp_maerts_request->measure_cpu) {
    sdp_maerts_results->cpu_util	= calc_cpu_util(0.0);
  };
  
  if (debug) {
    fprintf(where,
	    "recv_sdp_maerts: test complete, sending results.\n");
    fprintf(where,
	    "                 bytes_sent %g send_calls %d\n",
	    bytes_sent,
	    send_calls);
    fprintf(where,
	    "                 len %d\n",
	    len);
    fflush(where);
  }
  
  sdp_maerts_results->cpu_method = cpu_method;
  sdp_maerts_results->num_cpus   = lib_num_loc_cpus;
  send_response();

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

  }


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

void
send_sdp_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 %s\n";
  
  char *tput_fmt_1_line_1 = "\
%-6d %-6d %-6d   %-6d  %-6.2f   %7.2f   %s\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 %s\n";
  
  char *cpu_fmt_1_line_1 = "\
%-6d %-6d %-6d  %-6d %-6.2f  %-6.2f  %-6.2f %-6.2f %-6.3f  %-6.3f %s\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 addrinfo *local_res;
  struct addrinfo *remote_res;

  struct	sdp_rr_request_struct	*sdp_rr_request;
  struct	sdp_rr_response_struct	*sdp_rr_response;
  struct	sdp_rr_results_struct	*sdp_rr_result;

#ifdef WANT_FIRST_BURST
#define REQUEST_CWND_INITIAL 2
  /* "in the beginning..." the WANT_FIRST_BURST stuff was like both
     Unix and the state of New Jersey - both were simple an unspoiled.
     then it was realized that some stacks are quite picky about
     initial congestion windows and a non-trivial initial burst of
     requests would not be individual segments even with TCP_NODELAY
     set. so, we have to start tracking a poor-man's congestion window
     up here in window space because we want to try to make something
     happen that frankly, we cannot guarantee with the specification
     of SDP.  ain't that grand?-)  raj 2006-01-30 */
  int requests_outstanding = 0;
  int request_cwnd = REQUEST_CWND_INITIAL;  /* we ass-u-me that having
					       three requests
					       outstanding at the
					       beginning of the test
					       is ok with SDP stacks
					       of interest. the first
					       two will come from our
					       first_burst loop, and
					       the third from our
					       regularly scheduled
					       send */
#endif

  sdp_rr_request = 
    (struct sdp_rr_request_struct *)netperf_request.content.test_specific_data;
  sdp_rr_response=
    (struct sdp_rr_response_struct *)netperf_response.content.test_specific_data;
  sdp_rr_result	=
    (struct sdp_rr_results_struct *)netperf_response.content.test_specific_data;
  
#ifdef WANT_HISTOGRAM
  if (verbosity > 1) {
    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. */

  complete_addrinfos(&remote_res,
		     &local_res,
		     remote_host,
		     SOCK_STREAM,
		     IPPROTO_TCP,
		     0);

  if ( print_headers ) {
    print_top_test_header("SDP REQUEST/RESPONSE TEST",local_res,remote_res);
  }
  
  /* 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;

#ifdef WANT_FIRST_BURST
    /* we have to remember to reset the number of transactions
       outstanding and the "congestion window for each new
       iteration. raj 2006-01-31 */
    requests_outstanding = 0;
    request_cwnd = REQUEST_CWND_INITIAL;
#endif


    /* 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                        */
    /* fake things out by changing local_res->ai_family to AF_INET_SDP */
    local_res->ai_family = AF_INET_SDP;
    local_res->ai_protocol = 0;
    send_socket = create_data_socket(local_res);
  
    if (send_socket == INVALID_SOCKET){
      perror("netperf: send_sdp_rr: sdp stream data socket");
      exit(1);
    }
    
    if (debug) {
      fprintf(where,"send_sdp_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);
    }
    
    if (!no_control) {
      /* 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_SDP_RR;
      sdp_rr_request->recv_buf_size	=	rsr_size_req;
      sdp_rr_request->send_buf_size	=	rss_size_req;
      sdp_rr_request->recv_alignment    =	remote_recv_align;
      sdp_rr_request->recv_offset	=	remote_recv_offset;
      sdp_rr_request->send_alignment    =	remote_send_align;
      sdp_rr_request->send_offset	=	remote_send_offset;
      sdp_rr_request->request_size	=	req_size;
      sdp_rr_request->response_size	=	rsp_size;
      sdp_rr_request->no_delay	        =	rem_nodelay;
      sdp_rr_request->measure_cpu	=	remote_cpu_usage;
      sdp_rr_request->cpu_rate	        =	remote_cpu_rate;
      sdp_rr_request->so_rcvavoid	=	rem_rcvavoid;
      sdp_rr_request->so_sndavoid	=	rem_sndavoid;
      if (test_time) {
	sdp_rr_request->test_length	=	test_time;
      }
      else {
	sdp_rr_request->test_length	=	test_trans * -1;
      }
      sdp_rr_request->port              =      atoi(remote_data_port);
      sdp_rr_request->ipfamily = af_to_nf(remote_res->ai_family);
      
      if (debug > 1) {
	fprintf(where,"netperf: send_sdp_rr: requesting SDP 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 SDP
	 tests.  */
  
      recv_response();
  
      if (!netperf_response.content.serv_errno) {
	if (debug)
	  fprintf(where,"remote listen done.\n");
	rsr_size          = sdp_rr_response->recv_buf_size;
	rss_size          = sdp_rr_response->send_buf_size;
	rem_nodelay       = sdp_rr_response->no_delay;
	remote_cpu_usage  = sdp_rr_response->measure_cpu;
	remote_cpu_rate   = sdp_rr_response->cpu_rate;
	/* make sure that port numbers are in network order */
	set_port_number(remote_res,(short)sdp_rr_response->data_port_number);
      }
      else {
	Set_errno(netperf_response.content.serv_errno);
	fprintf(where,
		"netperf: remote error %d",
		netperf_response.content.serv_errno);
	perror("");
	fflush(where);
	
	exit(1);
      }
    }

#ifdef WANT_DEMO
    DEMO_RR_SETUP(1000)
#endif

    /*Connect up to the remote port on the data socket  */
    if (connect(send_socket, 
		remote_res->ai_addr,
		remote_res->ai_addrlen) == INVALID_SOCKET){
      perror("netperf: data socket connect failed");
      
      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
    INTERVALS_INIT();
#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. */

#ifdef WANT_DEMO
      if (demo_mode) {
	HIST_timestamp(demo_one_ptr);
      }
#endif

    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_FIRST_BURST
      /* we can inject no more than request_cwnd, which will grow with
	 time, and no more than first_burst_size.  we don't use <= to
	 account for the "regularly scheduled" send call.  of course
	 that makes it more a "max_outstanding_ than a
	 "first_burst_size" but for now we won't fix the names. also,
	 I suspect the extra check against < first_burst_size is
	 redundant since later I expect to make sure that request_cwnd
	 can never get larger than first_burst_size, but just at the
	 moment I'm feeling like a belt and suspenders kind of
	 programmer. raj 2006-01-30 */
      while ((first_burst_size > 0) &&
	     (requests_outstanding < request_cwnd) &&
	     (requests_outstanding < first_burst_size)) {
	if (debug) {
	  fprintf(where,
		  "injecting, req_outstndng %d req_cwnd %d burst %d\n",
		  requests_outstanding,
		  request_cwnd,
		  first_burst_size);
	}
	if ((len = send(send_socket,
			send_ring->buffer_ptr,
			req_size,
			0)) != req_size) {
	  /* we should never hit the end of the test in the first burst */
	  perror("send_sdp_rr: initial burst data send error");
	  exit(-1);
	}
	requests_outstanding += 1;
      }

#endif /* WANT_FIRST_BURST */
      
#ifdef WANT_HISTOGRAM
      if (verbosity > 1) {
	/* timestamp just before our call to send, and then again just
	   after the receive raj 8/94 */
	/* but only if we are actually going to display one. raj
	   2007-02-07 */

	HIST_timestamp(&time_one);
      }
#endif /* WANT_HISTOGRAM */
      
      if ((len = send(send_socket,
		      send_ring->buffer_ptr,
		      req_size,
		      0)) != req_size) {
	if (SOCKET_EINTR(len) || (errno == 0)) {
	  /* we hit the end of a */
	  /* timed test. */
	  timed_out = 1;
	  break;
	}
	perror("send_sdp_rr: data send error");
	exit(1);
      }
      send_ring = send_ring->next;

#ifdef WANT_FIRST_BURST
      requests_outstanding += 1;
#endif

      /* 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 ( SOCKET_EINTR(rsp_bytes_recvd) ) {
		    /* We hit the end of a timed test. */
			timed_out = 1;
			break;
		}
	  perror("send_sdp_rr: data recv error");
	  exit(1);
	}
	rsp_bytes_left -= rsp_bytes_recvd;
	temp_message_ptr  += rsp_bytes_recvd;
      }	
      recv_ring = recv_ring->next;
      
#ifdef WANT_FIRST_BURST
      /* so, since we've gotten a response back, update the
	 bookkeeping accordingly.  there is one less request
	 outstanding and we can put one more out there than before. */
      requests_outstanding -= 1;
      if (request_cwnd < first_burst_size) {
	request_cwnd += 1;
	if (debug) {
	  fprintf(where,
		  "incr req_cwnd to %d first_burst %d reqs_outstndng %d\n",
		  request_cwnd,
		  first_burst_size,
		  requests_outstanding);
	}
      }
#endif
      if (timed_out) {
	/* we may have been in a nested while loop - we need */
	/* another call to break. */
	break;
      }
      
#ifdef WANT_HISTOGRAM
      if (verbosity > 1) {
	HIST_timestamp(&time_two);
	HIST_add(time_hist,delta_micro(&time_one,&time_two));
      }
#endif /* WANT_HISTOGRAM */

#ifdef WANT_DEMO
      DEMO_RR_INTERVAL(1);
#endif

#ifdef WANT_INTERVALS      
      INTERVALS_WAIT();
#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);
	}
      }
    }

    /* At this point we used to call shutdown on the data socket to be
       sure all the data was delivered, but this was not germane in a
       request/response test, and it was causing the tests to "hang"
       when they were being controlled by time. So, I have replaced
       this shutdown call with a call to close that can be found later
       in the procedure. */
    
    /* 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? */
    
    if (!no_control) {
      /* Get the statistics from the remote end. The remote will have
	 calculated CPU utilization. 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);
	fprintf(where,"netperf: remote error %d",
		netperf_response.content.serv_errno);
	perror("");
	fflush(where);
	exit(1);
      }
    }
    
    /* We now calculate what our throughput 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	= (float) -1.0;
	local_service_demand	= (float) -1.0;
      }
      
      if (remote_cpu_usage) {
	remote_cpu_utilization = sdp_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,
						    sdp_rr_result->num_cpus);
      }
      else {
	remote_cpu_utilization = (float) -1.0;
	remote_service_demand  = (float) -1.0;
      }
      
    }
    else {
      /* we were not measuring cpu, for the confidence stuff, we */
      /* should make it -1.0 */
      local_cpu_utilization	= (float) -1.0;
      local_service_demand	= (float) -1.0;
      remote_cpu_utilization = (float) -1.0;
      remote_service_demand  = (float) -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 */
    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(sdp_rr_result->cpu_method);
    
    switch (verbosity) {
    case 0:
      if (local_cpu_usage) {
	fprintf(where,
		cpu_fmt_0,
		local_service_demand,
		local_cpu_method,
		((print_headers) || 
		 (result_brand == NULL)) ? "" : result_brand);
      }
      else {
	fprintf(where,
		cpu_fmt_0,
		remote_service_demand,
		remote_cpu_method,
		((print_headers) || 
		 (result_brand == NULL)) ? "" : result_brand);
      }
      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 */
	      ((print_headers) || 
	       (result_brand == NULL)) ? "" : result_brand);
      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,
	      ((print_headers) || 
	       (result_brand == NULL)) ? "" : result_brand);
      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,
	      ((print_headers) || 
	       (result_brand == NULL)) ? "" : result_brand);
      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 */
    /* SDP 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 */

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

  struct addrinfo *local_res;
  char local_name[BUFSIZ];
  char port_buffer[PORTBUFSIZE];

  struct	sockaddr_in        myaddr_in,
  peeraddr_in;
  SOCKET	s_listen,s_data;
  netperf_socklen_t 	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;
  int   sock_closed = 0;
  float	elapsed_time;
  
  struct	sdp_rr_request_struct	*sdp_rr_request;
  struct	sdp_rr_response_struct	*sdp_rr_response;
  struct	sdp_rr_results_struct	*sdp_rr_results;
  
  sdp_rr_request = 
    (struct sdp_rr_request_struct *)netperf_request.content.test_specific_data;
  sdp_rr_response =
    (struct sdp_rr_response_struct *)netperf_response.content.test_specific_data;
  sdp_rr_results =
    (struct sdp_rr_results_struct *)netperf_response.content.test_specific_data;
  
  if (debug) {
    fprintf(where,"netserver: recv_sdp_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_sdp_rr: setting the response type...\n");
    fflush(where);
  }
  
  netperf_response.content.response_type = SDP_RR_RESPONSE;
  
  if (debug) {
    fprintf(where,"recv_sdp_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_sdp_rr: requested recv alignment of %d offset %d\n",
	    sdp_rr_request->recv_alignment,
	    sdp_rr_request->recv_offset);
    fprintf(where,"recv_sdp_rr: requested send alignment of %d offset %d\n",
	    sdp_rr_request->send_alignment,
	    sdp_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,
				   sdp_rr_request->response_size,
				   sdp_rr_request->send_alignment,
				   sdp_rr_request->send_offset);

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

  
  /* Grab a socket to listen on, and then listen on it. */
  
  if (debug) {
    fprintf(where,"recv_sdp_rr: grabbing a socket...\n");
    fflush(where);
  }

  /* create_data_socket 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_req = sdp_rr_request->send_buf_size;
  lsr_size_req = sdp_rr_request->recv_buf_size;
  loc_nodelay = sdp_rr_request->no_delay;
  loc_rcvavoid = sdp_rr_request->so_rcvavoid;
  loc_sndavoid = sdp_rr_request->so_sndavoid;

  set_hostname_and_port(local_name,
			port_buffer,
			nf_to_af(sdp_rr_request->ipfamily),
			sdp_rr_request->port);

  local_res = complete_addrinfo(local_name,
				local_name,
				port_buffer,
				nf_to_af(sdp_rr_request->ipfamily),
				SOCK_STREAM,
				IPPROTO_TCP,
				0);

  /* fake things out by changing local_res->ai_family to AF_INET_SDP */
  local_res->ai_family = AF_INET_SDP;
  local_res->ai_protocol = 0;
  s_listen = create_data_socket(local_res);
  
  if (s_listen == INVALID_SOCKET) {
    netperf_response.content.serv_errno = errno;
    send_response();
    
    exit(1);
  }
  
  
#ifdef WIN32
  /* The test timer can fire during operations on the listening socket,
     so to make the start_timer below work we have to move
     it to close s_listen while we are blocked on accept. */
  win_kludge_socket2 = s_listen;
#endif

  
  /* 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();
    
    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();
    
    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. */
  
  sdp_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. */
  
  sdp_rr_response->cpu_rate = (float)0.0; 	/* assume no cpu */
  sdp_rr_response->measure_cpu = 0;

  if (sdp_rr_request->measure_cpu) {
    sdp_rr_response->measure_cpu = 1;
    sdp_rr_response->cpu_rate = calibrate_local_cpu(sdp_rr_request->cpu_rate);
  }
  
  
  /* before we send the response back to the initiator, pull some of */
  /* the socket parms from the globals */
  sdp_rr_response->send_buf_size = lss_size;
  sdp_rr_response->recv_buf_size = lsr_size;
  sdp_rr_response->no_delay = loc_nodelay;
  sdp_rr_response->so_rcvavoid = loc_rcvavoid;
  sdp_rr_response->so_sndavoid = loc_sndavoid;
  sdp_rr_response->test_length = sdp_rr_request->test_length;
  send_response();
  
  addrlen = sizeof(peeraddr_in);
  
  if ((s_data = accept(s_listen,
		       (struct sockaddr *)&peeraddr_in,
		       &addrlen)) == INVALID_SOCKET) {
    /* Let's just punt. The remote will be given some information */
    close(s_listen);
    
    exit(1);
  }
  
#ifdef KLUDGE_SOCKET_OPTIONS
  /* this is for those systems which *INCORRECTLY* fail to pass */
  /* attributes across an accept() call. Including this goes against */
  /* my better judgement :( raj 11/95 */

  kludge_socket_options(s_data);

#endif /* KLUDGE_SOCKET_OPTIONS */

#ifdef WIN32
  /* this is used so the timer thread can close the socket out from */
  /* under us, which to date is the easiest/cleanest/least */
  /* Windows-specific way I can find to force the winsock calls to */
  /* return WSAEINTR with the test is over. anything that will run on */
  /* 95 and NT and is closer to what netperf expects from Unix signals */
  /* and such would be appreciated raj 1/96 */
  win_kludge_socket = s_data;
#endif /* WIN32 */

  if (debug) {
    fprintf(where,"recv_sdp_rr: accept completes on the data connection.\n");
    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(sdp_rr_request->measure_cpu);
  
  /* The loop will exit when we hit the end of the test time, or when */
  /* we have exchanged the requested number of transactions. */
  
  if (sdp_rr_request->test_length > 0) {
    times_up = 0;
    trans_remaining = 0;
    start_timer(sdp_rr_request->test_length + PAD_TIME);
  }
  else {
    times_up = 1;
    trans_remaining = sdp_rr_request->test_length * -1;
  }

  trans_received = 0;
  
  while ((!times_up) || (trans_remaining > 0)) {
    temp_message_ptr = recv_ring->buffer_ptr;
    request_bytes_remaining	= sdp_rr_request->request_size;
    while(request_bytes_remaining > 0) {
      if((request_bytes_recvd=recv(s_data,
				   temp_message_ptr,
				   request_bytes_remaining,
				   0)) == SOCKET_ERROR) {
	if (SOCKET_EINTR(request_bytes_recvd))
	{
	  timed_out = 1;
	  break;
	}

	netperf_response.content.serv_errno = errno;
	send_response();
	exit(1);
      }
      else if( request_bytes_recvd == 0 ) {
	if (debug) {
	  fprintf(where,"zero is my hero\n");
	  fflush(where);
	}
	sock_closed = 1;
	break;
      }
      else {
	request_bytes_remaining -= request_bytes_recvd;
	temp_message_ptr  += request_bytes_recvd;
      }
    }

    recv_ring = recv_ring->next;

    if ((timed_out) || (sock_closed)) {
      /* we hit the end of the test based on time - or the socket
	 closed on us along the way.  bail out of here now... */
      if (debug) {
	fprintf(where,"yo5\n");
	fflush(where);
      }						
      break;
    }
    
    /* Now, send the response to the remote */
    if((bytes_sent=send(s_data,
			send_ring->buffer_ptr,
			sdp_rr_request->response_size,
			0)) == SOCKET_ERROR) {
      if (SOCKET_EINTR(bytes_sent)) {
	/* the test timer has popped */
	timed_out = 1;
	fprintf(where,"yo6\n");
	fflush(where);						
	break;
      }
      netperf_response.content.serv_errno = 992;
      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(sdp_rr_request->measure_cpu,&elapsed_time);
  
  stop_timer();

  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_sdp_rr: got %d transactions\n",
	    trans_received);
    fflush(where);
  }
  
  sdp_rr_results->bytes_received = (trans_received * 
				    (sdp_rr_request->request_size + 
				     sdp_rr_request->response_size));
  sdp_rr_results->trans_received = trans_received;
  sdp_rr_results->elapsed_time   = elapsed_time;
  sdp_rr_results->cpu_method     = cpu_method;
  sdp_rr_results->num_cpus       = lib_num_loc_cpus;
  if (sdp_rr_request->measure_cpu) {
    sdp_rr_results->cpu_util	= calc_cpu_util(elapsed_time);
  }
  
  if (debug) {
    fprintf(where,
	    "recv_sdp_rr: test complete, sending results.\n");
    fflush(where);
  }
  
  /* we are now done with the sockets */
  close(s_data);
  close(s_listen);

  send_response();
  
}



void
print_sdp_usage()
{

  printf("%s",sdp_usage);
  exit(1);

}
void
scan_sdp_args(argc, argv)
     int	argc;
     char	*argv[];

{

#define SOCKETS_ARGS "b:DhH:I:L:m:M:P:r:s:S:V46"

  extern char	*optarg;	  /* pointer to option string	*/
  
  int		c;
  
  char	
    arg1[BUFSIZ],  /* argument holders		*/
    arg2[BUFSIZ];

  if (no_control) {
    fprintf(where,
	    "The SDP tests do not know how to deal with no control tests\n");
    exit(-1);
  }

  strncpy(local_data_port,"0",sizeof(local_data_port));
  strncpy(remote_data_port,"0",sizeof(remote_data_port));
  
  /* 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, SOCKETS_ARGS)) != EOF) {
    switch (c) {
    case '?':	
    case '4':
      remote_data_family = AF_INET;
      local_data_family = AF_INET;
      break;
    case '6':
#if defined(AF_INET6)
      remote_data_family = AF_INET6;
      local_data_family = AF_INET6;
#else
      fprintf(stderr,
	      "This netperf was not compiled on an IPv6 capable host!\n");
      fflush(stderr);
      exit(-1);
#endif
      break;
    case 'h':
      print_sdp_usage();
      exit(1);
    case 'b':
#ifdef WANT_FIRST_BURST
      first_burst_size = atoi(optarg);
#else /* WANT_FIRST_BURST */
      printf("Initial request burst functionality not compiled-in!\n");
#endif /* WANT_FIRST_BURST */
      break;
    case 'D':
      /* set the nodelay flag */
      loc_nodelay = 1;
      rem_nodelay = 1;
      break;
    case 'H':
      break_args_explicit(optarg,arg1,arg2);
      if (arg1[0]) {
	/* make sure we leave room for the NULL termination boys and
	   girls. raj 2005-02-82 */ 
	remote_data_address = malloc(strlen(arg1)+1);
	strncpy(remote_data_address,arg1,strlen(arg1));
      }
      if (arg2[0])
	remote_data_family = parse_address_family(arg2);
      break;
    case 'L':
      break_args_explicit(optarg,arg1,arg2);
      if (arg1[0]) {
	/* make sure we leave room for the NULL termination boys and
	   girls. raj 2005-02-82 */ 
	local_data_address = malloc(strlen(arg1)+1);
	strncpy(local_data_address,arg1,strlen(arg1));
      }
      if (arg2[0])
	local_data_family = parse_address_family(arg2);
      break;
    case 'P':
      /* set the local and remote data port numbers for the tests to
	 allow them to run through those blankety blank end-to-end
	 breaking firewalls. raj 2004-06-15 */
      break_args(optarg,arg1,arg2);
      if (arg1[0])
	strncpy(local_data_port,arg1,sizeof(local_data_port));
      if (arg2[0])	
	strncpy(remote_data_port,arg2,sizeof(remote_data_port));
      break;
    case 's':
      /* set local socket sizes */
      break_args(optarg,arg1,arg2);
      if (arg1[0])
	lss_size_req = convert(arg1);
      if (arg2[0])
	lsr_size_req = convert(arg2);
      break;
    case 'S':
      /* set remote socket sizes */
      break_args(optarg,arg1,arg2);
      if (arg1[0])
	rss_size_req = convert(arg1);
      if (arg2[0])
	rsr_size_req = 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 size of the buffer for each sent message */
      send_size = convert(optarg);
      break;
    case 'M':
      /* set the size of the buffer for each received message */
      recv_size = convert(optarg);
      break;
    case 't':
      /* set the test name */
      strcpy(test_name,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 'N':
      /* this opton allows the user to set the number of 
       * messages to send.  This in effect modifies the test
       * time.  If we know the message size, then the we can
       * express the test time as message_size * number_messages
       */
      msg_count = convert (optarg);
      if (msg_count > 0)
	  test_time = 0;
      break;
    case 'B':
      non_block = 1;
      break;
    case 'T':
      num_associations = atoi(optarg);
      if (num_associations <= 1) {
	  printf("Number of SDP associations must be >= 1\n");
	  exit(1);
      }
      break;
    };
  }
}

#endif  /* WANT_SDP */