• ベストアンサー

socket: recvはいつ,どれだけ受け取るのか?

 現在,参考書にしたがってC++でソケットプログラミングを書いています.  sendとrecvを非同期にするために,本では select関数やWSAAsyncSelect関数などを利用していて,実際,本のとおりに書いて上手く動いています.  ここで伺いたいのですが,recvは,どうやって「データが届いたか」を知るのでしょうか.  同期ならば,トランシーバでの会話のように送信側が「どうぞ」といって送受信を交代させることができますが,非同期ならばそれができません.  NICとかが,プログラムに「届いたぞ!( or これから届くぞ!)」と教えてくれるのでしょうか.あるいは逆に,プログラムがNICに「届いてる?」と聞いているのでしょうか.仮に,ここに書いたような方法で届いたことが分かったとしても,どれくらい受け取ればいいかは分かりません(それも併せて教えてもらっているのでしょうか.データを送るときには,どれだけ送ればいいか分かりますよね.受信するときはどうしてるのかを知りたいと思っています).

質問者が選んだベストアンサー

  • ベストアンサー
  • chirubou
  • ベストアンサー率37% (189/502)
回答No.1

Linux しか知らないので Linux で説明をします。 NIC が通信パケットを受け取ると割り込みが発生し、CPU は割り込みを受け付けて、対応するデバイスドライバを起動します。この時、ドライバはソケットバッファと呼ばれる構造体にパケットの中身をコピーして、Linux カーネルの本体に渡し、そこで TCP 等の上位プロトコル処理が行われます。 一方、ユーザプログラムの方は、 select() なり read() で待っている訳ですが、OS はもちろんプロセスが何を待っているかを知っているので、対応する待ちの条件が満たされると、この場合は select() や read() が、抜けてくる(return する)訳です。 という事で、ユーザのプログラムは select() なり read() なりで受信データを「待つ」ことが必要です。もちろん select() や read() が呼ばれた時点で既に受信しているのならば、それらは直ぐに帰ってきます。read() や recv() はデータが届いた事を知る、というよりは、届いているかチェックして、まだ届いていなければ届くまで待つ(read() が抜けてこない)という処理になります。また NIC とユーザプログラムが直接やり取りをするのではなく、間にバッファがあって、対応するソケットのデータがある(受信済み)/ないか(未受信)、という問い合わせを行っているだけです。 ソケットの場合、データの送受信は非同期であり、送受信のタイミングのずれは(ソケット)バッファである程度吸収されます。もちろん、送受信バッファが満杯になった場合は流量制御が働いて、結果的に送信側の write() や send() が待ちに入ることになります。 Linux (Unix) のソケットの受信では、read() 等で指定されたバッファが常に満杯で返されるとは限らない設計になっています。つまり、その時に受信しているデータを返すだけなので、read() で返されたバイト数を必ず見ないと間違った動きになるので注意してください。

すると、全ての回答が全文表示されます。

その他の回答 (1)

  • m_mik
  • ベストアンサー率26% (31/117)
回答No.2

recv自身には、「データが届いた」か知る機能はありません。 何をするのかと言いますと、「届いているデータをバッファから取り出す」ことしかできません。 もしメッセージが届いていなかった場合には、「届くまで待つ」状態になります。(ブロッキングモード) また、届いていなかった場合には即座にエラーとなることもあります。(ノンブロッキングモード) では、どうやって届いたのかを知るのかと言いますと、サンプルにあったselectなどで、「データが届いているのか?」をバッファに対して問い合わせているのです。 NICは受信したデータを内部のバッファへと格納します。 Select関数により内部バッファにデータがあるのかを確認します。 recv関数により内部バッファからユーザのバッファへデータを転送します。 (ブロッキングモードの場合には、recvが内部バッファにデータが格納されるまで待ちます) 次にどれだけのデータを受信すればいいのかですが… 適当な量のバッファを用意して、recvに入れてもらうことになります。 ただし、recvでは「実際にバッファに入れることができたデータサイズ」を返します。実際に通信で取得できたデータサイズとは異なります。 例えば、recvで10kのバッファを用意して10kのデータを受信しようとしても、2k、4k、2kと受信が行われる場合もありえます。 この場合には、3回recvを呼び出さないといけないことになります。 どれだけのデータを通信するのかは、あらかじめ決めておくか(HTTPなどでは、ヘッダ中にデータの長さが書いてあるなど)、データがなくなる(コネクションを切断する)まで受信するという風に決めておく必要があります。

すると、全ての回答が全文表示されます。

関連するQ&A