views:

680

answers:

7

Suppose you have a program which reads from a socket. How do you keep the download rate below a certain given threshold?

+4  A: 

Assuming a network transport, a TCP/IP based one, Packets are sent in response to ACK/NACK packets going the other way.

By limiting the rate of packets acknowledging receipt of the incoming packets, you will in turn reduce the rate at which new packets are sent.

It can be a bit imprecise, so its possibly optimal to monitor the downstream rate and adjust the response rate adaptively untill it falls inside a comfortable threshold. ( This will happen really quick however, you send dosens of acks a second )

Kent Fredric
Doesn't this requires going to the low-level TCP/IP stack, and bypassing the standard socket layer?
Branan
+1  A: 

If you're reading from a socket, you have no control over the bandwidth used - you're reading the operating system's buffer of that socket, and nothing you say will make the person writing to the socket write less data (unless, of course, you've worked out a protocol for that).

All that reading slowly would do is fill up the buffer, and cause an eventual stall on the network end - but you have no control of how or when this happens.

If you really want to read only so much data at a time, you can do something like this:

ReadFixedRate() {
  while(Data_Exists()) {
    t = GetTime();
    ReadBlock();
    while(t + delay > GetTime()) {
      Delay()'
    }
  }
}
Branan
Buffers have maximum values. Cache? There is no cache involved.
Stu Thompson
You are, of course, absolutely right. I removed the cache stuff, and added a bit about buffers.
Branan
+1  A: 

wget seems to manage it with the --limit-rate option. Here's from the man page:

Note that Wget implements the limiting by sleeping the appropriate amount of time after a network read that took less time than specified by the rate. Eventually this strategy causes the TCP transfer to slow down to approximately the specified rate. However, it may take some time for this balance to be achieved, so don't be surprised if limiting the rate doesn't work well with very small files.

Ben Dunlap
+10  A: 

At the application layer (using a Berkeley socket style API) you just watch the clock, and read or write data at the rate you want to limit at.

If you only read 10kbps on average, but the source is sending more than that, then eventually all the buffers between it and you will fill up. TCP/IP allows for this, and the protocol will arrange for the sender to slow down (at the application layer, probably all you need to know is that at the other end, blocking write calls will block, nonblocking writes will fail, and asynchronous writes won't complete, until you've read enough data to allow it).

At the application layer you can only be approximate - you can't guarantee hard limits such as "no more than 10 kb will pass a given point in the network in any one second". But if you keep track of what you've received, you can get the average right in the long run.

Steve Jessop
A: 

As other have said, the OS kernel is managing the traffic and you are simply reading a copy of the data out of kernel memory. To roughly limit the rate of just one application, you need to delay your reads of the data and allow incoming packets to buffer up in the kernel, which will eventually slow the acknowledgment of incoming packets and reduce the rate on that one socket.

If you want to slow all traffic to the machine, you need to go and adjust the sizes of your incoming TCP buffers. In Linux, you would affect this change by altering the values in /proc/sys/net/ipv4/tcp_rmem (read memory buffer sizes) and other tcp_* files.

jvasak
+1  A: 

It is like when limiting a game to a certain number of FPS.

extern int FPS;
....    
timePerFrameinMS = 1000/FPS;

while(1) {
time = getMilliseconds();
DrawScene();
time = getMilliseconds()-time;
if (time < timePerFrameinMS) {
   sleep(timePerFrameinMS - time);
}
}

This way you make sure that the game refresh rate will be at most FPS. In the same manner DrawScene can be the function used to pump bytes into the socket stream.

Iulian Şerbănoiu
A: 

To add to Branan answer.

If you limit voluntarily the read speed at the receiver end, eventually queues will fill up at both end. Then the sender will either block in its send() call or return from the send() call with a sent_length less than the expected length passed on to the send() call.

If the sender is not ready to deal with this case by sleeping and trying resending what has not fit into OS buffers. You will ending up have connection issues (the sender may detect this as an error) or loosing data (the sender may unkowingly discard data the did not fit into OS buffers).

Philibert Perusse