views:

302

answers:

3

I'm writing a utility that will be uploading a bunch of files, and would like to provide the option to rate limit uploads. What is the best approach for rate limiting uploads when using the TcpClient class? My first instinct is to call NetworkStream.Write() with a limited number of bytes at a time, sleeping between calls (and skipping a call if the stream isn't done writing yet) until the buffer is uploaded. Has anyone implemented something like this before?

+1  A: 

Rather than creating this, you might also want to consider BITS (Background Internet Transfer Service) which allows the user (or admin) to configure bandwidth, and will handle queuing of transfers.

It does require specific support on the server (including in IIS, but needs enabling).

Richard
Hmm, unfortunately my server is on the LAMP stack so BITS won't work. Good to know about though.
Luke
+3  A: 

Implementing speed limit is relatively easy, take a look at the following snippet:

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

This is draft untested code, but I think it is enough to get the main idea.

arbiter
+1  A: 

I know this is an old entry, but i think this information can be usefull for someone who get here through google or another web search.

If we use the solution posted by "arbiter" we will found that the thread will send a large amount of data, and then it will sleep for a large amount of time, cause the usually speed limits are over 32 to 200 kb per second while with an standar pc, the thread can manage over 10 to 100 MB per second.

I used the next solution into my project. Note that is only a piece of code and you will have to modify it to adjust to your own. It is write in Visual basic. By the way, sorry about my english...

    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()

Hope this can help anyone. Bye.

Miguel Becerra
Thanks for the input. Even though its an old topic, the activity will cause it to be noticed :)
Luke