Условия тайм-аута доменного сокета / гонки, связанные с write () + close (); и (возможно) длина un-read () данных - PullRequest
0 голосов
/ 04 августа 2020

У меня очень стереотипная C программа, которая прослушивает сокет домена и выполняет двунаправленный ввод-вывод на x86_64 Linux.

При реализации некоторых логических схем отключения при ошибке c, я обнаружил, что иногда , данные, отправленные непосредственно перед close();, периодически вообще не видны на другом конце, хотя strace на отправляющем конце показывал write(x, "blah") = 4, ie, что а) ошибок не было и б) что вся длина данных явно была отправлена.

возился с тестовой программой, я обнаружил, что количество данных un- read(); сервером (?!), похоже, влияет на кажущийся идеальным write(); close();.

В частности, если I read() 8 байт на сервере, то отправьте 8 байтов через nc ...

$ while true; do echo -n '==['; echo aaaaaaaa | nc.openbsd -U /tmp/udstest; echo ']=='; done
==[]==
==[]==
==[]==
==[]==
==[]==
==[hi!]==
==[hi!]==
==[hi!]==
==[]==
==[hi!]==
==[]==
==[]==
==[]==
==[]==
...

Тогда как если я просто удаляю один байт :

$ while true; do echo -n '==['; echo aaaaaaa | nc.openbsd -U /tmp/udstest; echo ']=='; done
==[hi!]==
==[hi!]==
==[hi!]==
==[hi!]==
==[hi!]==
...

, я случайно использую утилиты, подобные netcat, как тестировать клиентов, пока я завершаю sh сервер (который использует текстовый протокол), cally nc.openbsd с опцией -U (сокет домена).

Однако ncat не дает такого же поведения - кажется, что сообщения не отбрасываются. socat также не отбрасывает сообщения, но если добавить -t0 (установить все таймауты на 0), socat также отбрасывает сообщения.

(idiomati c , не специальный) сервер сокетов, к которому я подключался в приведенных выше примерах, включен ниже.

Похоже, я столкнулся с чем-то, что зависит от

  • Время - это происходит не каждый раз

  • Длина данных, отправляемых от клиента к серверу - если сервер read() s 8 байтов, а сервер отправляет 8 или больше байт

  • Конфигурация тайм-аута в клиенте (я слишком напуган, чтобы исследовать)

Затем иногда сообщения могут быть отброшены.

Мои вопросы:

  1. Во что я здесь врезаюсь?

  2. Это возможно ли обнаружить «сломанный» клиент с точки зрения сервера?

  3. Могу ли я устранить неисправность клиента с контролируемого мной сервера? * 1 080 *

  4. Могу ли я полностью исключить любую возможность этого , если я контролирую и клиент, и сервер?

Заранее спасибо!

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/un.h>
#include <arpa/inet.h>

#define SOCKET_PATH "/tmp/udstest"

int setup_listen() {
    int listen_fd = socket(AF_LOCAL, SOCK_STREAM, 0);
    if (listen_fd < 0) {
        perror("socket");
        exit(1);
    }
    struct sockaddr_un listen_addr;
    socklen_t listen_addr_len = sizeof(listen_addr);
    memset(&listen_addr.sun_path, 0, listen_addr_len);
    listen_addr.sun_family = AF_UNIX;
    strcpy(listen_addr.sun_path, SOCKET_PATH);
    if (bind(listen_fd, (struct sockaddr *)&listen_addr, sizeof(listen_addr)) < 0) {
        perror("bind");
        exit(1);
    }
    if (listen(listen_fd, 64) < 0) {
        perror("listen");
        exit(1);
    }
    return listen_fd;
}

int main() {
    signal(SIGPIPE, SIG_IGN);
    fd_set conn_fdset;
    FD_ZERO(&conn_fdset);
    int listen_fd = setup_listen();
    FD_SET(listen_fd, &conn_fdset);
    for (;;) {
        fd_set select_fdset = conn_fdset;
        int c = select(FD_SETSIZE, &select_fdset, NULL, NULL, NULL);
        if (c < 0) {
            perror("select");
        } else {
            for (uint16_t fdno = 0; fdno < FD_SETSIZE; ++fdno) {
                if (FD_ISSET(fdno, &select_fdset)) {
                    if (fdno == listen_fd) {
                        int new_conn_fd = accept(listen_fd, NULL, NULL);
                        if (new_conn_fd < 0) {
                            perror("accept");
                            exit(1);
                        }
                        FD_SET(new_conn_fd, &conn_fdset);
                    } else {
                        char x[8];
                        int r = read(fdno, x, 8);
                        if (r == -1) perror("read");
                        int w = write(fdno, "hi!", 3);
                        if (w < 0) perror("write");
                        FD_CLR(fdno, &conn_fdset);
                        close(fdno);
                    }
                }
            }
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...