Recherche…


Introduction

IO peut être bloquant / non bloquant et synchrone / asynchrone. L'API POSIX fournit une API de blocage synchrone (par exemple, appels classiques en lecture, écriture, envoi, recv), API synchrone non bloquante (mêmes fonctions, descripteurs de fichiers ouverts avec les appels O_NONBLOCK et IO-multiplexage) et API asynchrone (fonctions commençant par aio_ ).

L'API synchrone est généralement utilisée avec le style "un thread / processus par fd". C'est terrible pour les ressources. L'API non bloquante permet de fonctionner avec un ensemble de fds dans un thread.

Sondage

Dans cet exemple, nous créons une paire de sockets connectés et envoyons 4 chaînes de l'un à l'autre et imprimons les chaînes reçues sur la console. Notez que le nombre de fois que nous appellerons send peut ne pas être égal au nombre de fois que nous appelons recv

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

Sélectionner

Select est un autre moyen de faire du multiplexage d'E / S. L'un de ses avantages est l'existence d'une API WinSock. De plus, sous Linux, select () modifie le délai d’expiration pour refléter le temps écoulé; la plupart des autres implémentations ne le font pas. (POSIX.1 autorise les deux comportements)

Les deux sondages et sélections ont des alternatives ppoll et pselect, qui permettent de gérer les signaux entrants pendant l'attente d'un événement. Et les deux deviennent lents avec une quantité énorme de descripteurs de fichiers (cent et plus), il serait donc judicieux de choisir un appel spécifique à la plate-forme, par exemple epoll sur Linux et kqueue sur FreeBSD. Ou passer à une API asynchrone (POSIX aio par exemple, ou quelque chose de spécifique comme les ports IO Completion).

Sélectionnez l'appel a le prototype suivant:

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

fd_set est un tableau de descripteurs de fichiers fd_set ,

nfds est le nombre maximal de tous les descripteurs de fichiers de l'ensemble + 1.

Extrait de travail avec 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
    }
}

Notez que sur la plupart des implemetations POSIX, les descripteurs de fichiers associés aux fichiers sur le disque sont bloquants. Donc, écrire dans un fichier, même si ce fichier était défini dans writefds , bloquerait jusqu'à ce que tous les octets ne soient pas vidés sur le disque



Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow