-1

First of all, I know that UDP is not a reliable protocol but I need to this in UDP and I'm aware of the consequences that may eventually happen.

Let's assume I have a long byte array that I have split in chunks of 32bytes into a List<byte[]>. This means that each packet sent will be 32bytes in length.

These packets will be sent in a foreach loop and I don't care if they arrive at the destination or not. Meaning I'm not expecting for a confirmation from the host (at least for now).

And my question is, how would I go in calculating the current transfer rate (preferably in kbps) of the server while it's receiving packets?

I'm a little confused on how could I achieve such calculation...

rfgamaral
  • 16,546
  • 57
  • 163
  • 275
  • 2
    kbps = kilobytes / second, so a `StopWatch` and a counter of downloaded bytes, `(bytes / 1024) / watch.Elapsed.TotalSeconds`... – Patrick Apr 08 '12 at 00:02
  • 1
    Indeed. There is *lots* of superfluous information in this post. –  Apr 08 '12 at 00:03
  • @caesay Me neither, but what you gonna do... :/ – rfgamaral Apr 08 '12 at 13:37
  • 2
    @pst I must confess that I was a bit unsure how to properly put this question. This (the transfer rate) is just a tiny detail of what I really have to do and I find this networking business very confusing. It was also very late and I was tired. Sorry about that, I tried to do my best to simplify the question. – rfgamaral Apr 08 '12 at 13:53

2 Answers2

5

If your question is "how do I calculate the transfer rate", you can divide the total number of downloaded bytes by the total number of seconds that passed.

   bytes
 --------- = transfer rate
  seconds

A good way to measure time in C# is the StopWatch class, and since K in computer science is 1024 (or 2^10) you can divide the number of bytes with 1024 (or shift it) and then divide it with the number of seconds it took to download that number of kilobytes.


If you are interested in an average transfer rate, you need to measure the downloaded bytes in intervals. You could do this with a two dimensional list, holding measure points and the downloaded bytes and time it took. For simplicity, break it of into a class that does the calculations

private readonly Stopwatch watch;
private readonly long[,] average;

public .ctor() {
    // use 10 measure points, for a larger or smaller average, adjust the 10
    average = new long[10, 2];
    watch = Stopwatch.StartNew();
}

public long BytesTransferred {
    set {
        for (int i = average.GetLength(0) - 1; i > 0; --i) {
            average[i, 0] = average[i - 1, 0];
            average[i, 1] = average[i - 1, 1];
        }
        average[0, 0] = sent = value;
        average[0, 1] = watch.ElapsedMilliseconds;
    }
}

public long TransferRate {
    get {
        int l = average.GetLength(0) - 1;
        double bytes = average[0, 0] - average[l, 0];
        double seconds = (average[0, 1] - average[l, 1]) / 1000d;
        return (long)(bytes / seconds);
    }
}

In your download method, break of a new thread, create an instance of the class above, and call BytesTransferred = totalBytes; in every interval. The TransferRate will be calculated every time you call TransferRate. Note that it is bytes/s, if you want another unit, divide by 1024 accordingly.

Patrick
  • 17,669
  • 6
  • 70
  • 85
  • Just to be clear, it wasn't me. This seems the kind of simple answer I'm looking for... :) Basically, the timer should be started when the transfer begins and I just have to divide the total downloaded bytes with the elapsed time from the stopwatch, right? It seems simple enough but I one one question. Won't that be the average transfer rate instead of a constant transfer rate in a specific moment in time? Or maybe this is an idiotic question and I'm failing to understand some basic concept!? – rfgamaral Apr 08 '12 at 13:44
  • @RicardoAmaral Yes, that will be an overall average. If you want the transfer rate on a smaller time lapse, you could call `Stop()` on the StopWatch (which basically pauses it) when you downloaded something and want to check something perhaps, and then call `Start()` when you start downloading again. – Patrick Apr 08 '12 at 13:52
  • Further more, if you are interested in monitoring the transfer rate *while* you are downloading, you could break of a thread which does the calculation and perhaps fires an event every two seconds or so (depending on your needs). This will also be an overall transfer rate though. – Patrick Apr 08 '12 at 13:54
  • But when exactly should I call `Stop()`/`Start()?` That's what I'm really confused about... The average rate seems simple enough to implement (haven't tried it yet) but I think it would look neater with a constant rate instead. – rfgamaral Apr 08 '12 at 13:56
  • @RicardoAmaral: That depends on what you want your user to know. If the transfer rate is *only when you are downloading* you should start and stop when you receive udp packages. If one receive is to few, measure it over larger download chunks. – Patrick Apr 08 '12 at 14:07
  • @Patrick: +1 Awesome code snippet! Thanks for sharing. Btw 'sent' variable is missing a declaration ;) – Aoi Karasu Feb 19 '14 at 19:32
