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

&pagelevel(3)&pagelevel

Oft ist es sinnvoll, Ein- und Ausgaben über mehrere Sockets zu verteilen. Für diese Art des Multiplexens der Ein-/Ausgabe verwenden Sie die Funktionen select() oder poll(). Mit diesen Funktionen kann ein Programm mehrere Verbindungen gleichzeitig überwachen.

Der folgende Programmausschnitt skizziert die Verwendung von select().

#include <sys/time.h>
#include <sys/types.h>
#include <sys/select.h>
 ...
fd_set readmask, writemask, exceptmask;
struct timeval timeout;
 ...
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 nfds spezifiziert, wieviele 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) kein Interesse haben, übergeben Sie beim select()-Aufruf als entsprechenden Parameter den Null-Zeiger.

Die Bitmasken, welche die Deskriptormengen repräsentieren, werden als Bitfelder in Integer-Reihungen abgespeichert. Die Größe der Bitfelder ist durch die Konstante FD_SETSIZE festgelegt. FD_SETSIZE ist in <sys/select.h> mit einem Wert definiert, der mindestens so groß ist wie die maximale Anzahl der vom System unterstützten Deskriptoren.

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

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,

  • bei welchen Deskriptoren noch nicht ausgewertete Ausnahmebedingungen vorliegen.

Wenn die Ausführung von select() wegen Timeout beendet wird, liefert select() den Wert 0 zurück. Die Bitmasken können jedoch bereits verändert sein.

Wenn select() wegen eines Fehlers beendet wird, liefert select() den Wert -1 zurück mit dem entsprechenden Fehler-Code in errno. Die Bitmasken sind dann unverändert.

Nach der Ausführung von select() können Sie mit dem Makro-Aufruf FD_ISSET(fd, &xyzmask) den Status eines Deskriptors fd überprüfen. 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, &readmask) 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 dem folgenden Programmcode kann ein beliebiger Prozess Daten von zwei Sockets lesen. Der Timeout-Wert beträgt fünf Sekunden. Das Beispielprogramm ist nur für die Kommunikationsdomäne AF_INET gültig. Wenn es gemäß den Angaben in "Socket- Adressierung" und "Socket erzeugen" geändert wird, gilt es auch für die Domäne AF_INET6.

#include <sys/select.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#define TRUE 1
#define TESTPORT 2222
int main(int argc, char **argv)
{
    int sock, length;
    struct sockaddr_in server;
    int msgsock;
    char buf[1024];
    int rval;
    fd_set ready;
    struct timeval to;
    /* 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);
    }
    /* Zugehörige Portnummer herausfinden und ausgeben */
    length = sizeof server;
    if (getsockname(sock, (struct sockaddr *)&server, &length) < 0) {
        perror("getting socket name");
        exit(1);
    }
    printf("Socket port #%d\n", ntohs(server.sin_port));
    /* Beginn der Annahme von Verbindungen. */
    listen(sock, 5);
    do {
        FD_ZERO(&ready);
        FD_SET(sock, &ready);
        to.tv_sec = 5;
        to.tv_usec=0;
        if (select(sock + 1, &ready, (fd_set *)0, (fd_set *)0, &to) < 0) {
            perror("select");
            continue;
        }
        if (FD_ISSET(sock, &ready)) {
            msgsock = accept(sock, (struct sockaddr *)0, (int *)0);
            if (msgsock == -1) {
                perror("accept");
            } else do {
                memset(buf, 0, sizeof buf);
                if ((rval = read(msgsock, buf, 1024)) < 0) {
                    perror("reading stream message");
                } else if (rval == 0) {
                    printf("Ending connection\n");
                } else {
                    printf("-->%s\n", buf);
                }
            } while (rval > 0);
            close(msgsock);
        } else {
            printf("Do something else\n");
        }
    } while (TRUE);
    exit(0);
}