- ベストアンサー
ソケットで通信するデータの帯域制御方法
ソケットでデータをやり取りするプログラムを、C言語で組んだのですが、今はとりあえず100バイトずつ送受信しています。 これを、指定された帯域を越えない範囲で、できるだけたくさんデータを送るようにしたいと考え、トラフィックシェーピングを実装したいのですが、どのようにすればいいのか、実装方法がわかりません。 (今はただ文字データをやり取りしているだけなのですが、これをファイル転送ができるものにしたいため。) どんなものでもいいので、参考になるサイトやサンプルソース等を定時いただければと思います。
- みんなの回答 (4)
- 専門家の回答
質問者が選んだベストアンサー
先日速度制限しながら送信するプログラムを仕事で書いたので、 ソース貼れませんがロジックだけ返答してみます。 (x)bpsで(y)byte送信するのにかかる時間は(z)秒とします。 1.現在時刻取得 2.send()でyバイト送信 3.現在時刻を取得し、1.の値を引いて送信所要時間を算出 4.sleep(z-3.の値) 5.1へ 上記のような送信ループを実装しました。 実際には0.??秒になるのでsleep()ではなくusleep()を呼びました。 送信後、余った時間は寝てもらうというわけです。 質問者様の環境が書かれていないのでアレですが、 私はLinuxで実装したので、現在時刻の取得には gettimeofday()を使いました。 蓄積誤差が発生するので、なるべく分解度の高い時刻やsleepを 使うといいと思います。 # たとえばtime()は秒を返すのでこういうケースでは使えません 若干でも参考になれば幸いです。
その他の回答 (3)
- jjk65536
- ベストアンサー率59% (66/111)
> よろしければ、もう少し説明していただけないでしょうか? では、実際に計算してみると分かるでしょうか。 前出の計算式を使って、10Mずつ送信するケースを考えると、 1Mbpsに制限したい場合、10Mbyte(10000000byte)送信するのに必要な時間は x = 1000000(bps) y = 10000000(byte) z = 10000000 * 8 / 1000000 = 80(秒) となります。 実際には10Mbyteを10秒で送信してしまった場合、70秒待てば 実質1Mbps相当ですよね、長い目でみれば。 しかしながら、最初の1秒間に注目してみると、全力で送信してますので 1Mbpsになったとは言えません。 1000byteずつ送信とsleepをくりかえした場合、最初の1秒に注目しても だいたい1Mbps程度となります。 小刻みに送ったり休んだりしてるので。 通信速度計りながら実際にやってみると分かりますよ。 > これは、fflush()が「コールした瞬間、バッファのパケットが発射される」ように(私が)実装すれば、という認識で正しいでしょうか? > それとも、fflush()が「コールした瞬間、バッファのパケットが発射される」ように実装されていれば、でしょうか? 曖昧な表現になってしまってすみません。 後者が正解です。 Posixではバッファをクリアする関数が用意されてるので、それを使ってみるのが正解かな、と 思ったもので。 参考までにfflush()のmanを貼っておきますね。 > fflush - ストリームの内容を強制的に出力(フラッシュ)する > > 書式 > #include <stdio.h> > > int fflush(FILE *stream); > > 説明 > fflush() 関数は、ユーザー空間でバッファリングされているすべてのデータを > 与えられた出力に書き出す (フラッシュする)。あるいはストリーム stream の > 下 位にある書き込み関数を用いてこのストリームを更新する。ストリームは開 > いた状態のままであり、この関数によって何の影響も受けない。 >
お礼
締め切ってポイントを付けるのが遅くなって申し訳ありません。 最後まで丁寧にご説明いただき、本当に有難うございました。
補足
有難うございました。 >実際には10Mbyteを10秒で送信してしまった場合、70秒待てば >実質1Mbps相当ですよね、長い目でみれば。 >しかしながら、最初の1秒間に注目してみると、全力で送信してますので >1Mbpsになったとは言えません。 平均1Mbpsだからといって、実際の送信状態が一定になっているとは限らない、ということですね。
- jjk65536
- ベストアンサー率59% (66/111)
> ところで、「1000byteに深い意味はないですが、・・・」とのことですが、この値はどのように決めたらよいのでしょう? > 一般的にこのくらいの値を使うことが多い、とか、こういう計算で求める、とか、帯域に対して何%、とか、あるのでしょうか? きっと質問されると思ってました(^^; これは正直私も論理的にどうっていうものはありません。 一つの考え方として、一般的にパケットサイズは1500byteです。 TCPヘッダを除くと1460byteですので、それより小さめがいいかなと 考えることができます。 ただし、send()時すぐに送信されるのか、バッファリングして適当なタイミングで まとめて送信するのかなどがネットワークスタック(TCP/IPドライバ)の実装次第で変わります。 ようはsend()一発とパケット1つが対応する保証がないので、 上のように考えても意味がないとも言えます。 実際に動作させる環境で様子をみて、期待と違う動きをしていたら 「この辺の仕様が思ってるのと違うのかな?」と思いを馳せつつ 送信サイズをチューンするって感じですかね。 Posix関数にはfflush()というバッファをクリアする関数があって、 これをコールした瞬間、バッファのパケットが発射される実装のものも あります。 一般的な数値も私は分からないです。 ただ、例えば10Mbyteごとにsleep()では、送信の時間と待ち時間が それぞれ長くなりすぎて、ネットワーク負荷が矩形波のように なってしまうことは想像できるかと思います。 そうならなさそうな数値ってことで、パケットサイズよりちょっと小さい 1000くらいとしてみました。 1460送ってfflush()が正解かもしれませんね。 割と適当でも動いてしまったので、それ以上私は調べてないというのが 本音のところです。 すみません。
補足
何度もお答えいただき、本当に有難うございます。 いくら探しても、これぞという値の決め方が見つからなかった理由がちょっとわかった気がします。 ですが、まだちょっとよくわからないところがあります。 よろしければ、もう少しお付き合いください。 >ただ、例えば10Mbyteごとにsleep()では、送信の時間と待ち時間が >それぞれ長くなりすぎて、ネットワーク負荷が矩形波のように >なってしまうことは想像できるかと思います。 すみませんが、ここがちょっとわかりません。よろしければ、もう少し説明していただけないでしょうか? (一気に大きなサイズを送り出したら、送信中は帯域を占有してしまうであろうことまではわかるのですが・・・) 不勉強で申しわけありません。 >1460送ってfflush()が正解かもしれませんね。 これは、fflush()が「コールした瞬間、バッファのパケットが発射される」ように(私が)実装すれば、という認識で正しいでしょうか? それとも、fflush()が「コールした瞬間、バッファのパケットが発射される」ように実装されていれば、でしょうか? こちらも不勉強で申しわけありません。
- jjk65536
- ベストアンサー率59% (66/111)
1です。 > とりあえず一気に送って、送信にかかる時間(何十秒とか何分とか)だけ待つ、ということでしょうか? それだとbps的にばらつきが出ますので、1000byte程度ずつ 送ります。 (1000byteに深い意味はないですが、小さめということで) たとえば1Mbpsに制限したい場合、1000byte送信するのに必要な時間は x = 1000000(bps) y = 1000(byte) z = 1000 * 8 / 1000000 = 0.008(秒) となりますので、0.008秒で送信したいわけです。 ここで、たとえば送信が0.005秒で終わってしまった場合、 0.003秒のsleep()で調整してやると、転送レートが だいたい1Mbpsとなります。 コレをファイル転送が終わるまでループで処理すると、 指定速度でファイルが転送されていきます。 Windowsでの注意点ですが、確かWin32APIのSleep()はあんまり 分解能が高くないので(参考URL参照)、送信サイズをもう少し 大きくするなどの工夫がいると思います。 過去実験した個人的な数値では、50ミリ秒以下のSleep()は 全て50ミリ秒待たされてしまいました。Windows2000での実験だったと 思うので、XPやVistaでどうなってるかは分りません。 作ってみたけど妙に通信速度が遅い、といったときには この辺を疑ってみてください。
補足
有難うございました。 やり方は、一度にドカッと、ではなく、一定量(少量)を送り続ける、なのですね。 ところで、「1000byteに深い意味はないですが、・・・」とのことですが、この値はどのように決めたらよいのでしょう? 一般的にこのくらいの値を使うことが多い、とか、こういう計算で求める、とか、帯域に対して何%、とか、あるのでしょうか? たびたびですみませんが、よろしかったらご教授願います。
補足
有難うございました。ロジックだけでも助かります。 ちなみに、私はWindows環境で作成しています。 なにか注意事項や補足がありましたら、よろしくお願いいたします。 あと、「2.send()でyバイト送信」とありますが、これはサイズの大きなファイルでも(分割などはせず)とりあえず一気に送って、送信にかかる時間(何十秒とか何分とか)だけ待つ、ということでしょうか?