Measuring Network Software Performance
by James K. Yun


Listing One
/* the sending (netperf) side of the TCP_RR test. */

void
send_tcp_rr(remote_host)
     char       remote_host[];
{
 . . .
    /* Set-up the test end conditions. For a request/response test, they */
    /* can be either time or transaction based. */

    if (test_time) {
      /* The user wanted to end the test after a period of time. */
      times_up = 0;
      trans_remaining = 0;
      start_timer(test_time);
    }
    else {
      /* The tester wanted to send a number of bytes. */
      trans_remaining = test_bytes;
      times_up = 1;
    }
    /* The cpu_start routine will grab the current time and possibly */
    /* value of the idle counter for later use in measuring cpu */
    /* utilization and/or service demand and thruput. */
    cpu_start(local_cpu_usage);
 . . .
    /* We use an "OR" to control test execution. When the test is */
    /* controlled by time, the byte count check will always return false. */
    /* When the test is controlled by byte count, the time test will */
    /* always return false. When the test is finished, the whole */
    /* expression will go false and we will stop sending data. */

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

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

      if (timed_out) {
        /* we may have been in a nested while loop - we need */
        /* another call to break. */
        break;
      }
      nummessages++;
      if (trans_remaining) {
        trans_remaining--;
      }
  . . .
    }
    /* 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);
  . . .
    /* We now calculate what our throughput was for the test. */
    thruput     = nummessages/elapsed_time;
  . . .
}

Listing Two
/* This routine implements the TCP unidirectional data transfer test */
/* (a.k.a. stream) for the sockets interface. It receives its */
/* parameters via global variables from the shell and writes its */
/* output to the standard output. */
void
send_tcp_stream(remote_host)
char    remote_host[];
{
 . . .
    /* Tell the remote end to do a listen.... */
    /* This call also sends a parameter message to Receiver (remote) side. */
    send_request();
 . . .
    /* receive message from Receiver (remote); contains data such as
     * port number but can be seen as a "ready" status.  -- yun
     */
    recv_response();
 . . .
    /*Connect up to the remote port on the data socket  */
    if (connect(send_socket, (struct sockaddr *)&server, sizeof(server)) <0){
      perror("netperf: send_tcp_stream: data socket connect failed");
      printf(" port: %d\n",ntohs(server.sin_port));
      exit(1);
    }
 . . .
    /* 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;
      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 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)) {

      if((len=send(send_socket, send_ring->buffer_ptr, send_size,
                   0)) != send_size) {
          /* the test was interrupted, must be the end of test */
          break;
        }
        perror("netperf: data send error");
        printf("len was %d\n",len);
        exit(1);
      }
      /* 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 buffers to the remote end. We do a graceful */
    /* release to insure all data has been taken by the remote. */
 . . .
    if (shutdown(send_socket,1) == -1) {
      perror("netperf: cannot shutdown tcp 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);

    /* we are finished with the socket, so close it to prevent hitting */
    /* the limit on maximum open files. */

    close(send_socket);

    /* 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 {
      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 TCP stream test, */
    /* that the two numbers should be *very* close... We calculate */
    /* bytes_sent regardless of the way the test length was controlled. */
    /* If it was time, we needed to, and if it was by bytes, the user may */
    /* have specified a number of bytes that wasn't a multiple of the */
    /* send_size, so we really didn't send what he asked for ;-) */

    bytes_sent  = ntohd(tcp_stream_result->bytes_received);

    thruput     = (double) calc_thruput(bytes_sent);
 . . .
}


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

void
recv_tcp_stream()
{
 . . .
  /* The parameter message from the Sender side had already been received
   * by the parent of recv_tcp_stream().  -- yun */
 . . .
  /* Now, let's set-up the socket to listen for connections */
  if (listen(s_listen, 5) == -1) {
    netperf_response.content.serv_errno = errno;
    close(s_listen);
    send_response();

    exit(1);
  }
 . . .
  /* send data such as port number to Sender; can also be seen as
   * Receiver's "reeady" status  -- yun */
  send_response();
 . . .
  if ((s_data=accept(s_listen, (struct sockaddr *)&peeraddr_in,
                     &addrlen)) == -1) {
    /* Let's just punt. The remote will be given some information */
    close(s_listen);
    exit(1);
  }
 . . .
  cpu_start(tcp_stream_request->measure_cpu);
 . . .
  /* The loop will exit when the sender does a shutdown, which will */
  /* return a length of zero   */

  while ((len = recv(s_data, recv_ring->buffer_ptr, recv_size, 0)) != 0) {
    if (len < 0) {
      netperf_response.content.serv_errno = errno;

      netperf_response.content.serv_errno = errno;
      send_response();
      exit(1);
    }
    bytes_received += len;
    receive_calls++;

    /* more to the next buffer in the recv_ring */
    recv_ring = recv_ring->next;
  }
  /* perform a shutdown to signal the sender that */
  /* we have received all the data sent. raj 4/93 */

  if (shutdown(s_data,1) == -1) {
      netperf_response.content.serv_errno = errno;
      send_response();
      exit(1);
    }
  cpu_stop(tcp_stream_request->measure_cpu,&elapsed_time);
 . . .
}





5


