Я пишу утилиту, которая будет загружать кучу файлов, и хотел бы предоставить возможность ограничивать скорость загрузки. Каков наилучший подход к ограничению скорости загрузки при использовании класса TcpClient? Мой первый порыв — вызвать NetworkStream.Write() с ограниченным числом байтов за раз, ожидая между вызовами (и пропуская вызов, если поток еще не закончил запись) до тех пор, пока буфер не будет загружен. Кто-нибудь реализовывал что-то подобное раньше?
Как я могу ограничить скорость загрузки с помощью TcpClient?
Ответы (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();
Это черновик непроверенного кода, но я думаю, что этого достаточно, чтобы понять основную идею.
Вместо того, чтобы создавать это, вы также можете рассмотреть BITS (фоновая служба передачи через Интернет), которая позволяет пользователю (или администратору) настраивать пропускную способность и будет обрабатывать очереди передач.
Это требует специальной поддержки на сервере (в том числе в IIS, но требует включения).
Я знаю, что это старая запись, но я думаю, что эта информация может быть полезна для тех, кто попал сюда через 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()
Надеюсь, это может помочь любому. До свидания.
Я провел некоторое исследование класса 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.