1

We use a simple system on fireBwall that updates the transfer rate every time you check it. It also stores the total information sent. I just copied this over from the Google Code page for fireBwall.

/// <summary>
/// Class to manage an adapters total transfered data
/// </summary>
public class BandwidthCounter
{
    /// <summary>
    /// Class to manage an adapters current transfer rate
    /// </summary>
    class MiniCounter
    {
        public uint bytes = 0;
        public uint kbytes = 0;
        public uint mbytes = 0;
        public uint gbytes = 0;
        public uint tbytes = 0;
        public uint pbytes = 0;
        DateTime lastRead = DateTime.Now;

        /// <summary>
        /// Adds bits(total misnomer because bits per second looks a lot better than bytes per second)
        /// </summary>
        /// <param name="count">The number of bits to add</param>
        public void AddBytes(uint count)
        {
            bytes += count;
            while (bytes > 1024)
            {
                kbytes++;
                bytes -= 1024;
            }
            while (kbytes > 1024)
            {
                mbytes++;
                kbytes -= 1024;
            }
            while (mbytes > 1024)
            {
                gbytes++;
                mbytes -= 1024;
            }
            while (gbytes > 1024)
            {
                tbytes++;
                gbytes -= 1024;
            }
            while (tbytes > 1024)
            {
                pbytes++;
                tbytes -= 1024;
            }
        }

