147

C のソケット ライブラリについていくつか質問があります。質問で参照するコードのスニペットを次に示します。

char recv_buffer[3000];
recv(socket, recv_buffer, 3000, 0);
  1. recv_buffer のサイズはどのように決定すればよいですか? 私は3000を使用していますが、それは任意です。
  2. recv()バッファより大きいパケットを受信すると どうなりますか?
  3. recv を再度呼び出さずにメッセージ全体を受信したかどうかを確認し、受信するものが何もないときに永遠に待機させるにはどうすればよいですか?
  4. スペースが不足することを恐れずにバッファーに追加し続けることができるように、バッファーに一定量のスペースを持たないようにする方法はありますか? おそらく、最新の応答をバッファstrcatに連結するために使用していますか?recv()

たくさんの質問があることは承知していますが、回答をいただければ幸いです。

4

6 に答える 6

249

これらの質問に対する答えは、ストリーム ソケット ( SOCK_STREAM) とデータグラム ソケット ( )のどちらを使用しているかによって異なりSOCK_DGRAMます。TCP/IP 内では、前者は TCP に対応し、後者は UDP に対応します。

に渡されるバッファの大きさをどのように知ることができますかrecv()?

  • SOCK_STREAM:あまり関係ありません。プロトコルがトランザクション/インタラクティブなものである場合は、合理的に予想される最大の個々のメッセージ/コマンドを保持できるサイズを選択してください (3000 はおそらく問題ありません)。プロトコルが大量のデータを転送している場合、より大きなバッファがより効率的になります。経験則としては、ソケットのカーネル受信バッファ サイズとほぼ同じです (多くの場合、約 256kB)。

  • SOCK_DGRAM: アプリケーション レベルのプロトコルがこれまでに送信した最大のパケットを保持するのに十分な大きさのバッファーを使用します。UDP を使用している場合、一般に、アプリケーション レベルのプロトコルは約 1400 バイトを超えるパケットを送信すべきではありません。パケットは確実に断片化して再構築する必要があるからです。

recvバッファより大きいパケットを取得するとどうなりますか?

  • SOCK_STREAM: ストリーム ソケットにはパケットの概念がないため、質問は実際には意味がありません。パケットは単なる連続したバイト ストリームです。バッファに余裕があるよりも多くのバイトを読み取ることができる場合、それらは OS によってキューに入れられ、次の呼び出しで使用できるようになりますrecv

  • SOCK_DGRAM: 余分なバイトは破棄されます。

メッセージ全体を受信したかどうかを確認するにはどうすればよいですか?

  • SOCK_STREAM: メッセージの終わりを判断する何らかの方法をアプリケーション レベルのプロトコルに組み込む必要があります。通常、これは長さのプレフィックス (メッセージの長さで各メッセージを開始する) またはメッセージの終わりの区切り文字 (たとえば、テキストベースのプロトコルでは単なる改行である可能性があります) のいずれかです。あまり使用されない 3 番目のオプションは、各メッセージの固定サイズを義務付けることです。これらのオプションの組み合わせも可能です。たとえば、長さの値を含む固定サイズのヘッダーです。

  • SOCK_DGRAM: 1 回のrecv呼び出しで、常に 1 つのデータグラムが返されます。

スペースが不足することを恐れずにバッファーに追加し続けることができるように、バッファーに一定量のスペースを持たないようにする方法はありますか?

いいえ。ただし、 (最初にまたはでrealloc()割り当てられた場合) を使用してバッファーのサイズを変更することはできます。malloc()calloc()

于 2010-05-19T00:53:15.840 に答える
18

TCPなどのストリーミングプロトコルの場合、バッファをほぼ任意のサイズに設定できます。とはいえ、4096や8192などの2の累乗である一般的な値が推奨されます。

バッファよりも多くのデータがある場合は、次の呼び出しのためにカーネルに保存されますrecv

はい、バッファを増やし続けることができます。オフセットから開始してバッファの中央にrecvを実行できます。次のidxようにします。

recv(socket, recv_buffer + idx, recv_buffer_size - idx, 0);
于 2010-05-19T00:28:03.377 に答える
15

SOCK_STREAMソケットがある場合はrecv、ストリームから「最初の3000バイトまで」を取得します。バッファの大きさに関する明確なガイダンスはありません。ストリームの大きさを知っているのは、すべて完了したときだけです;-)。

SOCK_DGRAMソケットがあり、データグラムがバッファよりも大きい場合、データグラムのrecv最初の部分でバッファを埋め、-1を返し、errnoをEMSGSIZEに設定します。残念ながら、プロトコルがUDPの場合、これは残りのデータグラムが失われることを意味します-UDPが信頼できないプロトコルと呼ばれる理由の一部です(信頼できるデータグラムプロトコルがあることは知っていますが、あまり人気がありません-私はできませんでした後者をよく知っているにもかかわらず、TCP/IPファミリーの1つに名前を付けてください;-)。

バッファを動的に拡張するには、最初にバッファを割り当て、必要に応じmallocて使用reallocします。recvしかし、残念ながら、UDPソースからは役に立ちません。

于 2010-05-19T00:28:05.910 に答える
9

ソケットの場合SOCK_STREAM、待機中のバイトの一部をプルするだけで、次の呼び出しでさらに取得できるため、バッファ サイズは実際には問題になりません。余裕のあるバッファサイズを選択してください。

ソケットの場合SOCK_DGRAM、待機メッセージの適切な部分が取得され、残りは破棄されます。次の ioctl を使用して、待機中のデータグラム サイズを取得できます。

#include <sys/ioctl.h>
int size;
ioctl(sockfd, FIONREAD, &size);

または、呼び出しのフラグを使用MSG_PEEKして、待機中のデータグラム サイズを取得することもできます。MSG_TRUNCrecv()

ssize_t size = recv(sockfd, buf, len, MSG_PEEK | MSG_TRUNC);

待機中のメッセージを覗く (受信しない)必要がありMSG_PEEKます。recv は切り捨てられたサイズではなく、実際のサイズを返します。MSG_TRUNC現在のバッファをオーバーフローさせないでください。

malloc(size)次に、実際のバッファとrecv()データグラムだけを使用できます。

于 2016-08-09T18:32:02.543 に答える
1

テクノロジーは常に実装固有のものであるため、あなたの質問に対する絶対的な答えはありません。着信バッファ サイズが TCP 通信に問題を引き起こさないため、UDP で通信していると想定しています。

RFC 768によると、UDP のパケット サイズ (ヘッダーを含む) は 8 ~ 65,515 バイトの範囲です。したがって、着信バッファーのフェイルプルーフ サイズは 65 507 バイト (~64KB) です。

ただし、すべての大きなパケットがネットワーク デバイスによって適切にルーティングされるわけではありません。詳細については、既存の説明を参照してください。

最大スループットのための UDP パケットの最適なサイズは?
インターネット上で最大の安全な UDP パケット サイズは?

于 2010-05-19T01:22:18.160 に答える
-4

16kb が適切です。ギガビット イーサネットを使用している場合、各パケットのサイズは 9kb になる可能性があります。

于 2010-05-19T00:46:48.960 に答える