views:

1385

answers:

5

I'm writing an app in C# that downloads concurrently (in different threads) using multiple connections to multiple servers, and I'd like to be able to limit the used bandwidth.

For a single connection the solution would be simple; I'd use the solution posted here : http://www.codeproject.com/KB/IP/Bandwidth_throttling.aspx which calculates a sleep-time for the single connection.

I'd like to know what the best way is to do this for multiple connections.

Using the ThrottledStream posted above and dividing the bandwidth (say 2MB/sec) evenly among the connections isn't right, if I'd have 3 very slow connections and 1 very fast one they'd all be capped to 512kb/sec, so the fast one won't go above 512kb/sec and the other 3 wouldn't even make that.

The preferred solution I think is to cap only the fastest connection(s) so the slower connections are used optimally.

Does anyone have any experience with this, example code or any advice ?

+1  A: 

Off the top of my head, I'd get a collector object / class that pulled sequentially from each stream with the collector sleeping as appropriate (probably via a moving average comparison). This will balance your streams to allow the full bandwidth limit to be achieved if one is slower than the other... you'll poll to pull, find it empty, and pull the next stream before sleeping.

Since your collector is limited by aggregate data pulled / time you'll get the right client side bandwidth.

Autocracy
+2  A: 

If it were me, I'd start by chopping the bandwidth requirement into N pieces where N is the number of concurrent connections. Next, I would monitor the performance of each connection and make adjustments as needed. First, I'd identify any connections operating at the maximum speed I've set. These connections would be candidates for increased speeds if the need exists. Next, I would identify those connections that are not meeting the bandwidth limits I set forth (the underachievers). It would then be easy to sum up the short-fallings of the latter group. You'd take that number, divide by the number of connections performing at top speed and make the adjustments.

Total Bandwidth Allowed: 100KB/Sec
Connections : 5
Initial per-connection limit: 20KB/sec

Actual Results:
Connection 1: 10KB/sec
Connection 2: 5KB/sec
Connection 3: 20KB/sec
Connection 3: 20KB/sec
Connection 3: 20KB/sec

Total Short: 15KB/sec

New Per-Connection Limits:
Connection 1: 10KB/sec
Connection 2: 5KB/sec
Connection 3: 25KB/sec
Connection 3: 25KB/sec
Connection 3: 25KB/sec

You'd then monitor this at some interval.

Robert H.
A: 

The total short above is really 25kb/sec, but instead of separating the connections into groups, I would sort by speed and loop through them. Start with the total bandwidth and set the new bandwidth to (remaining bandwidth / remaining count). This would keep the limits up for the 5k and 10k connections in case they speed up. You could go over your allotted bandwidth, but you would correct it.

List<Connection> connections = GetConnections();
connections.Sort(); // sorts by Speed
bandwidth = 100000;
for (int i = 0; i < connections.Count; i++)
{
    Connection cnn = connections[i];
    cnn.SpeedLimit = bandwidth / (connections.Count - i);
    bandwidth -= Math.Min(cnn.Speed, cnn.SpeedLimit);
}

(start with all connections SpeedLimit set to 20000 bytes/sec)

Speed   bandwidth SpeedLimit
5000    100000 20000
10000   95000 23750
20000   85000 28333
20000   65000 32500
20000   45000 45000

The total speed limit for connections is 149583 so you could go over your limit if the slow ones speed up, but if you adjust the limits every second it should be pretty close.

The connections speeds change to: 5000, 10000, 28333, 21000, 45000, so we've actually downloaded 109,333 bytes, but we will adjust again. All connections except the 45k one have reached their possible limits:

Speed   bandwidth SpeedLimit
5000    100000 20000
10000   95000 23750
21000   85000 28333
28333   64000 32000
45000   35667 35667

The new speeds will be 5000, 10000, 21000, 28333, 35667, 100k exactly

Speed   bandwidth SpeedLimit
5000    100000 20000
10000   95000 23750
21000   85000 28333
28333   64000 32000
35667   35667 35667

Now let's assume the 5k connection speeds up to 20k:

Speed   bandwidth SpeedLimit
10000   100000 20000
20000   90000 22500
21000   70000 23333
28333   49000 24500
35667   20667 20667
A: 

I have the same problem. I am trying out VS2010 Pro and I have made small home project where I am downloading multiple files (10-50) at the same time and I want to limit bandwidth for them all. I found this posting but I can't really see a solution so I tried for myself.

What I did was, I created a DownloadLimiter class which is built up real simple. Each time a Downloader wants to download a chunk (like, just before InputStream.Read()) it has to ask the DownloadLimiter for bandwidth. It has a variable which holds a limitation, example 50 000 (like bytesPerSecond) and availableBandwidthPerSecond which decreases as each ask() is processed and returned.

It also contains a Timer which resets the availableBandwidthPerSecond to the limitation each 1000ms.

The ask() method is locked so if there is not currently any available bandwidth left it just loops and Sleeps for a while until the Timer has reset the available bandwidth.

It works perfectly, however. There is an issue. Let's say you have a pretty fast connection (100 megabit) and you set the limit to 200kb. If you download one file (from a similar fast connection) then you will peak at 100megabit for x amounts of milliseconds then it will wait until the available bandwidth has been reset. And this will continue until the file has completed. The average download speed will be fairly even, but if you watch a bandwidth graph it will have peaks, each second. I guess that the posted link to ThrottledStream works the same way with peaking.

I don't know if the text above helped you... If anyone is interested I can post the code somewhere for you to read. Would be nice with some optimization (if you want) too since I am not that familiar with threading just yet. :)

DavidMB
A: 

Hi DavidMB,

Do you mind posting DownloadLimiter class ?

Thanks

Jeff in Seattle

jeff00seattle