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