Как я могу ограничить скорость загрузки с помощью TcpClient?

Я пишу утилиту, которая будет загружать кучу файлов, и хотел бы предоставить возможность ограничивать скорость загрузки. Каков наилучший подход к ограничению скорости загрузки при использовании класса TcpClient? Мой первый порыв — вызвать NetworkStream.Write() с ограниченным числом байтов за раз, ожидая между вызовами (и пропуская вызов, если поток еще не закончил запись) до тех пор, пока буфер не будет загружен. Кто-нибудь реализовывал что-то подобное раньше?


person Luke    schedule 30.06.2009    source источник


Ответы (4)


Реализовать ограничение скорости относительно просто, взгляните на следующий фрагмент:

const int OneSecond = 1000;

int SpeedLimit = 1024; // Speed limit 1kib/s

int Transmitted = 0;
Stopwatch Watch = new Stopwatch();
Watch.Start();
while(...)
{
    // Your send logic, which return BytesTransmitted
    Transmitted += BytesTransmitted;

    // Check moment speed every five second, you can choose any value
    int Elapsed = (int)Watch.ElapsedMilliseconds;
    if (Elapsed > 5000)
    {
        int ExpectedTransmit = SpeedLimit * Elapsed / OneSecond;
        int TransmitDelta = Transmitted - ExpectedTransmit;
        // Speed limit exceeded, put thread into sleep
        if (TransmitDelta > 0)
            Thread.Wait(TransmitDelta * OneSecond / SpeedLimit);

        Transmitted = 0;
        Watch.Reset();
    }
}
Watch.Stop();

Это черновик непроверенного кода, но я думаю, что этого достаточно, чтобы понять основную идею.

person arbiter    schedule 01.07.2009
comment
Я думаю, вам нужен watch.restart, поскольку он сбрасывает прошедшее время до 0, а затем перезапускает таймер. watch.reset просто устанавливает время обратно на 0 и останавливает itmer - person zeocrash; 30.09.2015

Вместо того, чтобы создавать это, вы также можете рассмотреть BITS (фоновая служба передачи через Интернет), которая позволяет пользователю (или администратору) настраивать пропускную способность и будет обрабатывать очереди передач.

Это требует специальной поддержки на сервере (в том числе в IIS, но требует включения).

person Richard    schedule 30.06.2009
comment
Хм, к сожалению, мой сервер находится в стеке LAMP, поэтому BITS не будет работать. Однако полезно знать об этом. - person Luke; 30.06.2009

Я знаю, что это старая запись, но я думаю, что эта информация может быть полезна для тех, кто попал сюда через Google или другой веб-поиск.

Если мы воспользуемся решением, опубликованным «арбитром», мы обнаружим, что поток будет отправлять большой объем данных, а затем будет спать в течение большого количества времени, потому что обычно ограничения скорости превышают 32–200 КБ в секунду, в то время как на стандартном ПК поток может обрабатывать от 10 до 100 МБ в секунду.

Я использовал следующее решение в своем проекте. Обратите внимание, что это всего лишь часть кода, и вам придется изменить его, чтобы приспособить к своему собственному. Пишется на Visual Basic. Кстати, извините за мой английский...

    Dim SpeedLimit As Long = User.DownloadKbSpeedLimit * 1024, Elapsed As Long = 0
    'Try to adjust buffersize to the operating system.
    'Seem to be stupid, but the test shows it goes better this way.
    If Environment.Is64BitOperatingSystem Then
        stream.BufferSize = 64 * 1024
    Else
        stream.BufferSize = 32 * 1024
    End If
    'If buffersize is bigger than speedlimite, cut the buffersize to avoid send too much data
    If SpeedLimit > 0 AndAlso e.BufferSize > SpeedLimit Then e.BufferSize = SpeedLimit
    'Create Byte array to send data
    Dim Buffer(e.BufferSize) As Byte
    'Create Watch to control the speed
    Dim Transmitted As Integer = 0, Watch As New Stopwatch()
    Watch.Start()
    'Start sending data
    While True
        'This enables the program to control another events or threads
        System.Threading.Thread.Sleep(10)
        Windows.Forms.Application.DoEvents()
        'Recover data and write into the stream
        If SpeedLimit = 0 OrElse Transmitted < SpeedLimit Then
            Dim Readed As Integer = SomeFileStream.Read(Buffer, 0, Buffer.Length)
            If Readed 0 Then Exit While
            Stream.Write(Buffer, Readed) 
            Transmitted += Readed
        End If
        If Watch.ElapsedMilliseconds > OneSecond Then
            Transmitted = 0
            Watch.Restart()
        End If
    End While
    Watch.Stop()
    Stream.Close() : Stream.Dispose()

Надеюсь, это может помочь любому. До свидания.

person Miguel Becerra    schedule 03.09.2010
comment
Спасибо за отзыв. Несмотря на то, что это старая тема, активность заставит ее заметить :) - person Luke; 03.09.2010

Я провел некоторое исследование класса TcpClient, и вот как я это сделал:

           'Throttle network Mbps...
            bandwidthUsedThisSecond = session.bytesSentThisSecond + session.bytesRecievedThisSecond
            If bandwidthTimer.AddMilliseconds(50) > Now And bandwidthUsedThisSecond >= (Mbps / 20) Then
                While bandwidthTimer.AddMilliseconds(50) > Now
                    Thread.Sleep(1)
                End While
            End If
            If bandwidthTimer.AddMilliseconds(50) <= Now Then
                bandwidthTimer = Now
                session.bytesRecievedThisSecond = 0
                session.bytesSentThisSecond = 0
                bandwidthUsedThisSecond = 0
            End If

Я уверен, что вы знаете, как преобразовать его в С#, если вы решите использовать его самостоятельно, и, возможно, это просто мой код, но он кажется более ясным, чем другие ответы.

Это в основном цикле, а пропускная способностьTimer является объектом Date.

person Pete    schedule 22.12.2011