1

How can I correctly calculate download speed from Httpclient ReadAsStreamAsync? I'm trying to get the old download speed by subtracting TotalBytesRead - OldTotalBytesRead, it works as intended, but the the problem is on the elapsed time, how can I do it that it will calculate exactly when the elapsed time is equal to 1000ms, in the code below, the speed calculate run > the elapsed time, to the speed is sometimes inaccurate, making the first value too large, then the seconds is small or vise versa.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleDownloadApp
{
    class Program
    {

        [DllImport("Shlwapi.dll", CharSet = CharSet.Auto)]
        private static extern long StrFormatByteSize(
            long fileSize,
            [MarshalAs(UnmanagedType.LPTStr)] StringBuilder buffer,
            int bufferSize);

        static HttpClient _httpClient = new HttpClient();

        static long ContentLength = 0;
        static long TotalBytesRead = 0;
        static long OldTotalBytesRead = 0;

        static void Main(string[] args)
        {
            string url = "http://ipv4.download.thinkbroadband.com/10MB.zip";
            string savePath = "10MB.zip";

            Task.Run(() => Download(url, savePath)).Wait();
        }

        static async Task Download(string url, string saveAs)
        {
            using (HttpResponseMessage response = await _httpClient.GetAsync(url, HttpCompletionOption.ResponseHeadersRead))
            {
                response.EnsureSuccessStatusCode();

                ContentLength = response.Content.Headers.ContentLength ?? 0;

                byte[] buffer = new byte[1024];

                Stopwatch st = new Stopwatch();
                st.Start();

                using (Stream contentStream = await response.Content.ReadAsStreamAsync())
                using (var fileStream = new FileStream(saveAs, FileMode.Create, FileAccess.Write, FileShare.None, buffer.Length, true))
                {
                    while(true)
                    {
                        var bytesRead = await contentStream.ReadAsync(buffer, 0, buffer.Length);
                        if(bytesRead == 0)
                        {
                            Console.WriteLine("Download complete.");
                            break;
                        }

                        await fileStream.WriteAsync(buffer, 0, bytesRead);

                        TotalBytesRead += bytesRead;

                        var elapsedTime = st.ElapsedMilliseconds;
                        if (elapsedTime >= 1000)
                        {
                            st.Reset();
                            st.Start();

                            long bytesChanged = TotalBytesRead - OldTotalBytesRead;
                            OldTotalBytesRead = TotalBytesRead;

                            Console.WriteLine("Elapsed time: {0} || Download speed: {1} || {2}/{3}",
                                elapsedTime,
                                StrFormatByteSize(bytesChanged / elapsedTime),
                                StrFormatByteSize(TotalBytesRead),
                                StrFormatByteSize(ContentLength));
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Converts a numeric value into a string that represents the number expressed as a size value in
        /// bytes, kilobytes, megabytes, or gigabytes, depending on the size.
        /// </summary>
        /// <param name="filelength">The numeric value to be converted.</param>
        /// <returns>the converted string</returns>
        public static string StrFormatByteSize(long filesize)
        {
            StringBuilder sb = new StringBuilder(11);
            StrFormatByteSize(filesize, sb, sb.Capacity);
            return sb.ToString();
        }
    }
}

result from downloading 10mb files result (can't add photo yet)

  • Windows uses timers to transfer data from application to network interface. Data size of TCP packets are max size of 1500 bytes. You are getting various size messages which indicates you application isn't continuously sending data at a fixed rate. The windows event timers will periodically grab what ever data is in the TCP buffer. – jdweng Apr 28 '18 at 08:04
  • At a guess; Is there an async-progress callback, and does it include a 'bytes transferred' datum? If so, track a time delta between callbacks and calculate the rate that way. – Immersive Apr 28 '18 at 08:09
  • no there is no async callback, i updated the post that include all codes – Thomas Scott Apr 28 '18 at 09:04
  • I don't see a problem with that. Yes, elapsed time is not exactly 1000ms, but it still reflects how much time approximately it took to download `bytesChanged` bytes. Though it's better to use total elapsed time and total bytes read to calculate speed - value will be smoother. Per second fluctuations might be quite high. – Evk Apr 28 '18 at 11:03

0 Answers0