2012-04-20 1 views
0

データグラムソケットを監視するスレッドでselect()を使用していますが、スレッドが開始する前にデータがソケットに送信されていない限り、select()は0を返し続けます。なぜ、データがすでに送信されている場合、選択はファイルディスクリプタを準備完了として表示するのですか?

私はCとC++はほとんどありません。

bool RelayStart() { 
    sock_recv = socket(AF_INET, SOCK_DGRAM, 0); 
    memset(&addr_recv, 0, sizeof(addr_recv)); 
    addr_recv.sin_family = AF_INET; 
    addr_recv.sin_port = htons(18902); 
    addr_recv.sin_addr.s_addr = htonl(INADDR_ANY); 
    bind(sock_recv, (struct sockaddr*) &addr_recv, sizeof(addr_recv)); 

    isRelayingPackets = true; 

    NSS::Thread::start(VIDEO_SEND_THREAD_ID); 

    return true; 
} 

スレッドを停止する方法:

bool RelayStop() { 
    isSendingVideo = false; 
    NSS::Thread::stop(); 
    close(sock_recv); 
    return true; 
} 

とスレッド内のメソッドを実行します。ここでスレッドを開始する方法があります

void Run() { 

    fd_set read_fds; 
    int select_return; 
    struct timeval select_timeout; 

    FD_ZERO(&read_fds); 
    FD_SET(sock_recv, &read_fds); 

    while (isRelayingPackets) { 

     select_timeout.tv_sec = 1; 
     select_timeout.tv_usec = 0; 

     select_return = select(sock_recv + 1, &read_fds, NULL, NULL, &select_timeout); 
     if (select_return > 0 && FD_ISSET(sock_recv, &read_fds)) { 
      // ... 
     } 
    } 
} 

問題は、もし存在していますRelayStart()が呼び出される前にすでにUDPパケットをポート18902に送信しているプロセスではないため、select()は常に0を返します。たとえば、スレッドを再起動せずに送信者を再起動することはできませんrder。

送信者が最初に開始されている限り、すべてうまくいくようです。

+2

'socket()'と 'bind()'がエラーを報告していないことを確認しましたか?あなたのコードにはエラーチェックがありません。また、スレッドが実行中にソケットがエラーを報告している場合に 'select()'を呼び出すときに例外 'fd_set'を含むようにしてください。 –

+0

エラーは例外セットには表示されませんが、奇妙なことですが、読み取りセット内にあります。帯域外データのみが例外セットに現れます。 – geekosaur

+0

私は堅牢なエラーチェックをしていない間、select()の戻り値を見ていましたが、一貫して0(エラーではありません)でした。 – Matt

答えて

4

Runスレッドは、read_fdsを1回だけ構成します。

selectread_fdsは、データが準備されていないすべてのディスクリプタとその前に設定されたデータ用に設定されたすべてのビットをすべてクリアしてデータを準備できるようにコールを更新します。

したがって、記述子にデータが準備されておらず、呼び出しがタイムアウトして(0を返す)、read_fdsのすべてのビットがクリアされるようになりました。同じオールゼロビットマスクを渡す呼び出しは、ファイル記述子をスキャンしません。

あなたはどちらかの再構築読み取り設定ループ内の各旅行にすることができます:

while (isRelayingPackets) { 
    FD_ZERO(&read_fds); 
    FD_SET(sock_recv, &read_fds); 
    ... 
} 

またはビット・セットのコピーと補助変数を使用します。

while (isRelayingPackets) { 
    fd_set select_arg = read_fds; 
    ... same as before but use &select_arg ... 
} 

(もちろん、いくつかの点で使いやすい非selectインターフェイスがあります。

+0

ああ、私はマニュアルでこの詳細を見落としました。「終了時(選択から)、実際にどのファイル記述子がステータスを変更したかを示すために、セットが修正されました。 FD_ISSET()マクロは、FDが変更されたセットの一部であるかどうかを判断するために存在します。ありがとう!あなたは簡単に非選択インターフェースをリストすることができますので、私はそれを見て読んでみることができますか? – Matt

+1

easy(?)は 'poll'です。ファンキーで効率的ですが、巨大なディスクリプタセットのLinux特有のものは 'epoll'です。 BSDには 'kqueue'があります。 – torek

+0

私は投票とepollに精通しており、それらを考慮しました。なぜ私が選択を選んだのか覚えていない。私はそれらをもう一度見直します。ありがとう! – Matt

0

どのように動作すると思いますか? select()のポイントは、データを読み取ることができるまでタイムアウトにスリープすることです。この場合、1秒後にタイムアウトして0を返します。おそらく、ストリームの開始前に実際にタイムアウトしたくないのでしょうか?

+1

デュークはデータは送信されていますが、 () 'は報告していません。私は 'select()'の問題が疑われる前にソケット自体に問題があると思うでしょう。 –

+0

レミーは正しいです。 select()が呼び出されている間にデータが実際に送信されていることを明確にしようとしていました。 – Matt

関連する問題