【问题标题】:How to calculate the UDP transfer rate (kbps) in C#?如何在 C# 中计算 UDP 传输速率 (kbps)?
【发布时间】:2012-04-07 23:46:05
【问题描述】:

首先,我知道 UDP 不是一个可靠的协议,但我需要在 UDP 中这样做,而且我知道最终可能会发生的后果。

假设我有一个很长的 byte 数组,我将它分成 32 字节的块,分成了一个 List<byte[]>。这意味着发送的每个数据包的长度为 32 字节。

这些数据包将在foreach 循环中发送,我不在乎它们是否到达目的地。这意味着我不期待主持人的确认(至少现在是这样)。

我的问题是,我将如何计算服务器在接收数据包时的当前传输速率(最好以 kbps 为单位)?

我对如何实现这样的计算有点困惑......

【问题讨论】:

  • kbps = 千字节/秒,所以一个 StopWatch 和一个下载字节计数器 (bytes / 1024) / watch.Elapsed.TotalSeconds...
  • 确实如此。这篇文章中有 很多 的多余信息。
  • @caesay 我也没有,但是你要做什么......:/
  • @pst 我必须承认我有点不确定如何正确提出这个问题。这(传输率)只是我真正要做的事情的一个小细节,我发现这个网络业务非常混乱。也很晚了,我累了。很抱歉,我已尽力简化问题。

标签: c# sockets udp transfer rate


【解决方案1】:

如果您的问题是“我如何计算传输速率”,您可以将下载的字节总数除以经过的总秒数。

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

在 C# 中测量时间的一个好方法是 StopWatch 类,由于计算机科学中的 K 是 1024(或 2^10),因此您可以将字节数除以 1024(或移位),然后再除以与下载该千字节数所需的秒数。


如果您对平均传输率感兴趣,则需要以间隔测量下载的字节数。您可以使用二维列表来执行此操作,其中包含测量点和下载的字节以及所花费的时间。为简单起见,将其分解为一个进行计算的类

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);
    }
}

在您的下载方法中,中断一个新线程,创建上述类的实例,并在每个时间间隔调用BytesTransferred = totalBytes;。每次调用 TransferRate 时都会计算 TransferRate。 注意是字节/秒,如果你想要另一个单位,相应地除以1024。

【讨论】:

  • 明确一点,不是我。这似乎是我正在寻找的那种简单的答案...... :) 基本上,计时器应该在传输开始时启动,我只需将下载的总字节数除以秒表经过的时间,对吗?看起来很简单,但我有一个问题。那不是特定时刻的平均传输率而不是恒定的传输率吗?或者这是一个愚蠢的问题,我没有理解一些基本概念!?
  • @RicardoAmaral 是的,这将是一个总体平均值。如果您希望在更短的时间间隔内传输速率,您可以在下载某些内容并想要检查某些内容时在秒表上调用Stop()(基本上会暂停它),然后在您再次开始下载时调用Start()
  • 此外,如果您有兴趣在下载时 监控传输速率,您可以中断执行计算的线程,并可能每两秒触发一次事件左右(取决于你的需要)。不过,这也将是一个整体传输速率。
  • 但是我什么时候应该打电话给Stop()/Start()? 这就是我真正感到困惑的......平均费率似乎很容易实现(还没有尝试过)但我认为它会以恒定速率看起来更整洁。
  • @RicardoAmaral:这取决于您希望用户知道什么。如果传输速率是仅在您下载时,您应该在收到 udp 包时启动和停止。如果接收量很少,请在较大的下载块上进行测量。
【解决方案2】:

我们在防火墙上使用了一个简单的系统,每次您检查时都会更新传输速率。它还存储发送的全部信息。我刚刚从 FireBwall 的 Google 代码页面复制了这个。

/// <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";
        }
    }
}

在处理传输速率的异步环境中非常适合我们。

【讨论】:

  • 我建议使用 StopWatch 而不是 DateTime.Now,或者至少是 DateTime.UtcNow,它们没有时区的开销,因为您只对时间差异感兴趣,而不是本地时间。
  • 另外,你的代码有点多余。 while (bytes &gt; 1024) { kbytes++; bytes -= 1024; } 可以替换为 kbytes = bytes / 1024; bytes %= 1024; 导致两个操作而不是一段时间,并逐位减少,对于 每个 单元。
  • 在您的AddBytes 方法中,为什么要将计数乘以 8,在我看来,您提供了虚假信息,说您的传输速度实际上比实际速度快 8 倍?
  • 虽然我很欣赏你的回答和长代码示例,但我认为这比我要找的有点太多了。
  • AddBytes 评论说它用词不当,实际上是在观察比特,因为大多数网络传输速率都是以某种形式的比特每秒来衡量的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-02
  • 2012-04-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多