假设您有一个从套接字读取数据的程序。如何将下载速率保持在某个给定阈值以下?

有帮助吗?

解决方案

在应用程序层(使用 Berkeley 套接字样式 API),您只需观察时钟,并以您想要限制的速率读取或写入数据。

如果您平均只读取 10kbps,但源发送的数据超过此值,那么最终它和您之间的所有缓冲区都会被填满。TCP/IP 允许这样做,并且协议会安排发送方放慢速度(在应用层,可能您需要知道的是在另一端,阻塞写入调用将阻塞,非阻塞写入将失败,异步写入将失败)写入不会完成,直到您读取了足够的数据以允许它)。

在应用程序层,您只能是近似的 - 您无法保证诸如“任何一秒内通过网络中给定点的数据量不超过 10 kb”之类的硬性限制。但如果您跟踪收到的内容,从长远来看,您可以获得正确的平均值。

其他提示

假设网络传输基于 TCP/IP,则发送数据包以响应反向的 ACK/NACK 数据包。

通过限制确认收到传入数据包的数据包速率,您将依次降低发送新数据包的速率。

它可能有点不精确,因此监控下游速率并自适应调整响应速率可能是最佳选择,直到它落在舒适的阈值内。(这会发生得很快,但是,你每秒发送大量的 ack )

这就像将游戏限制在一定数量的 FPS 一样。

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

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

这样您就可以确保游戏刷新率最高为 FPS。以同样的方式,DrawScene 可以是用于将字节泵入套接字流的函数。

如果您正在从套接字读取数据,则无法控制所使用的带宽 - 您正在读取该套接字的操作系统缓冲区,并且您所说的任何内容都不会让写入套接字的人写入更少的数据(当然,除非,你已经为此制定了一个协议)。

缓慢读取只会填满缓冲区,并导致网络端最终停止 - 但您无法控制这种情况如何或何时发生。

如果你真的想一次只读取这么多数据,你可以这样做:

ReadFixedRate() {
  while(Data_Exists()) {
    t = GetTime();
    ReadBlock();
    while(t + delay > GetTime()) {
      Delay()'
    }
  }
}

wget 似乎使用 --limit-rate 选项来管理它。这是手册页中的内容:

请注意,WGET通过在网络读取后的适当时间睡觉的时间少于费率指定的时间少的时间来实现限制。最终,该策略导致TCP转移减速至大约指定的速率。但是,实现此余额可能需要一些时间,因此,如果限制速率与很小的文件无法正常工作,请不要感到惊讶。

正如其他人所说,操作系统内核正在管理流量,您只是从内核内存中读取数据的副本。为了大致限制一个应用程序的速率,您需要延迟数据的读取并允许传入数据包在内核中缓冲,这最终会减慢传入数据包的确认并降低该套接字上的速率。

如果您想减慢到计算机的所有流量,则需要调整传入 TCP 缓冲区的大小。在 Linux 中,您可以通过更改 /proc/sys/net/ipv4/tcp_rmem(读取内存缓冲区大小)和其他 tcp_* 文件中的值来影响此更改。

添加到布兰南的答案:

如果您自愿限制接收端的读取速度,最终两端的队列都会被填满。然后,发送方将阻塞其 send() 调用,或者从 send() 调用中返回,且 send_length 小于传递给 send() 调用的预期长度。

如果发送者还没有准备好通过睡眠并尝试重新发送不适合操作系统缓冲区的内容来处理这种情况,那么最终将出现连接问题(发送者可能会将其检测为错误)或丢失数据(发送者可能会在不知情的情况下)丢弃不适合操作系统缓冲区的数据)。

设置小的套接字发送和接收缓冲区,例如 1k 或 2k,这样带宽 * 延迟乘积 = 缓冲区大小。您可能无法通过快速链接使其足够小。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top