Suche…


Einführung

E / A kann blockierend / nicht blockierend und synchron / asynchron sein. Die POSIX-API bietet eine synchrone Blockierungs-API (z. B. klassische Lese-, Schreib-, Sende-, Recv-Aufrufe), eine synchrone, nicht blockierende API (gleiche Funktionen, mit O_NONBLOCK Flag geöffnete O_NONBLOCK und IO-Multiplexing-Aufrufe) und eine asynchrone API (Funktionen, die mit aio_ ).

Eine synchrone API wird normalerweise mit einem "ein Thread / Prozess pro Fd" -Stil verwendet. Das ist schrecklich für Ressourcen. Die nicht blockierende API ermöglicht den Betrieb mit einem Satz FDS in einem Thread.

Umfrage

In diesem Beispiel erstellen wir ein Paar verbundene Sockets und senden 4 Strings von einem zum anderen und drucken die empfangenen Strings an die Konsole. Beachten Sie, dass die Anzahl der Aufrufe von send möglicherweise nicht gleich der Anzahl der Aufrufe von recv ist

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <poll.h>

#define BUFSIZE 512

int main()
{    
    #define CKERR(msg) {if(ret < 0) { perror(msg); \
        close(sockp[0]); close(sockp[1]); exit(EXIT_FAILURE); } }
    const char* strs_to_write[] = {"hello ", "from ", "other ", "side "};
    
    int sockp[2] = {-1, -1};
    ssize_t ret = socketpair (AF_UNIX, SOCK_STREAM, 0, sockp);
    CKERR("Socket pair creation error")
    
    struct pollfd pfds[2];
    for(int i=0; i<2; ++i) {
        pfds[i] = (struct pollfd){sockp[i], POLLIN|POLLOUT, 0};
        fcntl(sockp[i], F_SETFL|O_NONBLOCK); // nonblocking fds are
                // literally mandatory for IO multiplexing; non-portable
    }
    char buf[BUFSIZE];
    
    size_t snt = 0, msgs = sizeof(strs_to_write)/sizeof(char*);
    while(1) {
        int ret = poll(pfds,
            2 /*length of pollfd array*/,
            5 /*milliseconds to wait*/);
        CKERR("Poll error")

        if (pfds[0].revents & POLLOUT && snt < msgs) {
            // Checking POLLOUT before writing to ensure there is space
            // available in socket's kernel buffer to write, otherwise we
            // may face EWOULDBLOCK / EAGAIN error
            ssize_t ret = send(sockp[0], strs_to_write[snt], strlen(strs_to_write[snt]), 0);
            if(++snt >= msgs)
                close(sockp[0]);
            CKERR("send error")
            if (ret == 0) {
                puts("Connection closed");
                break;
            }
            if (ret > 0) {
                // assuming that all bytes were written
                // if ret != %sent bytes number%, send other bytes later
            }
        }
        if (pfds[1].revents & POLLIN) {
            // There is something to read
            ssize_t ret = recv(sockp[1], buf, BUFSIZE, 0);
            CKERR("receive error")
            if (ret == 0) {
                puts("Connection closed");
                break;
            }
            if (ret > 0) {
                printf("received str: %.*s\n", (int)ret, buf);
            }
        }

    }
    close(sockp[1]);
    return EXIT_SUCCESS;
}

Wählen

Wählen Sie eine andere Möglichkeit, E / A-Multiplexing durchzuführen. Ein Vorteil ist die Existenz einer Winsock-API. Unter Linux ändert select () außerdem das Zeitlimit, um die Zeit anzuzeigen, die nicht geschlafen wurde. Die meisten anderen Implementierungen machen dies nicht. (POSIX.1 erlaubt beide Verhaltensweisen.)

Sowohl poll als auch select haben ppoll- und pselect-Alternativen, mit denen eingehende Signale während des Wartens auf ein Ereignis behandelt werden können. Und beide mit riesiger Menge an Filedeskriptoren langsam werden (hundert und mehr), so wäre es klug , plattformspezifische Anruf, zum Beispiel wählen epoll auf Linux und kqueue auf FreeBSD. Oder wechseln Sie asynchronen API (POSIX aio zB oder etwas Bestimmtes wie IO Completion Ports).

Anruf auswählen hat den folgenden Prototyp:

int select(int nfds, fd_set *readfds, fd_set *writefds,
       fd_set *exceptfds, struct timeval *timeout);

fd_set ist ein Bitmasken-Array von Dateideskriptoren.

nfds ist die maximale Anzahl aller Dateideskriptoren in Satz + 1.

Ausschnitt der Arbeit mit select:

fd_set active_fd_set, read_fd_set;
FD_ZERO (&active_fd_set); // set fd_set to zeros
FD_SET (sock, &active_fd_set); // add sock to the set
// # define FD_SETSIZE sock + 1
while (1) {
    /* Block until input arrives on one or more active sockets. */
    read_fd_set = active_fd_set; // read_fd_set gets overriden each time
    if (select (FD_SETSIZE, &read_fd_set, NULL, NULL, NULL) < 0) {
        // handle error
    }
    // Service all file descriptors with input pending.
    for (i = 0; i < FD_SETSIZE; ++i) {
        if (FD_ISSET (i, &read_fd_set)) {
            // there is data for i
    }
}

Beachten Sie, dass bei den meisten POSIX-Implementierungen Dateibeschreibungen, die mit Dateien auf der Festplatte verknüpft sind, blockieren. Das Schreiben in eine Datei würde, selbst wenn diese Datei in writefds , blockieren, bis nicht alle Bytes auf die Festplatte geschrieben werden



Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow