/*------------------------------------------------------------------------ * int request_stop(session_t *session); * * Requests that the server stop transmitting data for the current * file transfer in the given session. This is done by sending a * retransmission request with a type of REQUEST_STOP. Returns 0 on * success and non-zero otherwise. Success means that we successfully * requested, not that we successfully halted. *------------------------------------------------------------------------*/ func (s *Session) requestStop() error { var retransmission []tsunami.Retransmission = []tsunami.Retransmission{tsunami.Retransmission{0, 0, 0}} retransmission[0].RequestType = tsunami.REQUEST_STOP /* send out the request */ _, err := s.connection.Write(tsunami.Retransmissions(retransmission).Bytes()) if err != nil { return err } return nil }
/*------------------------------------------------------------------------ * int update_stats(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.) *------------------------------------------------------------------------*/ func (s *Session) updateStats() { now_epoch := time.Now() /* the current Unix epoch */ var delta int64 /* time delta since last statistics update (usec) */ var delta_total int64 /* time delta since start of transmission (usec) */ var temp int64 /* temporary value for building the elapsed time */ var data_total float64 /* the total amount of data transferred (bytes) */ var data_this float64 /* the amount of data since last stat time */ var data_this_rexmit float64 /* the amount of data in received retransmissions */ // var data_this_goodpt float64 /* the amount of data as non-lost packets */ var retransmits_fraction float64 /* how many retransmit requests there were vs received blocks */ retransmission := make([]tsunami.Retransmission, 1) stats := &s.tr.stats /* find the total time elapsed */ delta = tsunami.Get_usec_since(stats.thisTime) temp = tsunami.Get_usec_since(stats.startTime) 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 = float64(s.param.blockSize) * float64(stats.totalBlocks) data_this = float64(s.param.blockSize) * float64(stats.totalBlocks-stats.thisBlocks) data_this_rexmit = float64(s.param.blockSize) * float64(stats.thisFlowRetransmitteds) // data_this_goodpt = float64(s.param.blockSize) * float64(stats.thisFlowOriginals) /* get the current UDP receive error count reported by the operating system */ stats.thisUdpErrors = tsunami.Get_udp_in_errors() /* precalculate some fractions */ retransmits_fraction = float64(stats.thisRetransmits) / (1.0 + float64(stats.thisRetransmits+stats.totalBlocks-stats.thisBlocks)) ringfill_fraction := float64(s.tr.ringBuffer.countData) / MAX_BLOCKS_QUEUED total_retransmits_fraction := float64(stats.totalRetransmits) / float64(stats.totalRetransmits+stats.totalBlocks) /* update the rate statistics */ // incoming transmit rate R = goodput R (Mbit/s) + retransmit R (Mbit/s) stats.thisTransmitRate = 8.0 * data_this / float64(d_seconds*u_mega) stats.thisRetransmitRate = 8.0 * data_this_rexmit / float64(d_seconds*u_mega) data_total_rate := 8.0 * data_total / float64(d_seconds_total*u_mega) fb := float64(s.param.history) / 100.0 // feedback ff := 1.0 - fb // feedforward // IIR filter rate R stats.transmitRate = fb*stats.transmitRate + ff*stats.thisRetransmitRate // IIR filtered composite error and loss, some sort of knee function stats.errorRate = fb*stats.errorRate + ff*500*100*(retransmits_fraction+ringfill_fraction) /* send the current error rate information to the server */ retransmission[0].RequestType = tsunami.REQUEST_ERROR_RATE retransmission[0].ErrorRate = uint32(s.tr.stats.errorRate) _, err := s.connection.Write(tsunami.Retransmissions(retransmission).Bytes()) if err != nil { fmt.Fprintln(os.Stderr, "Could not send error rate information", err) return } /* build the stats string */ statsFlags := s.statsFlags() //matlab // format := "%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\t%s\n" format := "%02d:%02d:%02d.%03d %4u %6.2fM %6.1fMbps %5.1f%% %7u %6.1fG %6.1fMbps %5.1f%% %5d %5d %7u %8u %8u %s\n" /* print to the transcript if the user wants */ statusLine := fmt.Sprintf(format, hours, minutes, seconds, milliseconds, stats.totalBlocks-stats.thisBlocks, stats.thisRetransmitRate, stats.thisTransmitRate, 100.0*retransmits_fraction, s.tr.stats.totalBlocks, data_total/float64(u_giga), data_total_rate, 100.0*total_retransmits_fraction, s.tr.retransmit.indexMax, s.tr.ringBuffer.countData, s.tr.blocksLeft, stats.thisRetransmits, uint64(stats.thisUdpErrors-stats.startUdpErrors), statsFlags) /* give the user a show if they want it */ if s.param.verbose { /* screen mode */ if s.param.outputMode == SCREEN_MODE { fmt.Printf("\033[2J\033[H") fmt.Printf("Current time: %s\n", now_epoch.Format(time.RFC3339)) fmt.Printf("Elapsed time: %02d:%02d:%02d.%03d\n\n", hours, minutes, seconds, milliseconds) fmt.Printf("Last interval\n--------------------------------------------------\n") fmt.Printf("Blocks count: %u\n", stats.totalBlocks-stats.thisBlocks) fmt.Printf("Data transferred: %0.2f GB\n", data_this/float64(u_giga)) fmt.Printf("Transfer rate: %0.2f Mbps\n", stats.thisTransmitRate) fmt.Printf("Retransmissions: %u (%0.2f%%)\n\n", stats.thisRetransmits, 100.0*retransmits_fraction) fmt.Printf("Cumulative\n--------------------------------------------------\n") fmt.Printf("Blocks count: %u\n", s.tr.stats.totalBlocks) fmt.Printf("Data transferred: %0.2f GB\n", data_total/float64(u_giga)) fmt.Printf("Transfer rate: %0.2f Mbps\n", data_total_rate) fmt.Printf("Retransmissions: %u (%0.2f%%)\n", stats.totalRetransmits, 100.0*total_retransmits_fraction) fmt.Printf("Flags : %s\n\n", statsFlags) fmt.Printf("OS UDP rx errors: %u\n", uint64(stats.thisUdpErrors-stats.startUdpErrors)) /* line mode */ } else { // s.iteration++ // if s.iteration%23 == 0 { // fmt.Printf(" last_interval transfer_total buffers transfer_remaining OS UDP\n") // fmt.Printf("time blk data rate rexmit blk data rate rexmit queue ring blk rt_len err \n") // } fmt.Printf("%s", statusLine) } os.Stdout.Sync() } s.XsriptDataLog(statusLine) /* reset the statistics for the next interval */ stats.thisBlocks = stats.totalBlocks stats.thisRetransmits = 0 stats.thisFlowOriginals = 0 stats.thisFlowRetransmitteds = 0 stats.thisTime = time.Now() }
/*------------------------------------------------------------------------ * int repeat_retransmit(session_t *session); * * Tries to repeat all of the outstanding retransmit requests for the * current transfer on the given session. Returns 0 on success and * non-zero on error. This also takes care of maintanence operations * on the transmission table, such as relocating the entries toward the * bottom of the array. *------------------------------------------------------------------------*/ func (s *Session) repeatRetransmit() error { var retransmission [MAX_RETRANSMISSION_BUFFER]tsunami.Retransmission s.tr.stats.thisRetransmits = 0 var count uint32 var entry uint32 var block uint32 transmit := &s.tr.retransmit // fmt.Fprintln(os.Stderr, "ttp_repeat_retransmit: index_max=", transmit.indexMax) /* discard received blocks from the list and prepare retransmit requests */ for entry = 0; (entry < transmit.indexMax) && (count < MAX_RETRANSMISSION_BUFFER); entry++ { /* get the block number */ block = transmit.table[entry] /* if we want the block */ if block != 0 && s.gotBlock(block) == 0 { /* save it */ transmit.table[count] = block /* insert retransmit request */ retransmission[count].RequestType = tsunami.REQUEST_RETRANSMIT retransmission[count].Block = block count++ } } /* if there are too many entries, restart transfer from earlier point */ if count >= MAX_RETRANSMISSION_BUFFER { /* restart from first missing block */ block = s.tr.blockCount if s.tr.blockCount > s.tr.gaplessToBlock+1 { block = s.tr.gaplessToBlock + 1 } retransmission[0].RequestType = tsunami.REQUEST_RESTART retransmission[0].Block = block _, err := s.connection.Write(tsunami.Retransmissions(retransmission[:1]).Bytes()) if err != nil { fmt.Fprintln(os.Stderr, "Could not send restart-at request") return err } /* remember the request so we can then ignore blocks that are still on the wire */ s.tr.restartPending = true s.tr.restartLastIndex = transmit.table[transmit.indexMax-1] s.tr.restartWireClearIndex = s.tr.blockCount if s.tr.blockCount > s.tr.restartLastIndex+s.tr.onWireEstimate { s.tr.restartWireClearIndex = s.tr.restartLastIndex + s.tr.onWireEstimate } /* reset the retransmission table and head block */ transmit.indexMax = 0 s.tr.nextBlock = block s.tr.stats.thisRetransmits = MAX_RETRANSMISSION_BUFFER /* queue is small enough */ } else { /* update to shrunken size */ transmit.indexMax = count /* update the statistics */ s.tr.stats.thisRetransmits = count s.tr.stats.totalRetransmits += count /* send out the requests */ if count > 0 { _, err := s.connection.Write(tsunami.Retransmissions(retransmission[:count]).Bytes()) if err != nil { fmt.Fprintln(os.Stderr, "Could not send retransmit requests") return err } } } fmt.Fprintln(os.Stderr, "ttp_repeat_retransmit: post-index_max=", transmit.indexMax) return nil }