4

C++ で jabber サーバー アプリケーションと別の jabber クライアント アプリケーションを使用しています。

クライアントが大量のメッセージ (1 秒あたり 20 件以上) を送受信すると、select がフリーズして返されなくなります。

netstat を使用すると、Linux でソケットが接続されたままになり、tcpdump を使用すると、メッセージはクライアントに送信されますが、select は返されません。

を選択するコードは次のとおりです。

bool ConnectionTCPBase::dataAvailable( int timeout )
  {
    if( m_socket < 0 )
      return true; // let recv() catch the closed fd

    fd_set fds;
    struct timeval tv;

    FD_ZERO( &fds );
    // the following causes a C4127 warning in VC++ Express 2008 and possibly other versions.
    // however, the reason for the warning can't be fixed in gloox.
    FD_SET( m_socket, &fds );

    tv.tv_sec = timeout / 1000000;
    tv.tv_usec = timeout % 1000000;

    return ( ( select( m_socket + 1, &fds, 0, 0, timeout == -1 ? 0 : &tv ) > 0 )
             && FD_ISSET( m_socket, &fds ) != 0 );
  }

そして、デッドロックはgdbにあります:

Thread 2 (Thread 0x7fe226ac2700 (LWP 10774)):
#0  0x00007fe224711ff3 in select () at ../sysdeps/unix/syscall-template.S:82
#1  0x00000000004706a9 in gloox::ConnectionTCPBase::dataAvailable (this=0xcaeb60, timeout=<value optimized out>) at connectiontcpbase.cpp:103
#2  0x000000000046c4cb in gloox::ConnectionTCPClient::recv (this=0xcaeb60, timeout=10) at connectiontcpclient.cpp:131
#3  0x0000000000471476 in gloox::ConnectionTLS::recv (this=0xd1a950, timeout=648813712) at connectiontls.cpp:89
#4  0x00000000004324cc in glooxd::C2S::recv (this=0xc5d120, timeout=10) at c2s.cpp:124
#5  0x0000000000435ced in glooxd::C2S::run (this=0xc5d120) at c2s.cpp:75
#6  0x000000000042d789 in CNetwork::run (this=0xc56df0) at src/Network.cpp:343
#7  0x000000000043115f in threading::ThreadManager::threadWorker (data=0xc56e10) at src/ThreadManager.cpp:15
#8  0x00007fe2249bc9ca in start_thread (arg=<value optimized out>) at pthread_create.c:300
#9  0x00007fe22471970d in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:112
#10 0x0000000000000000 in ?? ()

まだ彼に送信しているのに、select がメッセージの受信を停止する原因を知っていますか? ソケットを介して大量のメッセージを送受信する場合、Linux にバッファ制限はありますか?

ありがとう

4

1 に答える 1

1

いくつかの可能性があります。

超過FD_SETSIZE

コードは負のファイル記述子をチェックしていますが、上限FD_SETSIZE(通常は1024)を超えていないかどうかを確認しています。それが起こるときはいつでも、あなたのコードは

  • 自身のスタックを破壊する
  • fd_setに空を提示するselectと、ハングが発生します

同時に開いているファイル記述子をそれほど多く必要としない場合、解決策はおそらく、ファイル記述子のリーク、特に放棄された記述子のクローズを処理するスタックのコードを削除することです。

コードに、リークの可能性を示す疑わしいコメントがあります。

// let recv() catch the closed fd

このコメントが誰かm_socketが-1に設定し、aが閉じたソケットをキャッチして閉じることを期待しているrecv場合は、実際の閉じたソケットではなく-1を閉じている可能性があります。close(ネットワークレベルで閉じることと、別の呼び出しが必要なファイル記述子レベルで閉じることの違いに注意してください。)

これはに移動することでも処理できますがpoll、オペレーティングシステムによって課せられる他のいくつかの制限により、このルートは非常に困難になります。

帯域外データ

サーバーがデータを「送信」していると言います。sendそれがデータが(呼び出しではなくwrite)呼び出しを使用して送信されることを意味する場合は、を使用straceして送信フラグ引数を決定します。フラグが使用されている場合MSG_OOB、データは帯域外データとして到着します。別のパラメーターとしてselectコピーを渡すまで、呼び出しはそれらに気づきません。fds

fd_set fds_copy = fds;
select( m_socket + 1, &fds, 0, &fds_copy, timeout == -1 ? 0 : &tv )

プロセスの飢餓

ボックスがひどく過負荷になっている場合、サーバーはブロック呼び出しなしで実行されており、リアルタイムの優先順位で実行されています(topそれをチェックするために使用します)-そしてクライアントはそうではありません-クライアントは飢えている可能性があります。

中断されたプロセス

クライアントは理論的にはで停止する可能性がありますSIGSTOP。おそらく、これが当てはまるかどうか、どこctrl-Zかを押した、または自分で開始する以外の特定のプロセスがクライアントを制御していることを知っているでしょう。

于 2012-06-24T20:18:20.770 に答える