Zoeken…


Invoering

IO kan blokkeren / niet-blokkeren en synchroon / asynchroon. POSIX API biedt synchrone blokkeer-API (bijv. Klassieke lees-, schrijf-, verzend-, recv-oproepen), synchrone niet-blokkerende API (dezelfde functies, bestandsdescriptors geopend met O_NONBLOCK vlag en IO-multiplexing-oproepen) en asynchrone API (functies beginnend met aio_ ).

Synchrone API wordt meestal gebruikt met de stijl "één thread / proces per fd". Dit is vreselijk voor middelen. Niet-blokkerende API maakt het mogelijk om met een set fds in één thread te werken.

poll

In dit voorbeeld maken we een paar verbonden sockets en sturen we 4 strings van de ene naar de andere en drukken ontvangen strings af naar de console. Houd er rekening mee dat het aantal keren dat we verzenden verzenden mogelijk niet gelijk is aan het aantal keren dat we recv bellen

#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;
}

kiezen

Selecteren is een andere manier om I / O-multiplexen te doen. Een van de voordelen is een bestaan in winsock API. Bovendien wijzigt select () op Linux de time-out om de hoeveelheid niet geslapen tijd weer te geven; de meeste andere implementaties doen dit niet. (POSIX.1 staat beide gedrag toe.)

Zowel poll als select hebben ppoll- en pselect-alternatieven, waarmee inkomende signalen kunnen worden verwerkt tijdens het wachten op een gebeurtenis. En beide worden traag met een enorme hoeveelheid bestandsdescriptors (honderd en meer), dus het zou verstandig zijn om platformspecifieke call te kiezen, bijv. epoll op Linux en kqueue op FreeBSD. Of schakel over naar asynchrone API (bijvoorbeeld POSIX aio of iets specifieks zoals IO-voltooiingspoorten).

Selecteer oproep heeft het volgende prototype:

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

fd_set is een bitmaskerreeks van bestandsdescriptors,

nfds is het maximale aantal van alle bestandsdescriptors in set + 1.

Fragment van het werken met 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
    }
}

Merk op dat op de meeste POSIX-implementaties bestandsdescriptors die zijn gekoppeld aan bestanden op schijf worden geblokkeerd. Dus schrijven naar bestand, zelfs als dit bestand in writefds was ingesteld, zou blokkeren totdat alle bytes niet naar schijf worden gedumpt



Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow