2010-09-07 12 views
16

Ich frage mich, ob es eine einfache Möglichkeit gibt, ein fd_set zu durchlaufen? Der Grund dafür ist, dass ich nicht alle angeschlossenen Sockets durchlaufen muss, da select() diese fd_sets so ändert, dass nur diejenigen eingeschlossen sind, an denen ich interessiert bin. Ich weiß auch, dass die Verwendung einer Implementierung eines Typs, auf den nicht direkt zugegriffen werden soll, im Allgemeinen eine schlechte Idee ist, da sie über verschiedene Systeme hinweg variieren kann. Ich brauche jedoch eine Möglichkeit, dies zu tun, und mir gehen die Ideen aus. Also, meine Frage ist:Wie man durch ein fd_set iteriert

Wie iterate ich durch ein fd_set? Wenn das eine wirklich schlechte Übung ist, gibt es andere Möglichkeiten, mein "Problem" zu lösen, außer das Durchschleifen aller angeschlossenen Sockets?

Dank

+0

Um zu betonen, was ich meine. Ich möchte den FD_ISSET-Ansatz nicht verwenden, da ich alle angeschlossenen Sockets durchlaufen muss. Da aber per definitionem select() nicht relevante Dateideskriptoren aus der Menge entfernt, möchte ich die Menge durchlaufen. – Andreas

+6

Es bedeutet nicht unbedingt "alle verbunden". Sie können eine Teilmenge Ihrer verbundenen Sockets übergeben, um FD_ISSET für diese Teilmenge auszuwählen und anschließend zu verwenden. Gibt es auch ein Problem mit der Schleife über alle von ihnen? Wenn Sie nicht mit vielen Tausenden verbundener Sockets zu tun haben, wird die Schleife wahrscheinlich eine unwichtige Zeit in Anspruch nehmen. – Rakis

+1

Stimmen Sie mit Rakis überein. Dies ist eines der Dinge, die ineffizient zu sein scheinen, aber in den meisten Fällen ist es wirklich nicht Die Zeit, um durch die Schleife zu gehen, wird von der Zeit, die es dauert, um nur eine der eingestellten FDs zu bedienen, in den Schatten gestellt. – Duck

Antwort

5

Select setzt die Bit, das dem Dateideskriptor in der Menge entspricht. Sie müssen also nicht alle fds durchlaufen, wenn Sie nur an wenigen interessiert sind (und andere ignorieren können), nur die Dateideskriptoren testen, für die Sie sich interessieren.

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
Hier ist die fd_set Struktur:

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

Wo, fd_count ist die Anzahl der Steckdosen gesetzt (so können Sie eine Optimierung mit diesem Add) und fd_array a Bit-Vektor (der Größe FD_SETSIZE * sizeof (int) , die maschinenabhängig ist). In meinem Rechner ist es 64 * 64 = 4096.

Also, Ihre Frage ist im Wesentlichen: Was ist der effizienteste Weg, um die Bit-Positionen von 1s in einem Bit-Vektor (der Größe um 4096 Bits) zu finden?

Ich möchte hier eine Sache löschen:
"Schleifen durch alle verbundenen Sockets" bedeutet nicht, dass Sie tatsächlich Dinge zu einer Verbindung lesen/tun. FD_ISSET() prüft nur, ob das Bit im fd_set, das an der zugewiesenen file_descriptor Nummer der Verbindung positioniert ist, gesetzt ist oder nicht. Wenn Effizienz Ihr Ziel ist, ist das nicht die effizienteste? Heuristik verwenden?

Bitte teilen Sie uns mit, was mit dieser Methode nicht in Ordnung ist und was Sie mit der alternativen Methode erreichen möchten.

+0

Danke auch dir. Aber bitte sehen Sie meinen Kommentar, vielleicht habe ich nicht explizit genug erklärt, das ist der Ansatz, den ich nicht machen möchte. – Andreas

+3

Wenn dies nicht die Antwort ist, die [ist richtig/du willst], warum wurde es als Antwort markiert? –

+0

