Click here to Skip to main content
15,884,628 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I'm writing a program that downloads stuff from the internet. While the program is working fine, I want to get one critical part reviewed. I use System.Diagnostics.StopWatch object to measure the time taken for the ns (NetworkStream) object to read the bytes in buffer. I then divide the number of elapsed seconds in stopwatch by number of bytes received to calculate the speed of my download in KBPS. However, this method is giving me some extraordinarily large speeds (in excess of 8000KBPS), whereas I know that my internet bandwidth is not that fast. Is there anything wrong with this method? If not, then what is the correct way of getting the download speed? Here is my code:

C#
while(true)
{

    sw.Start();
    int n = ns.Read(buffer, 0, buffer.Length);
    sw.Stop();
    Speed = (float)n / sw.Elapsed.TotalSeconds;

    if (n==0)
        break;

    fs.Write(buffer,0,n);

    BytesRead+=n; //TODO: Persist the bytesRead object somewhere, so that it can be taken back for resumes
    bytesToRead -= n;
    OnProgress(this,new System.EventArgs());
    if (Status == DownloadStatusEnum.Paused) break;
}
Posted

You aren't really timing the data transfer speed - you are timing the transfer of data from a buffered stream to a memory block. The difference is that if the stream is already pre filled with more bytes than your buffer can hold, then the time you get is only that taken to copy the stream data into your buffer area - if it is partially filled, then your result is the time taken to receive the remaining bytes to fill your buffer, plus the data copy time.

You would be better off using a timer that starts when you issue the download command, and leaving it running continuously until the data transfer is complete. Then work out the bytes-per-second rate from the total number of bytes processed so far divided by current value of the timer. This won't give you a single magic number - it will vary as the data transfer goes on - but provided your data being transferred is large enough it will be pretty accurate at the end of the process.

But do remember that the bytes per second will not be the same as your internet bandwidth, for a lot of reasons:
1) It may not be the only thing going on
2) Some of the data transfer will be overhead to address and error check the data
3) The speed will also be related to the source speed and the general "busy-ness" of the internet and your ISP.
 
Share this answer
 
Comments
Prahlad Yeri 14-Apr-13 6:06am    
@OriginalGriff - Thanks for the solution. Regarding "You would be better off using a timer that starts when you issue the download command" - what do you suggest is a better approach for timer? - using the System.Diagnostics.StopWatch as I'm doing now ? or - using Syste.DateTime.Now to store in a start variable ?
OriginalGriff 14-Apr-13 6:43am    
That's a difficult one - the Stopwatch approach will work, but the maximum time period it can work over is not defined - it depends on the underlying hardware tick frequency and the size of the ElapsedTicks property, which is a 64 bit signed int. But since the hardware tick frequency is not defined or limited at all, the maximum time a stopwatch can time for is not defined either.
At the moment, we don't have to worry too much about this, as even with a 1 nanosecond tick, you can still run a stopwatch for years before it overflows - but that may not be true in a few years time!

Since your download measurement gets more accurate as the size of the download increases, I would be tempted to go with the DateTime approach, simply because you don't need the actual accuracy of the stopwatch to give a precise download speed result (strange, but true - the tiny difference in precision between Datetime.Now and using a stopwatch is irrelevant over the larger scale of the whole download) and it uses less system resources to measure as a timer does not need to be running in order to use it.
Prahlad Yeri 14-Apr-13 6:10am    
Also consider that I might have to stop this timer in between if the user sends a "pause" command.
OriginalGriff 14-Apr-13 6:48am    
Didn't see this before I answered your previous comment! :doh:

Easier to do with a stopwatch - you just issue a Stopwatch.Stop on pause and the Stopwatch.Start on resume without a stopwatch.Reset in between - but I'd still go with the DateTime.Now approach, with the addition of a "elapsedExtra" TimeSpan value to add in. Slightly more complex, but not a lot. When you pause, calculate the elapsed time, and add it to the "elapsedExtra" value. At the end, add the two TimeSpan values to get the total time taken.
CSS
<pre lang="Delphi"><pre lang="c#"><pre lang="CSS"><pre lang="CSS"><pre lang="CSS"><pre lang="CSS"><pre lang="Delphi"><pre lang="HTML"><pre lang="objc"><pre lang="Python"><pre lang="vb"><pre lang="xml"><pre lang="SQL"><pre lang="objc"><pre lang="Javascript"><pre lang="Javascript"><pre lang="Javascript"><pre lang="Javascript"><pre lang="Javascript"><pre lang="Javascript"><pre lang="Javascript"><pre lang="Javascript"><pre lang="Javascript"><pre lang="Javascript"><pre lang="Javascript"><pre lang="Javascript"><pre lang="Javascript"><pre lang="Javascript">
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900