How to get accurate download/upload speed in C#.NET?
Asked Answered
R

4

11

I want to get accurate download/upload speed through a Network Interface using C# .NET I know that it can be calculated using GetIPv4Statistics().BytesReceived and putting the Thread to sleep for sometime. But it's not giving the output what I am getting in my browser.

Refractory answered 28/11, 2012 at 8:13 Comment(10)
How much of a difference is it? What other network traffic is there?Overcareful
I'm not seeing a constant speed in my application whereas my browser shows something like constant speed. My application is fluctuating very much.Refractory
How is the throughput being calculated? I would expect it to not fluctuate much. Doing so might indicate the moving average is being calculated incorrectly.Overcareful
I am taking a DateTime begin then I record the ByteReceived.Then I call Thread.Sleep(200) and then again calculating the ByteReceived and DateTime end. Now I subtract both of them and divide the ByteReceived with the seconds calculated from DateTime.Refractory
Try to use a moving average. It will remove the excess fluctuations that may exist with such an approach. (However, I would still expect it to be "fairly constant" when downloading a large file from a single server.)Overcareful
The question in the link that you have posted, was asked by me only.Refractory
What do you mean by "moving average"? Can you throw some light?Refractory
en.wikipedia.org/wiki/Moving_average - e.g. keep the values from the last 10 200ms "reads". In LINQ, Sum can be used to make that relatively easy.Overcareful
Suppose, the download speed in my browser is 60KB/Sec. but my application is showing 2KB/Sec, 80KB/S, 120KB/Sec and also sometime 60KB/Sec. So which approach should I take?Refractory
let us continue this discussion in chatRefractory
J
4

By looking at another answer to a question you posted in NetworkInterface.GetIPv4Statistics().BytesReceived - What does it return? I believe the issue might be that you are using to small intervals. I believe the counter only counts whole packages, and if you for example are downloading a file the packages might get as big as 64 KB (65,535 bytes, IPv4 max package size) which is quite a lot if your maximum download throughput is 60 KB/s and you are measuring 200 ms intervals.

Given that your speed is 60 KB/s I would have set the running time to 10 seconds to get at least 9 packages per average. If you are writing it for all kinds of connections I would recommend you make the solution dynamic, ie if the speed is high you can easily decrease the averaging interval but in the case of slow connections you must increase the averaging interval.

Either do as @pst recommends by having a moving average or simply increase the sleep up to maybe 1 second.

And be sure to divide by the actual time taken rather than the time passed to Thread.Sleep().

Additional thought on intervals

My process would be something like this, measure for 5 second and gather data, ie bytes recieved as well as the number of packets.

var timePerPacket = 5000 / nrOfPackets; // Time per package in ms
var intervalTime = Math.Max(d, Math.Pow(2,(Math.Log10(timePerPacket)))*100);

This will cause the interval to increase slowly from about several tens of ms up to the time per packet. That way we always get at least (on average) one package per interval and we will not go nuts if we are on a 10 Gbps connection. The important part is that the measuring time should not be linear to the amount of data received.

Jigaboo answered 28/11, 2012 at 8:54 Comment(8)
Thanks. It's giving the output almost correct if I increase the interval. But I have to calculate other NetworkInterfaces' speed also. So, it will be too slow to update one particular network interface's speed value. So, I think I should go for @pst's implementation. What do you think?Refractory
@soham.m17 I would also recommend that. And see my last edit, the faster the connection the smaller "time amount" is necessary due to the ratio between throughput and max IPv4 package size. Always be sure to average over at least a handful of packages :)Jigaboo
Seems unlikely that ip packets >15xx bytes are used. Few internet connections support a larger MTU.Harmsworth
Yeah! I was not at all thinking about the packets. Very good point. So, I think I should keep at least 20 last records with 200ms interval. and I should also implement a solution so that It can update the 200ms value if the speed is high or low.Refractory
@Harmsworth You are of course correct in that 64KB frames are rarely used, but even jumbo frames at 9KB would severely impact a 60 KB/sec connection at 200 ms measuring intervals. Lets say he is using wifi 802.11n as well at home which powers down between bursts, that would also cause the same effect. We cannot simply know, but it is always good to plan for the worst :)Jigaboo
@flinderberg, can you discuss what interval should be used for Thread.Sleep() for certain connections with example of their speeds.Refractory
@soham.m17 Added some additional thoughts on interval lenght. Does it help?Jigaboo
@Jigaboo thanks for the update. Sorry for late reply. I cannot get the fact how to get the number of packets received. What will be the nature of the packets? Unicast? Non-Unicast? I know the number of bytes received. And also intervalTime calculation is clear to me. Please explain. Also What is 'd'?Refractory
O
11