Aus zwei Gründen. a) Die Bearbeitung lieferte die Informationen, nach denen ich suchte. b) Ich habe meine Meinung geändert und daher wurde die Antwort relevant. – Andreas

1

Siehe diesen Abschnitt 7.2 von Beej 's Anleitung zur Vernetzung -' 7.2. Wählen Sie() - Synchronous I/O Multiplexing 'mit FD_ISSET.

kurz gesagt, müssen Sie eine fd_set, um durchlaufen, um zu bestimmen, ob der Dateideskriptor zum Lesen/Schreiben bereit ist ...

+0

Danke für die Antwort. Ich weiß, dass dies der Standardansatz ist, aber ich möchte mich davon abhalten, siehe meinen Kommentar zu meinem eigenen Beitrag. – Andreas

4

Es ist ziemlich geradlinig:

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

Danke für die Antwort. Bitte sehen Sie sich meinen Kommentar an, um zu erklären, was ich tun möchte. – Andreas

0

Ich glaube nicht, was Sie versuchen, ist eine gute Idee.

Erstens ist das System abhängig, aber ich glaube, Sie wissen es bereits.

Zweitens sind diese Sätze auf der internen Ebene als ein Array von ganzen Zahlen gespeichert und fds werden als gesetzte Bits gespeichert. Jetzt nach den Man-Seiten von wählen Sie die FD_SETSIZE ist 1024. Selbst wenn Sie über iterieren wollten und Ihre interessierten fd erhalten, müssen Sie über diese Nummer zusammen mit dem Chaos der Bit-Manipulation. Also, es sei denn, Sie warten auf mehr als FD_SETSIZE fd's auf wählen, was ich nicht denke, so ist möglich, es ist keine gute Idee.

Oh warte !!. Auf jeden Fall ist es keine gute Idee.

10

Sie müssen eine fd_set-Struktur vor dem Aufruf von select() ausfüllen, Sie können Ihre ursprünglichen std :: sets nicht direkt übergeben. select() ändert dann das fd_set entsprechend, entfernt alle Sockets, die nicht "gesetzt" sind, und gibt an, wie viele Sockets noch vorhanden sind. Sie müssen den resultierenden fd_set durchlaufen, nicht Ihren std :: set. Es besteht keine Notwendigkeit FD_ISSET() aufrufen, da die resultierende fd_set enthält nur Steckdosen „Set“, die bereit sind, zum Beispiel:

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

Wo FD_ISSET() ins Spiel kommt häufiger ist, wenn Fehler mit mit ausgewählten Überprüfung() zB:

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, obwohl, finde ich einige 'aray's in Ihrem Code :) – Default

+0

Ich habe die Tippfehler behoben –

+0

Die [select manpage] (http://linux.die.net/man/2/select) sagt: 'nfds ist der Dateideskriptor mit der höchsten Nummer in jedem der drei Sätze plus 1. 'Benutze * die höchste Nummer *, nicht die * Anzahl *! – MaPePeR

3

Diese Schleife ist eine Einschränkung der select() Schnittstelle. Die zugrunde liegenden Implementierungen von fd_set sind normalerweise ein bisschen gesetzt, was offensichtlich bedeutet, dass die Suche nach einem Socket ein Scannen über die Bits erfordert.

Aus genau diesem Grund wurden mehrere alternative Schnittstellen erstellt - leider sind sie alle OS-spezifisch. Beispielsweise stellt Linux epoll bereit, das eine Liste nur der Dateideskriptoren zurückgibt, die aktiv sind. FreeBSD und Mac OS X bieten beide , was das gleiche Ergebnis liefert.

+0

Dies sollte die akzeptierte Antwort sein. – Agis

0

Ich glaube nicht, dass Sie mit dem select() Aufruf effizient arbeiten könnten. Die Informationen unter "The C10K problem" sind weiterhin gültig.

Sie werden einige plattformspezifische Lösungen benötigen:

Oder Sie könnten ein Event-Bibliothek verwenden Sie die Plattform Detail zu verstecken libev