ソケットライブラリでrecvを呼び出すときに、recvバッファーの大きさ


129

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

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

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

回答:


230

これらの質問に対する答えは、ストリームソケット(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()で割り当てられた場合)


1
使用しているプロトコルのメッセージの最後に「/ r / n / r / n」があります。そして、do whileループがあり、recvを呼び出している間に、recv_bufferの先頭にメッセージを配置します。そして、私のwhileステートメントは、このように見えますwhile((!(strstr(recv_buffer、 "\ r \ n \ r \ n"));私の質問は、1つのrecvが "\ r \ n"を取得して、次のrecv get "\ r \ n"で、while条件が真にならないようにしますか?
adhanlon

3
はい、そうです。完全なメッセージがない場合はループして、次のバイトをrecv部分的なメッセージの後にあるバッファに詰めることで、この問題を解決できます。でstrstr()埋められたrawバッファでは使用しないでくださいrecv()。nulターミネータが含まれているとは限らないため、strstr()クラッシュする可能性があります。
カフェ

3
UDPの場合、1400バイトを超えるUDPパケットを送信しても問題はありません。フラグメンテーションは完全に合法であり、IPプロトコルの基本的な部分です(IPv6でも、最初の送信者は常にフラグメンテーションを実行する必要があります)。UDPの場合、64 KBのバッファーを使用すれば常に保存されます。これは、IPパケット(v4またはv6)が64 KBを超えることはなく(フラグメント化されていても)、ヘッダーIIRCも含まれるため、データは常に確かに64 KB未満です。
Mecki

1
@caf recv()を呼び出すたびにバッファを空にする必要がありますか?私はコードのループを見てデータを収集し、それを再度ループすることで、より多くのデータを収集する必要があります。しかし、バッファがいっぱいになった場合、書き込みパスによるバッファへのメモリ割り当て量によるメモリ違反を回避するために、バッファを空にする必要はありませんか?
Alex_Nabu 2015

1
@Alex_Nabu:スペースが残っている限り、空にする必要はありませんrecv()。残りのスペースよりも多くのバイトを書き込むように指示する必要はありません。
カフェ、2015

16

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

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

はい、バッファを増やし続けることができます。あなたはoffsetから始まるバッファの真ん中にrecvを行うことができます、あなたはそうするidxでしょう:

recv(socket, recv_buffer + idx, recv_buffer_size - idx, 0);

6
2の累乗は複数の方法でより効率的になる可能性があり、強く推奨されます。
Yann Ramin、2010年

3
@theatrusについて詳しく説明すると、注目すべき効率は、モジュロ演算子をビット単位およびマスクで置き換えることができ(たとえば、x%1024 == x&1023)、整数除算を右シフト演算(たとえば、x / 1024 = = x / 2 ^ 10 == x >> 10)
vicatcu

15

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

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

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


7
UDPは常に最大で1つのUDPパケットを返し(ソケットバッファーに複数ある場合でも)、64 KBを超えるUDPパケットはありません(フラグメント化されている場合でも、IPパケットは最大で64 KBになる可能性があります)。絶対に安全で、UDPソケットでの受信中にデータが失われないことを保証します。
Mecki

7

以下のためにSOCK_STREAMあなただけ待っバイトのいくつかを引いていると、あなたは、次の呼び出しで、より取り出すことができるため、ソケット、バッファサイズは、実際に重要ではありません。余裕のあるバッファサイズを選択してください。

以下のためSOCK_DGRAMのソケットには、待機中のメッセージの嵌合部を取得し、残りは破棄されます。次のioctlを使用して待機データグラムサイズを取得できます。

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

あるいは、あなたは使うことができます MSG_PEEKMSG_TRUNCrecv()呼び出しのフラグとフラグをして、待機データグラムサイズを取得する。

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

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

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


MSG_PEEK | MSG_TRUNCは意味がありません。
ローンの侯爵

3
MSG_PEEKが待機中のメッセージを調べ(受信しない)、そのサイズを取得し(recvが切り捨てられていない実際のサイズを返す)、現在のバッファーがオーバーフローしないようにMSG_TRUNCが必要です。サイズを取得したら、適切なバッファーを割り当て、待機中のメッセージを受信します(ピークではなく、切り捨てません)。
smokku 2017年

@Alex Martelliは、64KBがUDPパケットの最大サイズであるmalloc()ため、64KBのバッファの場合、MSG_TRUNCは不要ですか?
mLstudent33

1
IPプロトコルはフラグメント化をサポートしているため、データグラムは単一のパケットよりも大きくなる可能性があります。フラグメント化され、複数のパケットで送信されます。また、SOCK_DGRAMUDPだけではありません。
smokku

1

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

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

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

最大のスループットを得るためのUDPパケットの最適サイズはどれくらいですか?
インターネット上で最大の安全なUDPパケットサイズとは


-4

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


3
TCPソケットはストリームです。つまり、recvは複数のパケットから蓄積されたデータを返す可能性があるため、パケットサイズはTCPにはまったく関係ありません。UDPの場合、各recv呼び出しは最大で1つのUDPパケットを返します。ここではパケットサイズが関係しますが、UDPパケットは必要に応じてフラグメント化される可能性があるため(通常はフラグメント化されるため)、正しいパケットサイズは約64 KBです。ただし、IPパケットは64 KBを超えることはできず、断片化されていなくてもかまいません。したがって、UDPソケットでのrecvは最大で64 KBを返すことができます(そして、返されないものは現在のパケットに対して破棄されます!)
Mecki
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.