10

バークレー ソケットと TCP (SOCK_STREAM ソケット) を使用しています。

プロセスは次のとおりです。

  1. リモートアドレスに接続します。
  2. 私はそれにメッセージを送ります。
  3. そこからメッセージが届きます。

次のバッファを使用していると想像してください。

char recv_buffer[3000];
recv(socket, recv_buffer, 3000, 0);

質問は次のとおりです。

  • recv を初めて呼び出した後、読み取りバッファーが空かどうかを知るにはどうすればよいですか? 空でない場合は、recv を再度呼び出す必要がありますが、空のときにそれを行うと、長時間ブロックされます。
  • recv_buffer に読み取ったバイト数を知るにはどうすればよいですか? 受信したメッセージに null バイトが含まれている可能性があるため、strlen を使用できません。

ありがとう。

4

4 に答える 4

12

recv を初めて呼び出した後、読み取りバッファーが空かどうかを知るにはどうすればよいですか? 空でない場合は、recv を再度呼び出す必要がありますが、空のときにそれを行うと、長時間ブロックされます。

selectまたはpollシステム コールをソケット記述子とともに使用して、ソケットからの読み取りを待機しているデータがあるかどうかを確認できます。

ただし、通常、送信者と受信者の両方が従う合意済みのプロトコルが存在する必要があります。これにより、両当事者が転送されるデータの量を知ることができます。たとえば、おそらく送信者は、送信するバイト数を示す 2 バイトの整数を最初に送信します。受信側は、最初にこの 2 バイト整数を読み取り、ソケットから読み取るバイト数を認識します。

とにかく、Tony が以下で指摘したように、堅牢なアプリケーションは、ヘッダー内の長さ情報の組み合わせを使用する必要があり、 への各呼び出しの前recvに (またはノンブロッキング ソケットを使用して) 追加データのソケットをポーリングすることと組み合わせて使用​​する必要があります。これにより、たとえば、(ヘッダーから) 100 バイトの読み取りが残っていることがわかっているが、何らかの理由でピアがデータの送信に失敗した場合に、アプリケーションがブロックされるのを防ぐことができます (おそらくピア コンピューターが予期せずオフになる)、recv通話がブロックされる原因となります。

recv_buffer に読み取ったバイト数を知るにはどうすればよいですか? 受信したメッセージに null バイトが含まれている可能性があるため、strlen を使用できません。

システム コールは、recv読み取ったバイト数を返すか、エラーが発生した場合は -1 を返します。

recv(2) の man ページから:

[recv] は、受信したバイト数を返します。エラーが発生した場合は -1 を返します。ピアが正常なシャットダウンを実行した場合、戻り値は 0 になります。

于 2010-12-06T01:41:26.757 に答える
2

recv を初めて呼び出した後、読み取りバッファーが空かどうかを知るにはどうすればよいですか?

初めて (クライアントを受け入れた後) でも、クライアント接続が失われた場合、recv がブロックされて失敗する可能性があります。次のいずれかを行う必要があります。

  • selector (BSDソケット)またはOS固有の同等のものを使用pollすると、特定のソケット記述子で利用可能なデータがあるかどうかがわかります(例外条件、およびより多くの出力を書き込むことができるバッファスペース)
  • ソケットをノンブロッキングに設定できます。これにより、recvすぐに利用できるものだけが返されます (おそらく何も返されません) 。
  • データをブロックする余裕があるスレッドを作成できます。recv他のスレッドが、続行することに関心のある他の作業を行っていることを知っています。

recv_buffer に読み取ったバイト数を知るにはどうすればよいですか? 受信したメッセージに null バイトが含まれている可能性があるため、strlen を使用できません。

recv()読み取ったバイト数を返すか、エラーの場合は -1 を返します。

TCP はバイト ストリームプロトコルであることに注意してください。つまり、TCPからバイトを正しい順序で読み書きできることのみが保証されますが、メッセージ境界が保持されることは保証されません。したがって、送信者がソケットに大きな単一の書き込みを行ったとしても、途中で断片化されていくつかの小さなブロックに到着するか、いくつかの小さなsend()/が統合されて 1 つの/write()によって取得される可能性があります。recv()read()

そのため、recv必要なすべてのデータ (つまり、処理できる完全な論理メッセージ) を取得するか、エラーが発生するまで、呼び出しをループするようにしてください。クライアントから後続の の一部/すべてを取得する準備ができている/処理できる必要がありsendます (両側から完全なメッセージを取得した後にのみ送信するプロトコルがなく、メッセージの長さを持つヘッダーを使用していない場合) . メッセージ ヘッダー (長さを含む) の recvs を実行してから本文を実行すると、より多くの への呼び出しが発生しrecv()、パフォーマンスに悪影響を及ぼす可能性があることに注意してください。

これらの信頼性の問題は、しばしば無視されます。単一のホスト、信頼性が高く高速な LAN、関係するルーターとスイッチの数が少なく、メッセージが少ないか非同時の場合、それらが現れる頻度は低くなります。その後、負荷がかかったり、より複雑なネットワーク上で壊れたりする可能性があります。

于 2010-12-06T02:13:23.847 に答える
0
  1. recv()が 3000 バイト未満を返した場合は、読み取りバッファーが空だったと見なすことができます。3000 バイトのバッファーに 3000 バイトが返された場合は、続行するかどうかを判断する必要があります。ほとんどのプロトコルには、TLV のバリエーション (タイプ、長さ、値) が含まれています。各メッセージには、メッセージのタイプ、長さ (長さが固定されている場合はタイプによって暗示される可能性があります)、および値の標識が含まれます。受け取ったデータを読んで、最後の単元が不完全であることがわかった場合は、さらに読み取る必要があると推測できます。ソケットをノンブロッキング ソケットにすることもできます。recv()読み取り用に読み取られたデータがない場合、EAGAIN または EWOULDBLOCK で失敗します。

  2. このrecv()関数は、読み取ったバイト数を返します。

于 2010-12-06T01:44:54.653 に答える
0

FIONREAD オプションを指定した ioctl() は、ブロックせずに現在どれだけのデータを読み取ることができるかを示します。

于 2011-02-13T03:04:40.713 に答える