私はTCPソケットで読み取り可能なバイト数を知りたいと思っています。実際にこの値を与えるはずのフラグ "FIONREAD"を指定してioctlを呼び出しています。 functionはreturn val 0(したがってErrorなし)として取得しますが、整数引数も値0を取得します。これは問題ありませんが、recv()メソッドを呼び出すと、実際にソケットからいくつかのバイトを読み取ります。違う?
//ここにいくつかのコード:
char recBuffer[BUFFERLENGTH] = {0};
int bytesAv = 0;
int bytesRead = 0;
int flags = 0;
if ( ioctl (m_Socket,FIONREAD,&bytesAv) < 0 )
{
// Error
}
if ( bytesAv < 1 )
{
// No Data Available
}
bytesRead = recv(m_Socket,recBuffer,BUFFERLENGTH,flags);
Recv関数を呼び出すと、いくつかの有効なデータを実際に読み取りました(予想していた)
非常に迅速に発生しているため、何も表示されません。あなたがしていること:
ioctl
:データはありますか? いいえ、まだ何もありませんrecv
:データが届くまでブロックします。しばらくして(短い)時間:これはあなたのデータですしたがって、本当にFIONREAD
を見たい場合は、そのまま待ってください。
/* Try FIONREAD until we get *something* or ioctl fails. */
while (!bytesAv && ioctl (m_Socket,FIONREAD,&bytesAv) >= 0)
sleep(1);
ここでの本当の答えは、cnicutarが言ったようにselect(2)を使用することです。トビー、あなたが理解していないのは、あなたが競争状態にあるということです。まず、ソケットを見て、そこに何バイトあるかを尋ねます。次に、コードが「データなし」ブロックを処理している間、ハードウェアとOSがアプリケーションと非同期でバイトを受信します。したがって、recv()関数が呼び出されるまでに、「利用可能なバイトがありません」という答えはもはや当てはまりません...
if ( ioctl (m_Socket,FIONREAD,&bytesAv) < 0 )
{ // Error
}
// BYTES MIGHT BE RECEIVED BY HARDWARE/OS HERE!
if ( bytesAv < 1 ) // AND HERE!
{
// No Data Available
// BUT BYTES MIGHT BE RECEIVED BY HARDWARE/OS HERE!
}
// AND MORE BYTES MIGHT BE RECEIVED BY HARDWARE/OS HERE!
bytesRead = recv(m_Socket,recBuffer,BUFFERLENGTH,flags);
// AND NOW bytesRead IS NOT EQUAL TO 0!
確かに、わずかな睡眠で2年前にプログラムが修正された可能性がありますが、それによってひどいコーディングプラクティスがわかり、select()を使用してソケットを正しく使用する方法を学ぶ機会を失いました。
さらに、Karoly Horvathが言ったように、ユーザーが渡したバッファーに保存できるバイト数を超えて読み取らないようにrecvに指示できます。その後、関数インターフェイスは「このfnはソケットで利用可能なバイト数を返しますが、 [渡したバッファサイズ]以下。
これは、この関数がバッファをクリアすることを心配する必要がないことを意味します。呼び出し元は、関数からすべてのバイトをクリアするために必要な回数だけ関数を呼び出すことができます(または、データホールセールを破棄し、特定のデータ収集関数でその機能を拘束しない別のfnを提供できます)。あまり多くのことをしないことで、関数はより柔軟になります。次に、特定のアプリケーションのデータ転送ニーズに適したラッパー関数を作成し、その特定のアプリの必要に応じて、fnがget_data fnおよびclear_socket fnを呼び出すようにします。これで、プロジェクト間で持ち運べるライブラリを構築しています。幸運なことに、コードを持っていくことができる雇用主がいる場合は、仕事も仕事もできます。
Select()、ioctl(FIONREAD)、recv()の順に使用します
ブロックI/Oを使用している場合、何も問題はありません。recv()は、データが利用可能になるまでブロックします。
Dockerを使用している人にとって、tcpreplayを使用して外部(この場合はUbuntu 16.04)からdockerのインターフェース(この場合はdocker0)にパケットを送信しようとしたときに同じ問題が発生しました。
回避策は、Docker内でtcpreplayを実行することでした。
Ioctl()がdockerでサポートされているかどうかわからないので、解決策ではありません。