Предположим, у вас есть программа, которая читает из сокета. Как сохранить скорость загрузки ниже определенного порога?





Предполагая сетевой транспорт, основанный на TCP / IP, пакеты отправляются в ответ на пакеты ACK / NACK, идущие в обратном направлении.
Ограничивая скорость пакетов, подтверждающих получение входящих пакетов, вы, в свою очередь, уменьшаете скорость, с которой отправляются новые пакеты.
Это может быть немного неточно, поэтому, возможно, оптимально контролировать скорость нисходящего потока и адаптивно регулировать скорость отклика, пока она не упадет в комфортный порог. (Это произойдет очень быстро, однако вы отправляете несколько подтверждений в секунду)
Если вы читаете из сокета, у вас нет контроля над используемой полосой пропускания - вы читаете буфер операционной системы этого сокета, и ничто из того, что вы говорите, не заставит человека, записывающего в сокет, писать меньше данных (если, конечно, , вы разработали для этого протокол).
Все, что происходит при медленном чтении, - это заполнение буфера и возможная остановка на стороне сети - но вы не можете контролировать, как и когда это произойдет.
Если вы действительно хотите читать только определенное количество данных за раз, вы можете сделать что-то вроде этого:
ReadFixedRate() {
while(Data_Exists()) {
t = GetTime();
ReadBlock();
while(t + delay > GetTime()) {
Delay()'
}
}
}
Буферы имеют максимальные значения. Кэш? Кеша нет.
Вы, конечно, абсолютно правы. Я удалил кэш и добавил немного о буферах.
Кажется, что wget справляется с этим с помощью опции --limit-rate. Вот со страницы руководства:
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.
На прикладном уровне (используя API в стиле сокетов Беркли) вы просто смотрите на часы и читаете или записываете данные с той скоростью, которую вы хотите ограничить.
Если вы читаете в среднем только 10 кбит / с, но источник отправляет больше, то в конечном итоге все буферы между ним и вами заполнятся. TCP / IP позволяет это, и протокол будет организовывать замедление отправителя (на уровне приложения, вероятно, все, что вам нужно знать, это то, что на другом конце блокирующие вызовы записи будут блокироваться, неблокирующая запись завершится ошибкой и асинхронная запись не будет завершена, пока вы не прочитаете достаточно данных, чтобы это разрешить).
На прикладном уровне вы можете быть только приблизительными - вы не можете гарантировать жестких ограничений, таких как «не более 10 КБ пройдет через заданную точку в сети за одну секунду». Но если вы будете следить за тем, что получили, вы сможете получить среднее значение в долгосрочной перспективе.
Как уже говорили другие, ядро ОС управляет трафиком, и вы просто читаете копию данных из памяти ядра. Чтобы примерно ограничить скорость всего одного приложения, вам нужно отложить чтение данных и позволить входящим пакетам буферизоваться в ядре, что в конечном итоге замедлит подтверждение входящих пакетов и снизит скорость на этом одном сокете.
Если вы хотите замедлить весь трафик на машину, вам нужно пойти и настроить размеры входящих TCP-буферов. В Linux вы можете повлиять на это изменение, изменив значения в / proc / sys / net / ipv4 / tcp_rmem (размеры буфера чтения памяти) и других файлах tcp_ *.
Это похоже на ограничение игры определенным количеством кадров в секунду.
extern int FPS;
....
timePerFrameinMS = 1000/FPS;
while(1) {
time = getMilliseconds();
DrawScene();
time = getMilliseconds()-time;
if (time < timePerFrameinMS) {
sleep(timePerFrameinMS - time);
}
}
Таким образом вы убедитесь, что частота обновления игры будет не выше FPS. Таким же образом DrawScene может быть функцией, используемой для перекачки байтов в поток сокета.
Чтобы добавить к ответу Бранана:
Если вы добровольно ограничите скорость чтения на стороне получателя, в конечном итоге очереди заполнятся на обоих концах. Затем отправитель либо заблокирует вызов send (), либо вернется из вызова send () с размером sent_length меньше ожидаемой длины, передаваемой вызову send ().
Если отправитель не готов справиться с этим случаем, засыпая и пытаясь повторно отправить то, что не помещается в буферы ОС, в конечном итоге у вас возникнут проблемы с подключением (отправитель может обнаружить это как ошибку) или потерять данные (отправитель может неосознанно отбросить данные, которые не поместились в буферы ОС).
Установите небольшие буферы отправки и получения сокета, скажем 1k или 2k, так, чтобы полоса пропускания * произведение задержки = размеру буфера. Возможно, вам не удастся сделать его достаточно маленьким по быстрым ссылкам.
Разве для этого не требуется переход к низкоуровневому стеку TCP / IP и обход стандартного уровня сокетов?