Here is a quick snippet of code from LINQPad. It uses a very simple moving average. It shows "accurate speeds" using "Speedtest.net". Things to keep in mind are Kbps is in bits and HTTP data is often compressed so the "downloaded bytes" will be significantly smaller for highly compressible data. Also, don't forget that any old process might be doing any old thing on the internet these days (without stricter firewall settings) ..

I like flindenberg's answer (don't change the accept), and I noticed that some polling periods would return "0" that aligns with his/her conclusions.

Use at your own peril.

void Main()
{
    var nics = System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces();
    // Select desired NIC
    var nic = nics.Single(n => n.Name == "Local Area Connection");
    var reads = Enumerable.Empty<double>();
    var sw = new Stopwatch();
    var lastBr = nic.GetIPv4Statistics().BytesReceived;
    for (var i = 0; i < 1000; i++) {

        sw.Restart();
        Thread.Sleep(100);
        var elapsed = sw.Elapsed.TotalSeconds;
        var br = nic.GetIPv4Statistics().BytesReceived;

        var local = (br - lastBr) / elapsed;
        lastBr = br;

        // Keep last 20, ~2 seconds
        reads = new [] { local }.Concat(reads).Take(20);

        if (i % 10 == 0) { // ~1 second
            var bSec = reads.Sum() / reads.Count();
            var kbs = (bSec * 8) / 1024; 
            Console.WriteLine("Kb/s ~ " + kbs);
        }
    }
}
Overcareful answered 28/11, 2012 at 9:5 Comment(4)
Thanks. but I don't get the if (i % 10 == 0) { // ~1 second part. You are showing the output in every 1 sec. is it so?Refractory
@soham.m17 Yes. It's easier to read than every 1/10th of a second. Alternatively, just poll once a second and adjust the SMA to say N (4 maybe?) seconds ..Overcareful
@soham.m17: SMA - simple moving average. You should check links which are provided by someone who is trying to help, taking into account it is the topic you've created.Seawright
Okay. Sorry. I had gone through only first few lines of that wikipedia page. Now I have seen. Very Sorry. Will keep your words in mind for future.Refractory
E
9

Please try this. To check internet connection speed.

 public double CheckInternetSpeed()
 {
        // Create Object Of WebClient
        System.Net.WebClient wc = new System.Net.WebClient();

        //DateTime Variable To Store Download Start Time.
        DateTime dt1 = DateTime.UtcNow;

        //Number Of Bytes Downloaded Are Stored In ‘data’
        byte[] data = wc.DownloadData("http://google.com");

        //DateTime Variable To Store Download End Time.
        DateTime dt2 = DateTime.UtcNow;

        //To Calculate Speed in Kb Divide Value Of data by 1024 And Then by End Time Subtract Start Time To Know Download Per Second.
        return Math.Round((data.Length / 1024) / (dt2 - dt1).TotalSeconds, 2);            
    }

It gives you the speed in Kb/Sec and share the result.

Eldon answered 19/3, 2015 at 18:45 Comment(0)
J
4

