/*------------------------------------------------------------------------ * 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; }
/*------------------------------------------------------------------------ * 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; }
/*------------------------------------------------------------------------ * 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) }