У меня очень стереотипная 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 080 *
Могу ли я полностью исключить любую возможность этого , если я контролирую и клиент, и сервер?
Заранее спасибо!
#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);
}
}
}
}
}
}