Your Browser is not longer supported

Please use Google Chrome, Mozilla Firefox or Microsoft Edge to view the page correctly
Loading...

{{viewport.spaceProperty.prod}}

Ein-/Ausgabe-Multiplexen mit der Funktion select()

&pagelevel(4)&pagelevel

Mit select() kann ein Programm mehrere Verbindungen gleichzeitig überwachen.

Der folgende Programmausschnitt skizziert die Verwendung von select():

#include <sys.time.h>
#include <sys.types.h>
...
char *readmask, *writemask, *exceptmask; 
struct timeval timeout;
int nfds;
 ...
select(nfds, readmask, writemask, exceptmask, &timeout);

Der Aufruf von select() benötigt als Parameter drei Zeiger auf jeweils eine Bitmaske, die eine Menge von Socket-Deskriptoren repräsentiert:

  • Anhand der mit readmask übergebenen Bitmaske prüft select(), von welchen Sockets Daten gelesen werden können.

  • Anhand der mit writemask übergebenen Bitmaske prüft select(), auf welche Sockets Daten geschrieben werden können.

  • Anhand der mit exceptmask übergebenen Bitmaske prüft select(), für welche Sockets eine noch nicht ausgewertete Ausnahmebedingung vorliegt.
    Der Parameter exceptmask wird derzeit von SOCKETS(BS2000) nicht ausgewertet.

Die Bitmasken zu den einzelnen Deskriptormengen werden als Bitfelder in Integer-Reihungen abgespeichert. Die maximal notwendige Größe der Bitfelder ermitteln Sie mit der Funktion getdtablesize() (siehe Abschnitt "getdtablesize() - Größe der Deskriptortabelle abfragen"). Der benötigte Speicher sollte dynamisch vom System angefordert werden.

Der Parameter nfds spezifiziert, wie viele Bits bzw. Deskriptoren überprüft werden sollen: select() überprüft in jeder Bitmaske die Bits 0 bis nfds-1.
Wenn Sie an einer der Informationen (Lesen, Schreiben oder noch nicht ausgewertete Ausnahmebedingungen) nicht interessiert sind, übergeben Sie beim select()-Aufruf als entsprechenden Parameter den Null-Zeiger.

Mit Makros bearbeiten Sie die Bitmasken. Insbesondere sollten Sie die Bitmasken vor der Bearbeitung auf 0 setzen. Die Makros zur Manipulation von Bitmasken sind erläutert im Abschnitt "select() - Ein-/Ausgabe multiplexen" bei der Funktionsbeschreibung von select().

Mit dem Parameter timeout legen Sie einen Timeout-Wert fest, wenn der Auswahlvorgang nicht länger als eine vorbestimmte Zeit dauern soll. Wenn Sie mit timeout den Null-Zeiger übergeben, blockiert die Ausführung von select() auf unbestimmte Zeit.
Ein zyklisches Auswahlverhalten (Polling) veranlassen Sie, wenn Sie für timeout einen Zeiger auf eine timeval-Variable übergeben, deren Komponenten sämtlich auf den Wert 0 gesetzt sind.

Bei erfolgreicher Ausführung spezifiziert der Rückgabewert von select() die Anzahl der selektierten Deskriptoren. Die Bitmasken zeigen dann an,

  • welche Deskriptoren zum Lesen bereit sind,

  • welche Deskriptoren zum Schreiben bereit sind.

Wenn die Ausführung von select() wegen Timeout beendet wird, liefert select() den Wert 0 zurück. Die Bitmasken sind dann undefiniert.
Wenn select() auf Grund eines Fehlers beendet wird, liefert select() den Wert „-1“ zurück mit dem entsprechenden Fehler-Code in errno. Die Bitmasken sind dann ebenfalls undefiniert.

Nach der erfolgreichen Ausführung von select() überprüfen Sie mit dem Makro-Aufruf FD_ISSET(fd, &mask) den Status eines Deskriptors fd. Der Makro liefert einen Wert ungleich 0, wenn fd ein Element der Bitmaske mask ist, andernfalls den Wert 0.

Ob auf einem Socket fd Verbindungsanforderungen auf ihre Annahme durch accept() warten, überprüfen Sie anhand der Lesebereitschaft des Sockets fd. Zu diesem Zweck rufen Sie select() und anschließend den Makro FD_ISSET (fd, &mask) auf. Liefert FD_ISSET einen Wert ungleich 0, so signalisiert dies die Lesebereitschaft des Sockets fd. Am Socket fd steht also eine Verbindungsanforderung an.

  
Beispiel: Verwenden von select() zum Überprüfen auf anstehende Verbindungsanforderungen

Mit folgendem Programmcode (für AF_INET) wird auf eine Verbindungsaufbauanforderung gewartet. Wenn sie eintrifft, wird sie angenommen, und das Programm wird beendet.

#include <stdlib.h>
#include <sys.types.h>
#include <sys.socket.h>
#include <sys.time.h>
#include <netinet.in.h>
#include <netdb.h>
#include <stdio.h>
#define TRUE 1
#define TESTPORT 5555
/*
* Dieses Programm überprüft mit select(), ob jemand versucht, eine
* Verbindung aufzubauen, und ruft dann accept() auf.
*/
main()
{
    int sock;
    struct sockaddr_in server;
    struct sockaddr_in client;
    int clientlen;
    int msgsock;
    int fdsize;
    char * ready;
    struct timeval to;
    memset(&server,'\0',sizeof(server));
    memset(&client,'\0',sizeof(client));
  
    clientlen = sizeof(client);
    /* Speicher für Testen der Socket-Deskriptoren durch soc_select()
    anfordern */
    if ((fdsize = getdtablesize()) < 0) {
             perror("get fd_size");
             exit(1);
       }
     if ((ready = memalloc(fdsize/8)) == NULL) {
             perror("no memory space");
             exit(1);
       }
    /* Socket erzeugen. */
     sock = socket(AF_INET, SOCK_STREAM, 0);
     if (sock < 0) {
            perror("opening stream socket");
            exit(1);
       }
/* Dem Socket unter Verwendung von Wildcards einen Namen zuordnen */
 server.sin_family = AF_INET;
 server.sin_addr.s_addr = htonl(INADDR_ANY);
 server.sin_port = htons(TESTPORT);
 if (bind(sock, (struct sockaddr *)&server, sizeof server) < 0) {
         perror("binding stream socket");
         exit(1);
   }
/* Beginn der Annahme von Verbindungen. */
 listen(sock, 5);
 do {
     memset(ready, 0, fdsize/8);
     FD_SET(sock, (fd_set *)ready);
     to.tv_sec = 5;
     to.tv_usec=0;
     if (select(sock + 1, (fd_set *)ready, (fd_set *)0, (fd_set *)0, &to) < 0) 
{
             perror("select");
         continue;
       }
     if (FD_ISSET(sock, (fd_set *)ready)) {
         msgsock = accept(sock, (struct sockaddr *)&client, &clientlen);
         if (msgsock >= 0)
           {
            /* Erfolgreiche Annahme des Verbindungsaufbauwunsches */
            /* Folgeverarbeitung der Daten, die ueber diese Verbindung */
            /* transferiert werden                                     */
            printf("Programmende nach erfolgreichem Verbindungsaufbau\n");
            break;
           }
         else
           {
            /* Es ist ein Fehler aufgetreten */
            /* Fehlermeldung und eventuell erneutes Warten auf einen   */
            /* Verbindungsaufbauwunsch                                 */
            printf("Programmende: Beim Verbindungsaufbau ist ein Fehler 
aufgetreten\n");
            break;
           }
       }
    } while (TRUE);
 exit(0);
}