ソケットから読み取るプログラムがあるとします。ダウンロード速度を特定のしきい値未満に保つにはどうすればよいでしょうか?
8 に答える
アプリケーション層 (バークレー ソケット スタイルの API を使用) では、クロックを監視し、制限したい速度でデータを読み書きします。
平均で 10kbps しか読み取れないが、ソースがそれ以上を送信している場合、最終的にはソースとあなたの間のすべてのバッファがいっぱいになります。TCP/IP はこれを許可し、プロトコルは送信者が遅くなるように調整します (アプリケーション層では、おそらく、もう一方の端で、ブロック書き込み呼び出しがブロックされ、非ブロック書き込み呼び出しが失敗し、非同期であることを知る必要があるだけです)。十分なデータを読み取るまで、書き込みは完了しません)。
アプリケーション レイヤーでは、おおよその値しか指定できません。「ネットワーク内の特定のポイントを 1 秒間に 10 kb 以下が通過する」などのハード リミットを保証することはできません。しかし、受け取ったものを追跡すれば、長期的には正しい平均を得ることができます.
TCP/IP ベースのネットワーク トランスポートを想定すると、逆方向の ACK/NACK パケットに応答してパケットが送信されます。
着信パケットの受信を確認するパケットのレートを制限することで、新しいパケットが送信されるレートを下げることができます。
少し不正確になる可能性があるため、ダウンストリーム レートを監視し、適切なしきい値内に収まるまで応答レートを適応的に調整することが最適な可能性があります。(これは非常に迅速に行われますが、1 秒間に大量の 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オプションでそれを管理しているようです。これがmanページからです:
Wgetは、レートで指定された時間よりも短い時間でネットワークを読み取った後、適切な時間をスリープ状態にすることで制限を実装していることに注意してください。最終的に、この戦略により、TCP転送はほぼ指定された速度まで遅くなります。ただし、このバランスがとれるまでには時間がかかる場合があるため、非常に小さいファイルでレートの制限がうまく機能しない場合でも驚かないでください。
他の人が言ったように、OS カーネルはトラフィックを管理しており、カーネル メモリからデータのコピーを読み取っているだけです。1 つのアプリケーションの速度を大まかに制限するには、データの読み取りを遅らせ、着信パケットがカーネルでバッファリングされるようにする必要があります。これにより、最終的に着信パケットの確認応答が遅くなり、その 1 つのソケットの速度が低下します。
マシンへのすべてのトラフィックを遅くしたい場合は、受信 TCP バッファのサイズを調整する必要があります。Linux では、/proc/sys/net/ipv4/tcp_rmem (読み取りメモリ バッファー サイズ) およびその他の tcp_* ファイルの値を変更することで、この変更に影響を与えます。
帯域幅×遅延積 = バッファ サイズとなるように、1k または 2k などの小さなソケット送受信バッファを設定します。高速リンクでは十分に小さくできない場合があります。
ブラナンの答えに追加するには:
受信側で読み取り速度を自発的に制限すると、最終的にキューが両方の側でいっぱいになります。次に、送信者は send() 呼び出しでブロックするか、send() 呼び出しに渡される予想される長さよりも短い sent_length で send() 呼び出しから戻ります。
送信者がスリープして OS バッファに収まらないものを再送信しようとしてこのケースに対処する準備ができていない場合、接続の問題 (送信者はこれをエラーとして検出する可能性があります) やデータの損失 (送信者が知らないうちに可能性があります) が発生します。 OS バッファに収まらなかったデータを破棄します)。