17

fd_set を反復する簡単な方法があるかどうか疑問に思っていますか? これを行う理由は、select() がこれらの fd_sets を変更して、関心のあるソケットのみを含めるようにするため、接続されているすべてのソケットをループする必要がないためです。また、直接アクセスすることを意図していない型の実装を使用することは、システムによって異なる可能性があるため、一般的に悪い考えであることも知っています。ただし、これを行うには何らかの方法が必要であり、アイデアが不足しています。だから、私の質問は:

fd_set を反復処理するにはどうすればよいですか? これが本当に悪い習慣である場合、接続されているすべてのソケットをループする以外に、「問題」を解決する方法はありますか?

ありがとう

4

7 に答える 7

11

select() を呼び出す前に fd_set 構造体を入力する必要があります。元の std::set ソケットを直接渡すことはできません。select() は、それに応じて fd_set を変更し、「設定」されていないソケットを削除し、残っているソケットの数を返します。std::set ではなく、結果の fd_set をループする必要があります。FD_ISSET() を呼び出す必要はありません。結果として得られる fd_set には、準備ができている "set" ソケットのみが含まれているためです。

fd_set read_fds;
FD_ZERO(&read_fds);

int max_fd = 0;

read_fds.fd_count = connected_sockets.size();
for( int i = 0; i < read_fds.fd_count; ++i ) 
{
    read_fds.fd_array[i] = connected_sockets[i];
    if (read_fds.fd_array[i] > max_fd)
      max_fd = read_fds.fd_array[i];
}

if (select(max_fd+1, &read_fds, NULL, NULL, NULL) > 0)
{ 
    for( int i = 0; i < read_fds.fd_count; ++i ) 
        do_socket_operation( read_fds.fd_array[i] ); 
} 

FD_ISSET() がより頻繁に使用されるのは、select() でエラー チェックを使用する場合です。

fd_set read_fds;
FD_ZERO(&read_fds);

fd_set error_fds;
FD_ZERO(&error_fds);

int max_fd = 0;

read_fds.fd_count = connected_sockets.size();
for( int i = 0; i < read_fds.fd_count; ++i ) 
{
    read_fds.fd_array[i] = connected_sockets[i];
    if (read_fds.fd_array[i] > max_fd)
      max_fd = read_fds.fd_array[i];
}

error_fds.fd_count = read_fds.fd_count;
for( int i = 0; i < read_fds.fd_count; ++i ) 
{
    error_fds.fd_array[i] = read_fds.fd_array[i];
}

if (select(max_fd+1, &read_fds, NULL, &error_fds, NULL) > 0)
{ 
    for( int i = 0; i < read_fds.fd_count; ++i ) 
    {
        if( !FD_ISSET(read_fds.fd_array[i], &error_fds) )
            do_socket_operation( read_fds.fd_array[i] ); 
    }

    for( int i = 0; i < error_fds.fd_count; ++i ) 
    {
        do_socket_error( error_fds.fd_array[i] ); 
    }
} 
于 2010-09-07T19:36:11.177 に答える
7

Select は、セット内のファイル記述子に対応するビットを設定します。そのため、関心のあるファイル記述子のみをテストするだけで、少数のファイル記述子のみに関心がある場合 (および他のものを無視できる場合) は、すべての fds を反復する必要はありません。 .

if (select(fdmax+1, &read_fds, NULL, NULL, NULL) == -1) {
   perror("select");
   exit(4);
}

if(FD_ISSET(fd0, &read_fds))
{
   //do things
}

if(FD_ISSET(fd1, &read_fds))
{
   //do more things
}

編集
fd_set 構造体は次のとおりです。

typedef struct fd_set {
        u_int   fd_count;               /* how many are SET? */
        SOCKET  fd_array[FD_SETSIZE];   /* an array of SOCKETs */
} fd_set;

ここで、fd_count はソケット セットの数 (したがって、これを使用して最適化を追加できます) であり、fd_array はビット ベクトル (サイズは FD_SETSIZE * sizeof(int)であり、マシンに依存します) です。私のマシンでは、64 * 64 = 4096 です。

したがって、あなたの質問は基本的に次のとおりです。ビットベクトル (約 4096 ビットのサイズ) で 1 のビット位置を見つける最も効率的な方法は何ですか?

ここで 1 つのことを明確にしたいと思います。
「接続されているすべてのソケットをループする」ということは、実際に接続に対して何かを読んだり実行したりしていることを意味するわけではありません。FD_ISSET() は、接続の割り当てられた file_descriptor 番号に位置する fd_set のビットが設定されているかどうかのみをチェックします。効率が目的なら、これが最も効率的ではないでしょうか。ヒューリスティックを使用していますか?

この方法の問題点と、別の方法を使用して何を達成しようとしているのかを教えてください。

于 2010-09-07T18:18:49.337 に答える
4

それはかなり簡単です:

for( int fd = 0; fd < max_fd; fd++ )
    if ( FD_ISSET(fd, &my_fd_set) )
        do_socket_operation( fd );
于 2010-09-07T18:17:02.583 に答える
4

このループはselect()インターフェイスの制限です。の基本的な実装fd_setは通常、ビット セットです。これは明らかに、ソケットを探すためにビットをスキャンする必要があることを意味します。

いくつかの代替インターフェイスが作成されたのは、まさにこのためです。残念ながら、それらはすべて OS 固有です。たとえば、Linuxには、アクティブなファイル記述子のみのリストを返すepollが用意されています。FreeBSD と Mac OS X は両方ともkqueueを提供し、同じ結果を達成します。

于 2010-09-08T02:36:48.853 に答える
1

Beejのネットワーキングガイドのこのセクション7.2を参照してください-'7.2 。select()-FD_ISSETを使用した「同期I/O多重化」。

つまり、ファイル記述子が読み取り/書き込みの準備ができているかどうかを判断するには、fd_setを反復処理する必要があります...

于 2010-09-07T18:14:23.900 に答える
0

あなたがやろうとしていることは良い考えではないと思います。

最初はシステムに依存しますが、あなたはすでにそれを知っていると思います。

次に、内部レベルでは、これらのセットは整数の配列として格納され、fdsはセットビットとして格納されます。selectのmanページによると、FD_SETSIZEは1024です。繰り返して興味のあるfdを取得したい場合でも、ビット操作の混乱とともにその番号をループする必要があります。したがって、選択時にFD_SETSIZE fdを超えるのを待っている場合を除いて、それは可能ではないと思いますが、それは良い考えではありません。

えっ、ちょっと待って!!。いずれにせよ、それは良い考えではありません。

于 2010-09-07T18:31:31.603 に答える
0

select()通話を効率的に使用して多くのことができるとは思いません。「The C10K problem」の情報はまだ有効です。

プラットフォーム固有のソリューションがいくつか必要になります。

または、イベント ライブラリを使用してプラットフォームの詳細を非表示にすることもできますlibev

于 2013-03-15T20:58:48.427 に答える