web-dev-qa-db-ja.com

fd_setを反復処理する方法

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

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

ありがとう

17
Andreas

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のビットが設定されているかどうかのみをチェックします。効率があなたの目標であるなら、これは最も効率的ではありませんか?ヒューリスティックを使用していますか?

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

6
lalli

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

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] ); 
    }
} 
11
Remy Lebeau

それはかなり簡単です:

for( int fd = 0; fd < max_fd; fd++ )
    if ( FD_ISSET(fd, &my_fd_set) )
        do_socket_operation( fd );
4
Rakis

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

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

3
caf

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

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

1
t0mm13b

select()呼び出しを効率的に使用して多くのことができるとは思いません。 " C10K問題 "の情報はまだ有効です。

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

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

0
qunying

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

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

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

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

0
aeh