int select( int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
Die select()-Funktion wird meistens immer dann verwendet, wenn Sockets im nicht-blockierenden Modus arbeiten. Wenn beispielsweise von mehreren Sockets Daten empfangen werden sollen, würde die recv()-Funktion den Empfang von Daten anderen Socketsblockieren. Da auch die Tastatur als eine Datei mit dem Datei-Deskriptor vom Wert 1 betrachtet wird, würden auch Eingaben von der Tastatur blockiert werden, bis Daten vom Socket empfangen werden. Dies ist im speziellen dann fatal, wenn nur in sehr großen zeitlichen Abständen Daten von einem Socket empfangen werden. Auch im Allgemeinen ist dies nicht schön und sollte deshalb vermieden werden.
Mittels der select()-Funktion können Datei-Deskriptoren (und damit auch Sockets) überprüft werden, ob Daten für den Empfang bereitstehen. Darüber hinaus bietet es die select()- Funktion an, zu überprüfen, ob ein Socket bereit ist Daten zu senden bzw. ob ein unerwartetes Ereignis aufgetreten ist (z.B. ein Socket wurde unerwartet geschlossen (reset)).
Da der Aufruf der select()-Funktion nicht blockierend sein soll, wird mittels des Parameters timeout die Zeit angegeben, wann die Funktion beendet werden soll. Stehen z.B. innerhalb dieser Zeit Daten zum Empfang bereit, dann wird die select()- Funktion frühzeitig abgebrochen.
Separat für jede Gruppe von Operationen (empfangsbereit, sendebereit und Ausnahmen) existiert eine Menge, die alle Datei- Deskriptoren enthält, die beobachtet werden sollen. Z.B. sind in der Menge readfds alle Deskriptoren enthalten, bei denen beobachtet werden soll, ob Daten zum Empfang bereitstehen. In der Menge writefds und der Menge exceptfds sind analog alle Deskriptoren enthalten, bei deren darauf geachtet werden soll, ob Daten gesendet werden können oder ein Ausnahme aufgetreten ist.
Zur Manipulation der Mengen vom Typ fd_set werden die Funktionen (genauer gesagt Makros) bereit gestellt:
- void FD_ZERO(fd_set *set): Die Funktion FD_ZERO() löscht die als Parameter angegeben Menge set.
- void FD_SET(int fd, fd_set *set): Die Funktion FD_SET() fügt den Deskriptor fd in die Menge set ein.
- void FD_CLR(int fd, fd_set *set): Die Funktion FD_CLR() entfernt den Deskriptor fd aus der Menge set.
- int FD_ISSET(int fd, fd_set *set): Die Funktion FD_ISSET() überprüft, ob der Deskriptor fd in der Menge set enthalten ist. Ist der Deskriptor nicht in der Menge set enthalten, dann erhält der Rückgabewert vom Typ int Wert 0. Die select()-Funktion fügt einen Deskriptor in die Menge ein.
Sind die Mengen readfds, writefds und execptfds initialisiert kann die select()-Funktionen aufgerufen werden:
Im Parameter nfds ist die Nummer des größten Datei-Deskriptor + 1 aus den Mengen readfds, writefds und execptfds enthalten. Zur Erinnerung: Datei-Deskriptoren sind Zahlen vom Typ int.
In den Parametern readfds, writefds und execptfds sind alle Datei-Deskriptoren enthalten, die beobachtet werden sollen.
Als Rückgabewert liefert die select()-Funktion die Anzahl der Datei-Deskriptoren, bei denen sich eine Änderung ergeben hat.
Aus der Beschreibung der select()-Funktion ist zu erkennen, dass der aufrufende Prozess solange vertagt wird, bis sich etwas bei einem zu beobachtenden Datei-Deskriptor verändert hat. Um diese Wartezeit zu begrenzen wird der optionale Parameter *timeout vom Typ struct timeval auf einen Wert ungleich NULL gesetzt.
Mittels der Variablen timeout wird eine Zeit in Sekunden und Mikrosekunden angeben:
struct timeval{ int tv_sec; // bezeichnet Sekunden int tv_usec; // bezeichnet Mikrosekunden }
Tritt nach Ablauf dieser Zeit keine Veränderung bei einem Datei-Deskriptor auf, dann wird die select()-Funktion frühzeitig beendet. Der Prozess kann nun andere Aufgaben ausführen. Als Rückgabewert liefern die select()-Funktion wird in diesem Fall den Wert 0. In jedem anderen Fall, z.B. bei auftretenden Fehlern, liefert die Funktion den Wert -1.
Beispiel:
int fd; fd_set readset; int result; struct timeval tv; // Initialize the set FD_ZERO(&readset); FD_SET(fd, &readset); // Initialize time out struct tv.tv_sec = 1; tv.tv_usec = 0; // select() result =select(fd+1, &tempset, NULL, NULL,&tv); // Check status if (result < 0) return -1; else if (result > 0 && FD_ISSET(fd, &tempset)){ // receive result = recv(fd, buffer, len, flags); }