【发布时间】:2008-09-18 17:58:33
【问题描述】:
假设您有一个从套接字读取的程序。如何将下载速率保持在某个给定阈值以下?
【问题讨论】:
标签: algorithm language-agnostic io
假设您有一个从套接字读取的程序。如何将下载速率保持在某个给定阈值以下?
【问题讨论】:
标签: algorithm language-agnostic io
在应用层(使用 Berkeley 套接字样式 API),您只需查看时钟,并以您希望限制的速率读取或写入数据。
如果您平均只读取 10kbps,但源发送的速度超过此值,那么最终它和您之间的所有缓冲区都会填满。 TCP/IP 允许这样做,并且协议会安排发送方减速(在应用层,您可能只需要知道在另一端,阻塞写入调用会阻塞,非阻塞写入会失败,并且异步在您读取足够的数据以允许写入之前,写入不会完成)。
在应用程序层,您只能是近似的 - 您不能保证硬限制,例如“在任何一秒内通过网络中给定点的数据不超过 10 kb”。但是,如果您跟踪收到的内容,从长远来看,您可以获得正确的平均值。
【讨论】:
假设网络传输,基于 TCP/IP 的传输,数据包被发送以响应 ACK/NACK 数据包以另一种方式发送。
通过限制确认接收传入数据包的数据包速率,您将反过来降低发送新数据包的速率。
它可能有点不精确,因此它可能是监控下游速率并自适应调整响应速率直到它落在舒适阈值内的最佳选择。 (这会很快发生,但是你会发送大量的确认)
【讨论】:
就像将游戏限制在一定数量的 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_* 文件中的值来影响此更改。
【讨论】:
补充 Branan 的答案:
如果您自愿限制接收端的读取速度,最终队列将在两端填满。然后,发送方要么阻塞其 send() 调用,要么从 send() 调用返回,其 sent_length 小于传递给 send() 调用的预期长度。
如果发送方没有准备好通过休眠并尝试重新发送不适合操作系统缓冲区的内容来处理这种情况,您最终会遇到连接问题(发送方可能会将其检测为错误)或丢失数据(发送方可能会在不知不觉中丢弃不适合操作系统缓冲区的数据)。
【讨论】:
设置小的套接字发送和接收缓冲区,比如 1k 或 2k,这样带宽*延迟乘积 = 缓冲区大小。您可能无法通过快速链接将其变得足够小。
【讨论】: