2010-09-07 4 views
16

Mi chiedo se c'è un modo semplice per scorrere un fd_set? La ragione per cui voglio farlo è di non dover eseguire il ciclo di tutti i socket connessi, dato che select() altera questi fd_set per includere solo quelli a cui sono interessato. So anche che l'utilizzo di un'implementazione di un tipo che non è destinato ad essere direttamente accessibile è generalmente una cattiva idea in quanto può variare tra diversi sistemi. Tuttavia, ho bisogno di un modo per farlo, e sono a corto di idee. Quindi, la mia domanda è:Come scorrere un fd_set

Come faccio a scorrere un fd_set? Se questa è una pratica veramente brutta, ci sono altri modi per risolvere il mio "problema" eccetto il looping di tutti i socket connessi?

Grazie

+0

Per sottolineare quello che voglio dire. Non voglio utilizzare l'approccio FD_ISSET poiché richiede di eseguire il ciclo di tutte le prese collegate. Ma poiché, per definizione, select() rimuove i descrittori di file non rilevanti dal set, voglio fare un ciclo attraverso il set. – Andreas

+6

Non significa necessariamente "tutti connessi". È possibile passare un sottoinsieme dei socket connessi per selezionare e quindi utilizzare FD_ISSET solo su quel sottoinsieme dopo la selezione dei resi. Inoltre, c'è un problema reale con il loop su tutti loro? A meno che non abbiate a che fare con molte migliaia di socket connessi, il ciclo richiederà probabilmente un tempo irrilevante. – Rakis

+1

D'accordo con Rakis. Questa è una di quelle cose che sembrano inefficienti, ma nella maggior parte dei casi non è proprio vero. Il tempo di attraversare il ciclo sarà sminuito dal tempo necessario per servire solo uno degli FD impostati. – Duck

risposta

5

Select imposta la bit corrispondente al descrittore di file nel set, quindi, non è necessario eseguire iterazioni su tutti i file fds se si è interessati solo a pochi (e si possono ignorare altri) basta testare solo i descrittori di file per i quali si è interessati.

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 
} 

EDIT
Ecco la struct 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; 

Dove, fd_count è il numero di socket set (in modo, è possibile aggiungere un'ottimizzazione di utilizzare questo) e fd_array è un vettore bit (della dimensione FD_SETSIZE * sizeof (int) che dipende dalla macchina). Nella mia macchina, è 64 * 64 = 4096.

Quindi, la tua domanda è essenzialmente: qual è il modo più efficace per trovare le posizioni di bit di 1s in un vettore bit (di dimensioni intorno a 4096 bit)?

Voglio cancellare una cosa qui:
"looping attraverso tutti i socket connessi" non significa che stai effettivamente leggendo/facendo roba per una connessione. FD_ISSET() controlla solo il tempo in cui il bit in fd_set posizionato al numero assegnato del file_digriptor della connessione è impostato o meno. Se l'efficienza è il tuo obiettivo, allora non è questo il più efficiente? usando l'euristica?

Si prega di dirci cosa c'è di sbagliato con questo metodo, e che cosa stai cercando di raggiungere con il metodo alternativo.

+0

Grazie anche a te. Ma per favore vedi il mio commento, forse non stavo spiegando esplicitamente questo è l'approccio che non desidero prendere. – Andreas

+3

Se questa non è la risposta che [è corretta/vuoi], perché è stata contrassegnata come risposta? –

+0

Per due motivi. a) la modifica ha fornito le informazioni che stavo cercando b) ho cambiato idea e quindi la risposta è diventata rilevante. – Andreas

1

Vedere questa sezione 7.2 di Beej 's guida al networking -' 7.2. selezionare() - Multiplexing I/O sincrono 'usando FD_ISSET.

insomma, è necessario scorrere un fd_set al fine di determinare se il descrittore di file è pronto per la lettura/scrittura ...

+0

Grazie per la risposta. So che questo è l'approccio standard, tuttavia sto cercando di influenzarlo, per favore vedi il mio commento sul mio post. – Andreas

4

è abbastanza straight-forward:

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

Grazie per la risposta. Si prega di vedere il mio commento per chiarimenti su ciò che voglio fare. – Andreas

0

Non credo che quello che si sta cercando di fare è una buona idea.

In primo luogo il suo sistema di dipendente, ma credo che già lo sai.

secondo luogo, sul piano interno questi insiemi sono memorizzati come un array di interi e FDS sono memorizzati come bit impostati. Ora, secondo le pagine man di select, FD_SETSIZE è 1024. Anche se si desidera eseguire un'iterazione e ottenere i propri Fd interessati, è necessario eseguire il loop su quel numero insieme al pasticcio della manipolazione dei bit. Quindi, a meno si è in attesa per più di FD_SETSIZE fd di selezionare su quale io non la penso così è possibile, la sua non è una buona idea.

Oh aspettare !!. In ogni caso non è una buona idea.

10

dovete compilare uno struct fd_set prima di chiamare select(), non è possibile passare in std :: originale di prese direttamente. select() quindi modifica fd_set di conseguenza, rimuovendo eventuali socket che non sono "impostati" e restituisce quanti socket sono rimasti. Devi eseguire il ciclo attraverso il risultante fd_set, non il tuo std :: set. Non c'è bisogno di chiamare FD_ISSET() perché il fd_set risultante contiene solo "set" prese che sono pronti, ad esempio:

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

Dove FD_ISSET() entra in gioco il più delle volte è quando si utilizza il controllo degli errori con select() , ad esempio:

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]); 
    } 
} 
+0

+1, anche se, trovo alcuni 'aray' s nel tuo codice :) – Default

+0

Ho corretto gli errori di battitura –

+0

La [seleziona manpage] (http://linux.die.net/man/2/select) dice:' nfds è il descrittore di file con il numero più alto in uno qualsiasi dei tre gruppi, più 1. 'Usa * il numero più alto *, non il * numero *! – MaPePeR

3

Questo loop è una limitazione dell'interfaccia select(). Le implementazioni sottostanti di fd_set sono di solito un bit impostato, il che significa ovviamente che la ricerca di un socket richiede la scansione dei bit.

E 'proprio per questo motivo che diverse interfacce alternative sono state create - purtroppo, sono tutti specifici del sistema operativo. Ad esempio, Linux fornisce epoll, che restituisce un elenco di soli descrittori di file che sono attivi. Sia FreeBSD che Mac OS X forniscono entrambi kqueue, che ottiene lo stesso risultato.

+0

Questa dovrebbe essere la risposta accettata. – Agis

0

non credo che si potrebbe fare molto utilizzando in modo efficiente la chiamata select(). Le informazioni su "The C10K problem" sono ancora valide.

Avrete bisogno di alcune soluzioni specifiche di piattaforma:

Oppure si potrebbe usare una libreria evento per nascondere dettaglio piattaforma per voi libev