By looking at another answer to a question you posted in NetworkInterface.GetIPv4Statistics().BytesReceived - What does it return? I believe the issue might be that you are using to small intervals. I believe the counter only counts whole packages, and if you for example are downloading a file the packages might get as big as 64 KB (65,535 bytes, IPv4 max package size) which is quite a lot if your maximum download throughput is 60 KB/s and you are measuring 200 ms intervals.

Given that your speed is 60 KB/s I would have set the running time to 10 seconds to get at least 9 packages per average. If you are writing it for all kinds of connections I would recommend you make the solution dynamic, ie if the speed is high you can easily decrease the averaging interval but in the case of slow connections you must increase the averaging interval.

Either do as @pst recommends by having a moving average or simply increase the sleep up to maybe 1 second.

And be sure to divide by the actual time taken rather than the time passed to Thread.Sleep().

Additional thought on intervals

My process would be something like this, measure for 5 second and gather data, ie bytes recieved as well as the number of packets.

var timePerPacket = 5000 / nrOfPackets; // Time per package in ms
var intervalTime = Math.Max(d, Math.Pow(2,(Math.Log10(timePerPacket)))*100);

This will cause the interval to increase slowly from about several tens of ms up to the time per packet. That way we always get at least (on average) one package per interval and we will not go nuts if we are on a 10 Gbps connection. The important part is that the measuring time should not be linear to the amount of data received.

Jigaboo answered 28/11, 2012 at 8:54 Comment(8)
Thanks. It's giving the output almost correct if I increase the interval. But I have to calculate other NetworkInterfaces' speed also. So, it will be too slow to update one particular network interface's speed value. So, I think I should go for @pst's implementation. What do you think?Refractory
@soham.m17 I would also recommend that. And see my last edit, the faster the connection the smaller "time amount" is necessary due to the ratio between throughput and max IPv4 package size. Always be sure to average over at least a handful of packages :)Jigaboo
Seems unlikely that ip packets >15xx bytes are used. Few internet connections support a larger MTU.Harmsworth
Yeah! I was not at all thinking about the packets. Very good point. So, I think I should keep at least 20 last records with 200ms interval. and I should also implement a solution so that It can update the 200ms value if the speed is high or low.Refractory
@Harmsworth You are of course correct in that 64KB frames are rarely used, but even jumbo frames at 9KB would severely impact a 60 KB/sec connection at 200 ms measuring intervals. Lets say he is using wifi 802.11n as well at home which powers down between bursts, that would also cause the same effect. We cannot simply know, but it is always good to plan for the worst :)Jigaboo
@flinderberg, can you discuss what interval should be used for Thread.Sleep() for certain connections with example of their speeds.Refractory
@soham.m17 Added some additional thoughts on interval lenght. Does it help?Jigaboo
@Jigaboo thanks for the update. Sorry for late reply. I cannot get the fact how to get the number of packets received. What will be the nature of the packets? Unicast? Non-Unicast? I know the number of bytes received. And also intervalTime calculation is clear to me. Please explain. Also What is 'd'?Refractory
P
0

The SSL handshake takes some time as a result modified @sandeep answer. I first created a request and then measure the time to download the content. I believe this is a little more accurate but still not 100%. It is an approximation.

public async Task<int> GetInternetSpeedAsync(CancellationToken ct = default)
{
    const double kb = 1024;
    
    // do not use compression
    using var client = new HttpClient();

    int numberOfBytesRead = 0;

    var buffer = new byte[10240].AsMemory();
    
    // create request
    var stream = await client.GetStreamAsync("https://www.google.com", ct);

    // start timer
    DateTime dt1 = DateTime.UtcNow;

    // download stuff
    while (true)
    {
        var i = await stream.ReadAsync(buffer, ct);
        if (i < 1)
            break;

        numberOfBytesRead += i;
    }

    // end timer
    DateTime dt2 = DateTime.UtcNow;

    double kilobytes = numberOfBytesRead / kb;
    double time = (dt2 - dt1).TotalSeconds;

    // speed in Kb per Second.
    return (int)(kilobytes / time);
}
Passivism answered 27/8, 2022 at 19:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.