Поиск…


Вступление

IO может быть блокирующим / неблокирующим и синхронным / асинхронным. POSIX API обеспечивает синхронный блокирующий API (например, классические запросы чтения, записи, отправки, recv), синхронный неблокирующий API (те же функции, файловые дескрипторы, открытые с O_NONBLOCK флагов O_NONBLOCK и вызовы IO-мультиплексирования) и асинхронный API (функции, начинающиеся с aio_ ).

Синхронный API обычно используется со стилем «один поток / процесс в fd». Это ужасно для ресурсов. Неблокирующий API позволяет работать с набором fds в одном потоке.

Опрос

В этом примере мы создаем пару подключенных сокетов и отправляем 4 строки из одного в другой и печатаем полученные строки для консоли. Обратите внимание, что количество раз, которое мы будем называть send, может быть не равным числу раз, которое мы вызываем 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;
}

Выбрать

Выбор - это еще один способ мультиплексирования ввода-вывода. Одним из его преимуществ является существование в API winsock. Кроме того, в Linux, select () изменяет таймаут, чтобы отразить количество времени, которое не спалось; большинство других реализаций этого не делают. (POSIX.1 разрешает либо поведение.)

Оба опроса и выбора имеют альтернативы ppoll и pselect, которые позволяют обрабатывать входящие сигналы во время ожидания события. И оба из них становятся медленными с огромным количеством дескрипторов файлов (сто и более), поэтому было бы разумно выбрать конкретный вызов платформы, например epoll на Linux и kqueue на FreeBSD. Или переключитесь на асинхронный API (например, POSIX aio или что-то конкретное, например, IO Completion Ports).

Выбор вызова имеет следующий прототип:

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

fd_set - это fd_set массив дескрипторов файлов,

nfds - максимальное количество всех дескрипторов файлов в наборе + 1.

Фрагмент работы с выбором:

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

Обратите внимание, что на большинстве POSIX-пресетов файловые дескрипторы, связанные с файлами на диске, блокируются. Поэтому запись в файл, даже если этот файл был установлен в writefds , блокируется до тех пор, пока все байты не будут сброшены на диск



Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow