25

ストリームソケットでデータを送信する標準的な方法は、常に、書き込むデータのチャンクを使用してsendを呼び出し、戻り値をチェックしてすべてのデータが送信されたかどうかを確認してから、メッセージ全体が受け入れられるまでsendを呼び出し続けることです。

たとえば、これは一般的なスキームの簡単な例です。

int send_all(int sock、unsigned char * buffer、int len){
  int nsent;

  while(len> 0){
    nsent = send(sock、buffer、len、0);
    if(nsent == -1)//エラー
      -1を返します。

    バッファ+=nsent;
    len-= nsent;
  }
  0を返します。// OK、送信されたすべてのデータ
}

BSDのマンページでさえそれについて言及しています

...送信するメッセージを保持するためにソケットに使用可能なメッセージスペースがない場合、send()は通常ブロックします...

これは、sendがすべてのデータを送信せずに戻る可能性があると想定する必要があることを示しています。今ではこれはかなり壊れていると思いますが、W。リチャードスティーブンスでさえ、最初の章ではなく、ネットワークプログラミングに関する彼の標準的な参考書でこれを想定していますが、より高度な例では、writeを呼び出す代わりに彼自身のwriten(すべてのデータを書き込む)関数を使用しています。

送信がすべてのデータを送信できないか、基になるバッファ内のデータを受け入れることができず、ソケットがブロックされている場合、送信要求全体が受け入れられたときに送信をブロックして返す必要があるため、これはまだ多かれ少なかれ壊れていると思います。

つまり、上記のコード例では、送信されたデータが少ない状態でsendが返されると、新しいリクエストで再度呼び出されます。前回の電話から何が変わったのですか?最大で数百のCPUサイクルが経過したため、バッファーはまだいっぱいです。send nowがデータを受け入れる場合、なぜ以前はそれを受け入れることができなかったのですか?

そうしないと、データを受け入れて試行を続けることができないソケットでデータを送信しようとする非効率的なループになってしまいます。

したがって、回避策は、必要に応じて、非常に非効率的なコードになるようです。そのような状況では、ソケットのブロックを回避する必要があり、代わりにselectと一緒に非ブロックソケットを使用する必要があります。

4

1 に答える 1

24

上記の説明に欠けているのは、Unixでは、システムコールがシグナルで中断される可能性があることです。これが、ブロッキングsend(2)が短いカウントを返す可能性がある理由です。

于 2010-04-11T21:21:56.327 に答える