- - PR -
ソケットでウィンドウ制御
投稿者 | 投稿内容 | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
投稿日時: 2007-12-19 02:11
ソケットを利用した通信プログラムでウィンドウ制御を考慮する必要は有るのでしょうか?
最近携わった仕事での事なのですが、recvで受信したときに、次の電文まで読んでしまう事が希にありました。原因はウィンドウ制御のせいであろうということでその対策をしました。 しかし、ソケットはセッション層のプロトコルで、ウィンドウ制御はその下位のトランスポート層のプロトコルです。ソケットを利用するプログラミングが下位のプロトコルの制御を意識して作るというのは違うのではないかとずっと疑問に思っていました。 もしかして、ソケットライブラリのバグなのでしょうか? [ メッセージ編集済み 編集者: thumbpull 編集日時 2007-12-19 21:32 ] | ||||||||||||
|
投稿日時: 2007-12-19 08:33
無い。通信の即時性が求められる場合に、意図的にウィンドウサイズを小さく設定することもある・・・と聞いたことはあるが、そんなの例外中の例外だと思う。
それが正常な動作。勘違いしている人が多いが、ファイル入出力や232Cと同じストリーム入出力なので、TCP自体はデータの区切りを意識しません。1回の送信に対して、2回の受信イベントが走ったり、逆に2回の送信に対して1回の受信イベントしか起こらないのは仕様です。外乱の少ないLANで、低負荷で通信していると、1回の送信に対して1回の受信イベントが起こるような動作になるので、勘違いして居る人が多いだけ。
アプリケーションのバグですね。 | ||||||||||||
|
投稿日時: 2007-12-19 08:36
できる限り、その通りであるべきですが、
時と場合によってはウィンドウまで考慮する必要があります。 パフォーマンスとか経路とか、いろいろ。 常に考慮しなければならない、ということはありません。
次の電文とはなんでしょうか? もしかして、sendで送った電文は sendした単位でrecvされていると思っていますか? ピアが送信した電文は正しい順序で送られることは保証されてますが、 どこで切れるかは保証されていません。 send1回に対してrecv数回の場合もありますし、 send数回に対してrecv1回の場合もあります。 これはウィンドウサイズや途中の経路などに依存します。 ウィンドウサイズを変更することで send1回に対してrecv1回になるよう調節するのは間違いです。 間にある機材などで変わってきたり、 ドライバのアップデートなどで変更される場合もあります。 sendとrecvが対応していなくても動作するよう、 プログラムで制御しなければいけません。 | ||||||||||||
|
投稿日時: 2007-12-19 10:28
http://www.kt.rim.or.jp/~ksk/wskfaq-ja/articles/lame-list.html#item20
あたりをご参考に。 同じサイトですけど、 http://www.kt.rim.or.jp/~ksk/wskfaq-ja/articles/effective-tcp.html とか、 http://www.kt.rim.or.jp/~ksk/wskfaq-ja/intermediate.html#nagle-desc とその次の項も読んでおくといいかと思います。 UDPだとsend/sendtoとrecv/recvfromは1対1に対応したかと思いますが、そもそも途中で データが失われる可能性を考慮しないといけませんね。 (参照URL追加しました) [ メッセージ編集済み 編集者: TAD 編集日時 2007-12-19 11:08 ] | ||||||||||||
|
投稿日時: 2007-12-19 23:45
皆さんレスありがとうございます。
大変勉強になります。(トランプポート→トランスポートでした) アプリケーションレベルでの電文は、データリンク層のMTUによって分割される事は知っていたので、1回のsendに対して複数回のrecvが必要になる事は想定していました。 今回、テストでエラーが発生し、1回のrecvで2回分のsendの電文を受信している事が分かり、ウィンドウ制御が原因ではないかということで対策をしました。 ウィンドウ制御を知らなかった時点でダメダメですね... 対処は、電文中にある電文長を考慮して受信するようにしました。 TADさんに教えて頂いたURLにあったものと同じですね。 ということは、TCP/IPを利用する場合、電文中に電文長を持たないのは致命的ということですね。 私は、データリンク層で分割されたパケットは、データリンク層の責任で、トランスポートのウィンドウ制御で結合されたパケットは。トランスポート層の責任で、それぞれ元のパケットに戻すものと考えていたのですが、それは間違いで、それぞれの層は、互いに強調・補完して成り立っているというのが正しい理解でしょうか。 | ||||||||||||
|
投稿日時: 2007-12-20 01:01
SOCK_STREAM型のソケットは、その名の通りバイトストリームなので、
レコード境界(send/recvの切れ目)は保存されません。 TCPというプロトコル階層にはパケットと言う概念が存在しない (下位のIP層にはあるが、TCP層で終端される) というのが正しい理解でしょう。 | ||||||||||||
|
投稿日時: 2007-12-20 12:33
「元のパケットに戻す」という理解のほうがより正解に近いと思います。 ただし正確にいうと、パケットの分割・再構成はネットワーク層(IP層)の仕事です。 なぜならIP層の仕事はルーティングで、パケットの分割・再構成はルーティングのさい に発生するものだからです。そして始点、中継点1、中継点2・・・のどこででも分割は 可能で、終点においてかならず始点のパケットに再構成されます。再構成できないときは 単に捨てられます。これは信頼性のないサービスです。 次にトランスポート層(TCPの場合)はバイトストリームをパケット化して送り、受け取った パケットをバイトストリームに復元します。TCPの仕事はエンドツーエンドで信頼性のある 通信を提供することで、そのためパケットではなくバイトストリームを扱っています。これは かならず元通りに再構成されるか、さもなくばエラーになることが保証されています。これは 信頼性のあるサービスです。 ですからソケットプログラミングでは、ストリーム型のソケット(つまりTCP)使用時は、 バイトストリームを扱うのであって、パケットを扱うわけではありません。つまり「元の パケットに戻る」のではなく、元のバイトストリームに戻るというのが正しいです。 またバイトストリーム中には実際はレコード境界(PUSHフラグの位置)が存在します。が、 これに頼ってはいけないことになっています。これはそういう仕様です。そもそもTCP層と アプリケーション層ではレイヤが違うので、アプリケーション層でレコード単位のやりとり をするのであれば、アプリケーションのレコードをバイトストリーム化してTCPに渡し、 TCPから受信したバイトストリームを元のレコードに復元するのは自然であり、何の問題も ないように思います。 なおウィンドウ制御というのが何を指すのかは私には不明でした。実際のTCPにはフロー制御 というものがあり、ウィンドウはそのために使われていますが(厳密にいうとそれだけでは なくふくそう制御にも使われるのですが)、ソケットプログラミングではフロー制御を意識す ることはまずない気がします。この辺の議論は真意がつかみきれていないので、よくわかり ません。 では。 | ||||||||||||
|
投稿日時: 2007-12-21 10:21
あるいはよく行われているようにデータの終端あるいは開始記号(列)を決めておくという 手もあるでしょう。(テキストデータに対して行末にCRLFを付加するとか...) 何らかの方法で、一連のデータストリームから必要なデータ単位を取り出せればいいわけです。 TCP/IPではエラーがあったことは検出できるのでそれほど悲惨にはならないかも知れませんが、 以前RS-232の通信で先頭にデータ長を送るように決めていたら、処理落ちがあってデータ長を 取りこぼしてしまい、以降の通信が壊滅してしまったことがあります。特定の終端/開始記号で データ区切りをする方法なら一つの電文は失われますが、以降の復帰はできたのでしょうが。 システムの要求によっては万一のエラーからの回復も考えていろいろと決めた方がいいかもしれません。 #TCP/IPの"信頼性がある"というのは100%のデータ伝送を保証するという意味ではなく、 #正常に通信ができるか、そうでなければエラーとして検出できる、という意味。 |