        /// <summary>
        /// Returns the bits per second since the last time this function was called
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            if (pbytes > 0)
            {
                double ret = (double)pbytes + ((double)((double)tbytes / 1024));
                ret = ret / (DateTime.Now - lastRead).TotalSeconds;
                lastRead = DateTime.Now;
                string s = ret.ToString();
                if (s.Length > 6)
                    s = s.Substring(0, 6);
                return s + " Pb";
            }
            else if (tbytes > 0)
            {
                double ret = (double)tbytes + ((double)((double)gbytes / 1024));
                ret = ret / (DateTime.Now - lastRead).TotalSeconds;
                lastRead = DateTime.Now;
                string s = ret.ToString();
                if (s.Length > 6)
                    s = s.Substring(0, 6);
                return s + " Tb";
            }
            else if (gbytes > 0)
            {
                double ret = (double)gbytes + ((double)((double)mbytes / 1024));
                ret = ret / (DateTime.Now - lastRead).TotalSeconds;
                lastRead = DateTime.Now;
                string s = ret.ToString();
                if (s.Length > 6)
                    s = s.Substring(0, 6);
                return s + " Gb";
            }
            else if (mbytes > 0)
            {
                double ret = (double)mbytes + ((double)((double)kbytes / 1024));
                ret = ret / (DateTime.Now - lastRead).TotalSeconds;
                lastRead = DateTime.Now;
                string s = ret.ToString();
                if (s.Length > 6)
                    s = s.Substring(0, 6);
                return s + " Mb";
            }
            else if (kbytes > 0)
            {
                double ret = (double)kbytes + ((double)((double)bytes / 1024));
                ret = ret / (DateTime.Now - lastRead).TotalSeconds;
                lastRead = DateTime.Now;
                string s = ret.ToString();
                if (s.Length > 6)
                    s = s.Substring(0, 6);
                return s + " Kb";
            }
            else
            {
                double ret = bytes;
                ret = ret / (DateTime.Now - lastRead).TotalSeconds;
                lastRead = DateTime.Now;
                string s = ret.ToString();
                if (s.Length > 6)
                    s = s.Substring(0, 6);
                return s + " b";
            }
        }
    }

    private uint bytes = 0;
    private uint kbytes = 0;
    private uint mbytes = 0;
    private uint gbytes = 0;
    private uint tbytes = 0;
    private uint pbytes = 0;
    MiniCounter perSecond = new MiniCounter();

    /// <summary>
    /// Empty constructor, because thats constructive
    /// </summary>
    public BandwidthCounter()
    {

    }

    /// <summary>
    /// Accesses the current transfer rate, returning the text
    /// </summary>
    /// <returns></returns>
    public string GetPerSecond()
    {
        string s = perSecond.ToString() + "/s";
        perSecond = new MiniCounter();
        return s;
    }

    /// <summary>
    /// Adds bytes to the total transfered
    /// </summary>
    /// <param name="count">Byte count</param>
    public void AddBytes(uint count)
    {
        // overflow max
        if ((count * 8) >= Int32.MaxValue)
            return;

        count = 8 * count;
        perSecond.AddBytes(count);
        bytes += count;
        while (bytes > 1024)
        {
            kbytes++;
            bytes -= 1024;
        }
        while (kbytes > 1024)
        {
            mbytes++;
            kbytes -= 1024;
        }
        while (mbytes > 1024)
        {
            gbytes++;
            mbytes -= 1024;
        }
        while (gbytes > 1024)
        {
            tbytes++;
            gbytes -= 1024;
        }
        while (tbytes > 1024)
        {
            pbytes++;
            tbytes -= 1024;
        }
    }

    /// <summary>
    /// Prints out a relevant string for the bits transfered
    /// </summary>
    /// <returns></returns>
    public override string ToString()
    {
        if (pbytes > 0)
        {
            double ret = (double)pbytes + ((double)((double)tbytes / 1024));
            string s = ret.ToString();
            if (s.Length > 6)
                s = s.Substring(0, 6);
            return s + " Pb";
        }
        else if (tbytes > 0)
        {
            double ret = (double)tbytes + ((double)((double)gbytes / 1024));
            string s = ret.ToString();
            if (s.Length > 6)
                s = s.Substring(0, 6);
            return s + " Tb";
        }
        else if (gbytes > 0)
        {
            double ret = (double)gbytes + ((double)((double)mbytes / 1024));
            string s = ret.ToString();
            if (s.Length > 6)
                s = s.Substring(0, 6);
            return s + " Gb";
        }
        else if (mbytes > 0)
        {
            double ret = (double)mbytes + ((double)((double)kbytes / 1024));
            string s = ret.ToString();
            if (s.Length > 6)
                s = s.Substring(0, 6);
            return s + " Mb";
        }
        else if (kbytes > 0)
        {
            double ret = (double)kbytes + ((double)((double)bytes / 1024));
            string s = ret.ToString();
            if (s.Length > 6)
                s = s.Substring(0, 6);
            return s + " Kb";
        }
        else
        {
            string s = bytes.ToString();
            if (s.Length > 6)
                s = s.Substring(0, 6);
            return s + " b";
        }
    }
}

Works pretty well for us in an asynchronous environment dealing with transfer rates.

bwall
  • 1,501
  • 1
  • 10
  • 6
  • 1
    I would recommend using StopWatch instead of `DateTime.Now`, or at least `DateTime.UtcNow`, which don't have the overhead of timezone, since you are only interested in time diffs, and not local time. – Patrick Apr 08 '12 at 12:33
  • Also, your code is a bit redundant. `while (bytes > 1024) { kbytes++; bytes -= 1024; }` can be replaced with `kbytes = bytes / 1024; bytes %= 1024;` resulting in two operations instead of a while and reducing bit by bit, for *each* of the units. – Patrick Apr 08 '12 at 12:40
  • In your `AddBytes` method, why are you multiplying count with 8, to me that looks like you are providing false information, saying that your transfer rate is actually eight times faster than it really is? – Patrick Apr 08 '12 at 12:46
  • Although I appreciate your answer and the long code example, I think this is a bit too much than what I'm looking for. – rfgamaral Apr 08 '12 at 13:38
  • The AddBytes comment says its a misnomer, its actually watching the bits, as most network transfer rates are measured in some form of bits per second. – bwall Apr 09 '12 at 13:21
  • As for the redundancy, kbytes would have to be kbytes += bytes / 1024 since its a value that's is increasing. Thank you though for the input, this was a nice code review, and I have some changes to make now. :) – bwall Apr 09 '12 at 13:59