web-dev-qa-db-ja.com

ソケットのブロック:「send()」が正確に戻るのはいつですか?

正確には、BSDソケットsend()関数が呼び出し元に戻るのはいつですか?

ノンブロッキングモードでは、すぐに戻るはずですよね?

blockingモードに関しては man page はこう言います:

メッセージがソケットの送信バッファーに収まらない場合、send()は、ソケットが非ブロッキングI/Oモード。

質問:

  1. これは、カーネル送信バッファーに空きがある場合、send()呼び出しが常にすぐに戻ることを意味しますか?
  2. send()呼び出しの動作とパフォーマンスはTCPとUDPで同じですか?一致しない場合、なぜ一致しないのですか?
41
David Citron

これは、カーネル送信バッファーに空きがある場合、send()呼び出しが常にすぐに戻ることを意味しますか?

はい。指定したメモリがカーネルのバッファにコピーされた直後を意味する限り。一部のEdgeのケースでは、これはそれほど迅速ではない場合があります。たとえば、渡すポインターがメモリ違反のファイルまたはスワップからバッファーをプルする必要があるページフォールトをトリガーする場合、呼び出しの戻りに大幅な遅延が追加されます。

Send()呼び出しの動作とパフォーマンスはTCPとUDPで同じですか?そうでない場合、なぜそうではないのですか?

結構です。可能なパフォーマンスの違いは、TCP/IPスタックのOSの実装によって異なります。理論的には、UDPソケットはOSが実行する必要がある処理が少ないため、少し安くなる可能性があります。

EDIT:一方、TCPを使用すると、システムコールごとにはるかに多くのデータを送信できるため、通常、TCPを使用するとバイトあたりのコストが大幅に低くなります。 。これは、最近のLinuxカーネルでは sendmmsg() で軽減できます。

動作については、ほぼ同じです。

ソケットをブロックする場合、TCPとUDPの両方が、カーネルバッファーにスペースができるまでブロックされます。ただし、UDPソケットは、バッファー全体がカーネルバッファーに格納できるようになるまで待機するのに対し、TCPソケットは、カーネルバッファーに1バイトだけをコピーするように決定する場合があります(通常、1バイト以上です)。バイト)。

64kiBより大きいパケットを送信しようとすると、UDPソケットは[〜#〜] emsgsize [〜#〜]で一貫して失敗する可能性があります。これは、UDPがdatagramソケットであるため、バッファ全体を単一のIPパケット(または一連のIPパケットフラグメント)として送信するか、まったく送信しないことが保証されるためです。

非ブロッキングソケットはブロッキングバージョンと同じように動作しますが、ブロックする代わりに(カーネルバッファーに十分なスペースがない場合)、呼び出しは失敗します[〜#〜] eagain [ 〜#〜](または[〜#〜] ewouldblock [〜#〜])。これが発生したら、ソケットをepoll/kqueue/select(または使用しているもの)に戻して、ソケットが再び書き込み可能になるのを待ちます。

POSIXで作業するときはいつものように、呼び出しが[〜#〜] eintr [〜#〜]で失敗する可能性があることに注意してください(呼び出しが信号によって中断されます)。この場合、おそらくsend()を再度呼び出したいでしょう。

34
Arvid

カーネルバッファーに空きがある場合、send()はバッファーにできるだけ多くのバイトをコピーしてすぐに終了し、実際にコピーされたバイト数を返します(要求されたバイト数よりも少ない場合があります)。カーネルバッファーにスペースがない場合、send()は、スペースが利用可能になるか、タイムアウトが発生する(設定されている場合)まで、ブロックします。

5
Remy Lebeau

Send()は、データがカーネルに受け入れられるとすぐに戻ります。ソケットをブロックする場合:send()は、カーネルバッファーがsend()呼び出しに提供されたデータを取り込むのに十分な空きがない場合、ブロックします。

非ブロッキングソケット:send()はブロックしませんが、失敗して-1を返すか、部分的にコピーされたバイト数を返します(使用可能なバッファスペースによって異なります)。 errno EWOULDBLOCKまたはEAGAINを設定します。つまり、send()の時点では、バッファーはすべてのバイトを取り込むことができなかったため、select()呼び出しでデータをsend()に再試行する必要があります。または、sleep()でループを作成してsend()を呼び出すこともできますが、実際に送信されるバイト数と送信される残りのバイト数に注意する必要があります。

1
Sumit Trehan

これは、カーネル送信バッファーに空きがある場合、send()呼び出しが常にすぐに戻ることを意味しますか?

そうじゃないの?データが送信される瞬間は、別の方法で定義できます。これは、OSがスタックでの配信のためにデータを受け入れた瞬間だと思います。そうでなければ、それを定義することは非常に困難です。データがネットワークカードバッファに送信されるのは一瞬ですか?または、データがネットワークカードバッファーから押し出された後ですか?

これを確実に知る必要がある問題はありますか、それとも興味がありますか?

0

あなたの推定は正しいです。カーネル送信バッファーに空きがある場合、カーネルはデータを送信バッファーにコピーし、send()が返されます。

0
caf