Esempio n. 1
0
/*------------------------------------------------------------------------
 * int ttp_update_stats(ttp_session_t *session);
 *
 * This routine must be called every interval to update the statistics
 * for the progress of the ongoing file transfer.  Returns 0 on success
 * and non-zero on failure.  (There is not currently any way to fail.)
 *------------------------------------------------------------------------*/
int ttp_update_stats(ttp_session_t *session)
{
    time_t            now_epoch = time(NULL);                 /* the current Unix epoch                         */
    u_int64_t         delta;                                  /* time delta since last statistics update (usec) */
    double            d_seconds;
    u_int64_t         delta_total;                            /* time delta since start of transmission (usec)  */
    double            d_seconds_total;
    u_int64_t         temp;                                   /* temporary value for building the elapsed time  */
    int               hours, minutes, seconds, milliseconds;  /* parts of the elapsed time                      */
    double            data_total;                             /* the total amount of data transferred (bytes)   */
    double            data_total_rate;
    double            data_this;                              /* the amount of data since last stat time        */
    double            data_this_rexmit;                       /* the amount of data in received retransmissions */ 
    double            data_this_goodpt;                       /* the amount of data as non-lost packets         */
    double            retransmits_fraction;                   /* how many retransmit requests there were vs received blocks */
    double            total_retransmits_fraction;
    double            ringfill_fraction;
    statistics_t     *stats = &(session->transfer.stats);
    retransmission_t  retransmission;
    int               status;
    static u_int32_t  iteration = 0;
    static char       stats_line[128];

    double ff, fb;

    const double u_mega = 1024*1024;
    const double u_giga = 1024*1024*1024;

    /* find the total time elapsed */
    delta        =        get_usec_since(&stats->this_time);
    delta_total  = temp = get_usec_since(&stats->start_time);
    milliseconds = (temp % 1000000) / 1000;  temp /= 1000000;
    seconds      = temp % 60;                temp /= 60;
    minutes      = temp % 60;                temp /= 60;
    hours        = temp;

    d_seconds       = delta / 1e6;
    d_seconds_total = delta_total / 1e6;

    /* find the amount of data transferred (bytes) */
    data_total  = ((double) session->parameter->block_size) * stats->total_blocks;
    data_this   = ((double) session->parameter->block_size) * (stats->total_blocks - stats->this_blocks);
    data_this_rexmit = ((double) session->parameter->block_size) * stats->this_flow_retransmitteds;
    data_this_goodpt = ((double) session->parameter->block_size) * stats->this_flow_originals;
    // <=> data_this == data_this_rexmit + data_this_goodpt

    /* get the current UDP receive error count reported by the operating system */
    stats->this_udp_errors = get_udp_in_errors();

    /* precalculate some fractions */
    retransmits_fraction = stats->this_retransmits / (1.0 + stats->this_retransmits + stats->total_blocks - stats->this_blocks);
    ringfill_fraction    = session->transfer.ring_buffer->count_data / MAX_BLOCKS_QUEUED;
    total_retransmits_fraction = stats->total_retransmits / (stats->total_retransmits + stats->total_blocks);

    /* update the rate statistics */
    // incoming transmit rate R = goodput R (Mbit/s) + retransmit R (Mbit/s)
    stats->this_transmit_rate   = 8.0 * data_this        / (d_seconds * u_mega); 
    stats->this_retransmit_rate = 8.0 * data_this_rexmit / (d_seconds * u_mega);
    data_total_rate             = 8.0 * data_total       / (d_seconds_total * u_mega);

    fb = session->parameter->history / 100.0;  // feedback
    ff = 1.0 - fb;                             // feedforward

    // IIR filter rate R
    stats->transmit_rate = fb * stats->transmit_rate + ff * stats->this_transmit_rate;

    // IIR filtered composite error and loss, some sort of knee function
    stats->error_rate = fb * stats->error_rate + ff * 500*100 * (retransmits_fraction + ringfill_fraction);
        
    /* send the current error rate information to the server */
    memset(&retransmission, 0, sizeof(retransmission));
    retransmission.request_type = htons(REQUEST_ERROR_RATE);
    retransmission.error_rate   = htonl((u_int64_t) session->transfer.stats.error_rate);
    status = fwrite(&retransmission, sizeof(retransmission), 1, session->server);
    if ((status <= 0) || fflush(session->server))
        return warn("Could not send error rate information");

    /* build the stats string */    
    #ifdef STATS_MATLABFORMAT
    sprintf(stats_line, "%02d\t%02d\t%02d\t%03d\t%4u\t%6.2f\t%6.1f\t%5.1f\t%7u\t%6.1f\t%6.1f\t%5.1f\t%5d\t%5d\t%7u\t%8u\t%8Lu\n",
    #else
    sprintf(stats_line, "%02d:%02d:%02d.%03d %4u %6.2fM %6.1fMbps %5.1f%% %7u %6.1fG %6.1fMbps %5.1f%% %5d %5d %7u %8u %8Lu\n",
    #endif
        hours, minutes, seconds, milliseconds,
        stats->total_blocks - stats->this_blocks,
        stats->this_retransmit_rate,
        stats->this_transmit_rate,
        100.0 * retransmits_fraction,
        session->transfer.stats.total_blocks,
        data_total / u_giga,
        data_total_rate,
        100.0 * total_retransmits_fraction,
        session->transfer.retransmit.index_max,
        session->transfer.ring_buffer->count_data,
        session->transfer.blocks_left, 
        stats->this_retransmits,
        (ull_t)(stats->this_udp_errors - stats->start_udp_errors)
        );

    /* give the user a show if they want it */
    if (session->parameter->verbose_yn) {

        /* screen mode */
        if (session->parameter->output_mode == SCREEN_MODE) {
            printf("\033[2J\033[H");
            printf("Current time:   %s\n", ctime(&now_epoch));
            printf("Elapsed time:   %02d:%02d:%02d.%03d\n\n", hours, minutes, seconds, milliseconds);
            printf("Last interval\n--------------------------------------------------\n");
            printf("Blocks count:     %u\n",             stats->total_blocks - stats->this_blocks);
            printf("Data transferred: %0.2f GB\n",       data_this  / u_giga);
            printf("Transfer rate:    %0.2f Mbps\n",     stats->this_transmit_rate);
            printf("Retransmissions:  %u (%0.2f%%)\n\n", stats->this_retransmits, 100.0*retransmits_fraction);
            printf("Cumulative\n--------------------------------------------------\n");
            printf("Blocks count:     %u\n",             session->transfer.stats.total_blocks);
            printf("Data transferred: %0.2f GB\n",       data_total / u_giga);
            printf("Transfer rate:    %0.2f Mbps\n",     data_total_rate);
            printf("Retransmissions:  %u (%0.2f%%)\n\n", stats->total_retransmits, 100.0*total_retransmits_fraction);
            printf("OS UDP rx errors: %Lu\n",            (ull_t)(stats->this_udp_errors - stats->start_udp_errors));

        /* line mode */
        } else {

            /* print a header if necessary */
            #ifndef STATS_NOHEADER
            if (!(iteration++ % 23)) {
                printf("             last_interval                   transfer_total                   buffers      transfer_remaining  OS UDP\n");
                printf("time          blk    data       rate rexmit     blk    data       rate rexmit queue  ring     blk   rt_len      err \n");
            }
            #endif
            printf("%s", stats_line);
        }

        /* and flush the output */
        fflush(stdout);
    }

    /* print to the transcript if the user wants */
    if (session->parameter->transcript_yn)
        xscript_data_log(session, stats_line);

    /* reset the statistics for the next interval */
    stats->this_blocks              = stats->total_blocks;
    stats->this_retransmits         = 0;
    stats->this_flow_originals      = 0;
    stats->this_flow_retransmitteds = 0;
    gettimeofday(&(stats->this_time), NULL);

    /* indicate success */
    return 0;
}
Esempio n. 2
0
/*------------------------------------------------------------------------
 * int ttp_accept_retransmit(ttp_session_t *session,
 *                           retransmission_t *retransmission,
 *                           u_char *datagram);
 *
 * Handles the given retransmission request.  The actions taken depend
 * on the nature of the request:
 *
 *   REQUEST_RETRANSMIT -- Retransmit the given block.
 *   REQUEST_RESTART    -- Restart the transfer at the given block.
 *   REQUEST_ERROR_RATE -- Use the given error rate to adjust the IPD.
 *
 * For REQUEST_RETRANSMIT messsages, the given buffer must be large
 * enough to hold (block_size + 6) bytes.  For other messages, the
 * datagram parameter is ignored.
 *
 * Returns 0 on success and non-zero on failure.
 *------------------------------------------------------------------------*/
int ttp_accept_retransmit(ttp_session_t *session, retransmission_t *retransmission, u_char *datagram)
{
    ttp_transfer_t  *xfer      = &session->transfer;
    ttp_parameter_t *param     = session->parameter;
    static int       iteration = 0;
    static char      stats_line[80];
    int              status;
    u_int16_t        type;

    /* convert the retransmission fields to host byte order */
    retransmission->block      = ntohl(retransmission->block);
    retransmission->error_rate = ntohl(retransmission->error_rate);
    type                       = ntohs(retransmission->request_type);

    /* if it's an error rate notification */
    if (type == REQUEST_ERROR_RATE) {

	/* calculate a new IPD */
	if (retransmission->error_rate > param->error_rate) {
	    double factor1 = (1.0 * param->slower_num / param->slower_den) - 1.0;
	    double factor2 = (1.0 + retransmission->error_rate - param->error_rate) / (100000.0 - param->error_rate);
	    xfer->ipd_current *= 1.0 + (factor1 * factor2);
	} else {
	    xfer->ipd_current *= (double) param->faster_num / param->faster_den;
	}

    /* make sure the IPD is still in range, for later calculations */
    xfer->ipd_current = max(min(xfer->ipd_current, 10000.0), param->ipd_time);

    /* build the stats string */
    sprintf(stats_line, "%6u %3.2fus %5uus %7u %6.2f %3u\n",
        retransmission->error_rate, (float)xfer->ipd_current, param->ipd_time, xfer->block,
        100.0 * xfer->block / param->block_count, session->session_id);

	/* print a status report */
	if (!(iteration++ % 23))
	    printf(" erate     ipd  target   block   %%done srvNr\n");
	printf("%s", stats_line);

	/* print to the transcript if the user wants */
	if (param->transcript_yn)
	    xscript_data_log(session, stats_line);

    /* if it's a restart request */
    } else if (type == REQUEST_RESTART) {

	/* do range-checking first */
	if ((retransmission->block == 0) || (retransmission->block > param->block_count)) {
	    sprintf(g_error, "Attempt to restart at illegal block %u", retransmission->block);
	    return warn(g_error);
	} else
	    xfer->block = retransmission->block;

    /* if it's a retransmit request */
    } else if (type == REQUEST_RETRANSMIT) {

        /* build the retransmission */
        status = build_datagram(session, retransmission->block, TS_BLOCK_RETRANSMISSION, datagram);
        if (status < 0) {
            sprintf(g_error, "Could not build retransmission for block %u", retransmission->block);
            return warn(g_error);
        }
      
        /* try to send out the block */
        status = sendto(xfer->udp_fd, datagram, 6 + param->block_size, 0, xfer->udp_address, xfer->udp_length);
        if (status < 0) {
            sprintf(g_error, "Could not retransmit block %u", retransmission->block);
            return warn(g_error);
        }

    /* if it's another kind of request */
    } else {
	sprintf(g_error, "Received unknown retransmission request of type %u", ntohs(retransmission->request_type));
	return warn(g_error);
    }

    /* we're done */
    return 0;
}
Esempio n. 3
0
/*------------------------------------------------------------------------
 * void client_handler(ttp_session_t *session);
 *
 * This routine is run by the client processes that are created in
 * response to incoming connections.
 *------------------------------------------------------------------------*/
void client_handler(ttp_session_t *session)
{
    retransmission_t  retransmission;                /* the retransmission data object                 */
    struct timeval    start, stop;                   /* the start and stop times for the transfer      */
    struct timeval    prevpacketT;                   /* the send time of the previous packet           */
    struct timeval    currpacketT;                   /* the interpacket delay value                    */
    struct timeval    lastfeedback;                  /* the time since last client feedback            */
    struct timeval    lasthblostreport;              /* the time since last 'heartbeat lost' report    */
    u_int32_t         deadconnection_counter;        /* the counter for checking dead conn timeout     */
    int               retransmitlen;                 /* number of bytes read from retransmission queue */
    u_char            datagram[MAX_BLOCK_SIZE + 6];  /* the datagram containing the file block         */
    int64_t           ipd_time;                      /* the time to delay/sleep after packet, signed   */
    int64_t           ipd_usleep_diff;               /* the time correction to ipd_time, signed        */
    int64_t           ipd_time_max;
    int               status;
    ttp_transfer_t   *xfer  = &session->transfer;
    ttp_parameter_t  *param =  session->parameter;
    u_int64_t         delta;
    u_char            block_type;

    /* negotiate the connection parameters */
    status = ttp_negotiate(session);
    if (status < 0)
        error("Protocol revision number mismatch");

    /* have the client try to authenticate to us */
    status = ttp_authenticate(session, session->parameter->secret);
    if (status < 0)
        error("Client authentication failure");

    if (1==param->verbose_yn) {
        fprintf(stderr,"Client authenticated. Negotiated parameters are:\n");
        fprintf(stderr,"Block size: %d\n", param->block_size);
        fprintf(stderr,"Buffer size: %d\n", param->udp_buffer); 
        fprintf(stderr,"Port: %d\n", param->tcp_port);    
    }

    /* while we haven't been told to stop */
    while (1) {

    /* make the client descriptor blocking */
    status = fcntl(session->client_fd, F_SETFL, 0);
    if (status < 0)
        error("Could not make client socket blocking");

    /* negotiate another transfer */
    status = ttp_open_transfer(session);
    if (status < 0) {
        warn("Invalid file request");
        continue;
    }

    /* negotiate a data transfer port */
    status = ttp_open_port(session);
    if (status < 0) {
        warn("UDP socket creation failed");
        continue;
    }

    /* make the client descriptor non-blocking again */
    status = fcntl(session->client_fd, F_SETFL, O_NONBLOCK);
    if (status < 0)
        error("Could not make client socket non-blocking");

    /*---------------------------
     * START TIMING
     *---------------------------*/
    gettimeofday(&start, NULL);
    if (param->transcript_yn)
        xscript_data_start(session, &start);

    lasthblostreport       = start;
    lastfeedback           = start;
    prevpacketT            = start;
    deadconnection_counter = 0;
    ipd_time               = 0;
    ipd_time_max           = 0;
    ipd_usleep_diff        = 0;
    retransmitlen          = 0;

    /* start by blasting out every block */
    xfer->block = 0;
    while (xfer->block <= param->block_count) {

        /* default: flag as retransmitted block */
        block_type = TS_BLOCK_RETRANSMISSION;

        /* precalculate time to wait after sending the next packet */
        gettimeofday(&currpacketT, NULL);
        ipd_usleep_diff = xfer->ipd_current + tv_diff_usec(prevpacketT, currpacketT);
        prevpacketT = currpacketT;
        if (ipd_usleep_diff > 0 || ipd_time > 0) {
            ipd_time += ipd_usleep_diff;
        }
        ipd_time_max = (ipd_time > ipd_time_max) ? ipd_time : ipd_time_max;

        /* see if transmit requests are available */
        status = read(session->client_fd, ((char*)&retransmission)+retransmitlen, sizeof(retransmission)-retransmitlen);
        #ifndef VSIB_REALTIME
        if ((status <= 0) && (errno != EAGAIN))
            error("Retransmission read failed");
        #else
        if ((status <= 0) && (errno != EAGAIN) && (!session->parameter->fileout))
            error("Retransmission read failed and not writing local backup file");
        #endif
        if (status > 0)
            retransmitlen += status;

        /* if we have a retransmission */
        if (retransmitlen == sizeof(retransmission_t)) {

            /* store current time */
            lastfeedback           = currpacketT;
            lasthblostreport       = currpacketT;
            deadconnection_counter = 0;

            /* if it's a stop request, go back to waiting for a filename */
            if (ntohs(retransmission.request_type) == REQUEST_STOP) {
                fprintf(stderr, "Transmission complete.\n");
                break;
            }

            /* otherwise, handle the retransmission */
            status = ttp_accept_retransmit(session, &retransmission, datagram);
            if (status < 0)
                warn("Retransmission error");
            retransmitlen = 0;

        /* if we have no retransmission */
        } else if (retransmitlen < sizeof(retransmission_t)) {

            /* build the block */
            xfer->block = min(xfer->block + 1, param->block_count);
            block_type = (xfer->block == param->block_count) ? TS_BLOCK_TERMINATE : TS_BLOCK_ORIGINAL;
            status = build_datagram(session, xfer->block, block_type, datagram);
            if (status < 0) {
                sprintf(g_error, "Could not read block #%u", xfer->block);
                error(g_error);
            }

            /* transmit the block */
            status = sendto(xfer->udp_fd, datagram, 6 + param->block_size, 0, xfer->udp_address, xfer->udp_length);
            if (status < 0) {
                sprintf(g_error, "Could not transmit block #%u", xfer->block);
                warn(g_error);
                continue;
            }

        /* if we have too long retransmission message */
        } else if (retransmitlen > sizeof(retransmission_t)) {

            fprintf(stderr, "warn: retransmitlen > %d\n", (int)sizeof(retransmission_t));
            retransmitlen = 0;

        }

        /* monitor client heartbeat and disconnect dead client */
        if ((deadconnection_counter++) > 2048) {
            char stats_line[160];

            deadconnection_counter = 0;

            /* limit 'heartbeat lost' reports to 500ms intervals */
            if (get_usec_since(&lasthblostreport) < 500000.0) continue;
            gettimeofday(&lasthblostreport, NULL);

            /* throttle IPD with fake 100% loss report */
            #ifndef VSIB_REALTIME
            retransmission.request_type = htons(REQUEST_ERROR_RATE);
            retransmission.error_rate   = htonl(100000);
            retransmission.block = 0;
            ttp_accept_retransmit(session, &retransmission, datagram);
            #endif

            delta = get_usec_since(&lastfeedback);

            /* show an (additional) statistics line */
            snprintf(stats_line, sizeof(stats_line)-1,
                                "   n/a     n/a     n/a %7u %6.2f %3u -- no heartbeat since %3.2fs\n",
                                xfer->block, 100.0 * xfer->block / param->block_count, session->session_id,
                                1e-6*delta);
            if (param->transcript_yn)
               xscript_data_log(session, stats_line);
            fprintf(stderr, "%s", stats_line);

            /* handle timeout for normal file transfers */
            #ifndef VSIB_REALTIME
            if ((1e-6 * delta) > param->hb_timeout) {
                fprintf(stderr, "Heartbeat timeout of %d seconds reached, terminating transfer.\n", param->hb_timeout);
                break;
            }
            #else
            /* handle timeout condition for : realtime with local backup, simple realtime */
            if ((1e-6 * delta) > param->hb_timeout) {
                if ((session->parameter->fileout) && (block_type == TS_BLOCK_TERMINATE)) {
                    fprintf(stderr, "Reached the Terminate block and timed out, terminating transfer.\n");
                    break;
                } else if(!session->parameter->fileout) {
                    fprintf(stderr, "Heartbeat timeout of %d seconds reached and not doing local backup, terminating transfer now.\n", param->hb_timeout);
                    break;
                } else {
                    lastfeedback = currpacketT;
                }
            }
            #endif
        }

         /* wait before handling the next packet */
         if (block_type == TS_BLOCK_TERMINATE) {
             usleep_that_works(10*ipd_time_max);
         }
         if (ipd_time > 0) {
             usleep_that_works(ipd_time);
         }

    }

    /*---------------------------
     * STOP TIMING
     *---------------------------*/
    gettimeofday(&stop, NULL);
    if (param->transcript_yn)
        xscript_data_stop(session, &stop);
    delta = 1000000LL * (stop.tv_sec - start.tv_sec) + stop.tv_usec - start.tv_usec;

    /* report on the transfer */
    if (param->verbose_yn)
        fprintf(stderr, "Server %d transferred %llu bytes in %0.2f seconds (%0.1f Mbps)\n",
                session->session_id, (ull_t)param->file_size, delta / 1000000.0, 
                8.0 * param->file_size / (delta * 1e-6 * 1024*1024) );

    /* close the transcript */
    if (param->transcript_yn)
        xscript_close(session, delta);

    #ifndef VSIB_REALTIME

    /* close the file */
    fclose(xfer->file);

    #else

    /* VSIB local disk copy: close file only if file output was requested */
    if (param->fileout) {
        fclose(xfer->file);
    }

    /* stop the VSIB */
    stop_vsib(session);
    fclose(xfer->vsib);

    #endif

    /* close the UDP socket */
    close(xfer->udp_fd);
    memset(xfer, 0, sizeof(*xfer));

    } //while(1)

}