ソケットライブラリでrecvを呼び出すときのrecvバッファーの大きさ
Cのソケットライブラリについていくつか質問があります。質問で参照するコードスニペットを以下に示します。
char recv_buffer[3000];
recv(socket, recv_buffer, 3000, 0);
- Recv_bufferの大きさを決めるにはどうすればよいですか? 3000を使用していますが、それは任意です。
recv()
が私のバッファーより大きいパケットを受信した場合はどうなりますか?- recvを再度呼び出さずにメッセージ全体を受信し、受信するものがない場合にメッセージを永久に待機させるにはどうすればよいですか?
- バッファが一定の容量を持たないようにする方法はありますか?
strcat
を使用して、バッファへの最新のrecv()
応答を連結することができますか?
たくさんの質問があることは知っていますが、どんな回答でも大歓迎です。
これらの質問に対する答えは、ストリームソケット(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
:単一のrecv
呼び出しは常に単一のデータグラムを返します。
スペースが不足することを恐れずにバッファーに追加し続けることができるように、バッファーに一定のスペースがないようにする方法はありますか?
いいえ。ただし、realloc()
を使用してバッファのサイズを変更することができます(元々malloc()
またはcalloc()
で割り当てられていた場合)。
TCPなどのストリーミングプロトコルの場合、バッファを任意のサイズに設定できます。ただし、4096や8192などの2の累乗である一般的な値が推奨されます。
バッファのデータよりも多くのデータがある場合は、recv
への次回の呼び出しのためにカーネルに保存されます。
はい、バッファを増やし続けることができます。オフセットidx
から始まるバッファーの中央にrecvを実行できます。
recv(socket, recv_buffer + idx, recv_buffer_size - idx, 0);
SOCK_STREAM
ソケットがある場合、recv
はストリームから「最初の3000バイトまで」を取得します。バッファの大きさに関する明確なガイダンスはありません。ストリームの大きさを知っているのは、すべて完了したときだけです;-)。
SOCK_DGRAM
ソケットがあり、データグラムがバッファより大きい場合、recv
はバッファにデータグラムの最初の部分を入れ、-1を返し、errnoをEMSGSIZEに設定します。残念ながら、プロトコルがUDPの場合、これはデータグラムの残りが失われることを意味します-UDPが呼び出される理由の一部信頼できないプロトコル(信頼できるデータグラムプロトコルがあることは知っていますが、そうではありません人気-後者をかなりよく知っているにもかかわらず、TCP/IPファミリーに名前を付けることはできませんでした;-)。
バッファを動的に拡張するには、最初にmalloc
で割り当て、必要に応じてrealloc
を使用します。しかし、それはUDPソースからのrecv
には役立ちません。
SOCK_STREAM
ソケットの場合、待機中のバイトの一部を引き出しているだけで、次の呼び出しでさらに多くを取得できるため、バッファーサイズは実際には重要ではありません。余裕のあるバッファサイズを選択してください。
SOCK_DGRAM
ソケットの場合、待機メッセージの適切な部分を取得し、残りは破棄されます。次のioctlを使用して、待機中のデータグラムサイズを取得できます。
#include <sys/ioctl.h>
int size;
ioctl(sockfd, FIONREAD, &size);
または、recv()
呼び出しのMSG_PEEK
およびMSG_TRUNC
フラグを使用して、待機中のデータグラムサイズを取得できます。
ssize_t size = recv(sockfd, buf, len, MSG_PEEK | MSG_TRUNC);
待機メッセージを覗く(受信しない)には、MSG_PEEK
が必要です。recvは、切り捨てられていない実際のサイズを返します。現在のバッファをオーバーフローさせないためには、MSG_TRUNC
が必要です。
そうすれば、malloc(size)
実際のバッファとrecv()
データグラムだけを作成できます。
テクノロジーは常に実装固有であるため、質問に対する絶対的な答えはありません。着信バッファサイズはTCP通信に問題をもたらさないため、UDPで通信していると想定しています。
RFC 768 によると、UDPのパケットサイズ(ヘッダーを含む)の範囲は8〜65 515バイトです。したがって、着信バッファのフェイルプルーフサイズは65 507バイト(〜64KB)です。
ただし、すべての大きなパケットがネットワークデバイスによって適切にルーティングできるわけではありません。詳細については、既存の説明を参照してください。