LINUX.ORG.RU

poll после send

 ,


0

2

Всем привет. Ткните носом, хочу разобраться. Есть маленький клиент на си, соединяющийся по локальному сокету с сервером. Хочется, чтобы клиент дожидался ответа от сервера, что данные получены или отваливался по таймауту. Набросал небольшой код:

int poll_ret;
struct pollfd fds;
 
    if (send(s, send_data, sizeof(send_data->level)
       sizeof(send_data->cmd_id), 0) < 0) {
         fprintf(stderr, "Cannot send to %s: %s.\n",LOCAL_SOCK,
                                             strerror(errno));
                          close(s);
                          return FAILED;
    }

    fds.fd = s;
    fds.events |= POLLIN;
    poll_ret = poll(&fds, 1, 5000);
 
    fprintf(stderr, "poll_ret = %d\n", poll_ret);
 
    if (poll_ret < 0) {
          perror("poll");
    } else if (poll_ret == 0) {
          fprintf(stderr, "End of data\n");
    } else {
         ret = read(fds.fd, buf, 100);
         fprintf(stderr, "Data: %s\n", buf);
    }
почти всегда poll возвращает 1 и сразу же все идет дальше, а нужно чтобы ждал данные, пару раз было что работает как задумывалось, т.е. отваливался по таймауту. Может кто подскажет как лучше сделать? Сокет неблокирующий, сервер делаю так: sudo nc -l -U /run/lock.sock


fds.events |= POLLIN;

dafaq, молодой человек? fds.events = POLLIN. «|=» на неинициализированную переменную на стеке.

kawaii_neko ★★★★ ()

Ещё один потенциальный баг в коде

Сокет неблокирующий

Тогда тебе нужно send() вызывать в цикле с poll() и POLLOUT, так как он не обязан отправлять всё сообщение за раз и может вернуть количество отправленных данных меньшее, чем ты ему передал аргументом.

Насчёт read() не уверен (обычно для сокетов юзают recv()), но скорее всего он тоже может прочитать меньше, чем ты запросил.

Deleted ()
Последнее исправление: Deleted (всего исправлений: 1 )
Ответ на: Ещё один потенциальный баг в коде от Deleted

Хорошо. А как, к примеру send отправляет данные? К примеру, мы хотим отправить 100 байт, send вернул нам 60, отправились первые 60 байт, не с конца, не из середины, а именно первые? Т.е. потом мы можем просто сделать

   ssize_t ret = send(sock, buf + 60, len - 60, 0);

Так?

А с recv такая же ситуация?

Dennis7 ()
Последнее исправление: Dennis7 (всего исправлений: 1 )
Ответ на: комментарий от Dennis7

отправились первые 60 байт, не с конца, не из середины, а именно первые?

Отправились именно те 60 байт, которые лежат в памяти, начиная с адреса:

ssize_t ret = send(sock, buf, len, 0);
                         ^^^

Если используешь poll, зачем сокет неблокирующий? Ведь poll используется для того, чтобы иметь таймаут при использовании стандартного блокирующего сокета?

gag ★★★★★ ()
Ответ на: комментарий от gag

Если уже добавлять poll перед read, тогда сразу и сокет делать блокирующем.

Не надо советовать человеку плохое. «Сработавший» POLLOUT не гарантирует то, что операция записи на блокирующем дескрипторе не заблокирует поток, например если в неё передать очень много данных. Насколько я помню, с POLLIN и чтением тоже могут бать приколы, но в более сложных случаях: например, если с одного дескриптора идёт чтение в несколько потоков (если я ничего не напутал).

Вот вам цитата Линуса Торвальдса:

yeah, different file descriptor types have somewhat different semantics for «blocking» (with tty's being probably the most complex - think of all the timeout and «minimum character count» and line ending issues with termios).

So the exact detail end up being complicated, but the basic rule of «poll/select goes together with nonblocking IO» still holds. As you saw, mixing poll and blocking IO can «work» but usually has various issues.

It's made more subtle by the fact that poll actually generally tries to help applications be efficient, so if I recall correctly (on my phone right now, no source code) poll will not return writable until the write queues are «sufficiently' empty (something like half empty) so that applications that are in a poll/write loop don't get woken up for each packet that gets sent and acknowledged, but instead get bigger poll sleeps and bigger writes.

So details like that, along with timing etc, makes things much harder to predict. Blocking writes end up » almost working well" except when they don't.

With nonblocking IO, those subtleties all go away.

Deleted ()
Последнее исправление: Deleted (всего исправлений: 2 )
Ответ на: комментарий от Deleted

«Сработавший» POLLOUT не гарантирует то, что операция записи на блокирующем дескрипторе не заблокирует поток, например если в неё передать очень много данных.

А с неблокирующим write(много данных) просто вернёт, что принял не так много, не?

Да и хорошо, до отправления и смысла дальше идти нет. ТС желает:

Хочется, чтобы клиент дожидался ответа от сервера, что данные получены или отваливался по таймауту.

Мне кажется логичным, что таймаут должен пойти после того как клиент ему все данные отправил. Если оставить сокет неблокирующим, получится: кинули серверу уйму данных, и пока мы ему ещё не всё отправили, уже начали считать ему время на ответ.

Так ведь и сами посоветовали:

Тогда тебе нужно send() вызывать в цикле с poll() и POLLOUT, так как он не обязан отправлять всё сообщение за раз и может вернуть количество отправленных данных меньшее, чем ты ему передал аргументом.

Т.е. вместо одного просто блокирующего вызова, получаем его эмуляцию.

gag ★★★★★ ()
Ответ на: комментарий от gag

А с неблокирующим write(много данных) просто вернёт, что принял не так много, не?

Да, вернёт сколько фактически удалось записать/отправить.

Да и хорошо, до отправления и смысла дальше идти нет.

Идти дальше смысл есть практически всегда, когда файловых дескрипторов (не обязательно сокетов) больше одного. Исключение - когда работа ведётся с файлами.

Т.е. вместо одного просто блокирующего вызова, получаем его эмуляцию.

Это верно только для случаев, когда дескриптор в poll() передаётся всего один.

Мне не известно, ТС показал полный код или же только минимальный пример, на котором воспроизводится проблема. Общее правило, позволяющее избежать мутных проблем, включая дедлоки на ровном месте, примерно такое: если используешь select()/poll()/etc., передавай ему только неблокирующие дескрипторы. Не хочешь или не можешь неблокирующие дескрипторы - не используй select()/poll()/etc.

Deleted ()
Ответ на: комментарий от gag

Ведь poll используется для того, чтобы иметь таймаут при использовании стандартного блокирующего сокета?

Кстати с реализацией таймаута на чтение с блокирующего сокета при помощи poll() тоже всё очень плохо. Если ждать POLLIN с таймаутом, а затем читать из сокета, например, 30 байт, то обязательно случится так, что сервер отправит 1 байт (что достаточно для получения POLLIN), а затем перестанет посылать что-либо. В особо суровом случае при этом будут летать TCP keepalive и соединение само никогда не отвалится.

Deleted ()
Ответ на: комментарий от gag

Тогда тебе нужно send() вызывать в цикле с poll() и POLLOUT, так как он не обязан отправлять всё сообщение за раз

Т.е. вместо одного просто блокирующего вызова, получаем его эмуляцию.

Лол, нет, не получаем, так как блокирующий I/O точно так же не гарантирует отправки всех данных.

Вы чего-то совсем дикие, как из джунглей. Вас в манах забанили или что?

LamerOk ★★★★★ ()
Последнее исправление: LamerOk (всего исправлений: 1 )
Ответ на: комментарий от Deleted

Мне не известно, ТС показал полный код или же только минимальный пример

Я исходил из того, что было показано и не рассматривал

когда файловых дескрипторов (не обязательно сокетов) больше одного.

если используешь select()/poll()/etc., передавай ему только неблокирующие дескрипторы.

Интересно. Я всегда пользовался правилом, что я в read() указываю просто размер буфера, а не сколько байт я требую. И соответственно, если сокет стал читабельным, то вот сколько там появилось, столько и будет сразу же возвращено read(). Почему бы и нет?

gag ★★★★★ ()
Ответ на: комментарий от LamerOk

Тогда тебе нужно send() вызывать в цикле с poll() и POLLOUT, так как он не обязан отправлять всё сообщение за раз

Т.е. вместо одного просто блокирующего вызова, получаем его эмуляцию.

Лол, нет, не получаем, так как блокирующий I/O точно так же не гарантирует отправки всех данных.

А я и не эмулирую отправку всех данных, а только эквивалентность цикл poll()/send()/nonblock и send()/block.

gag ★★★★★ ()
Ответ на: комментарий от LamerOk

Лол, нет, не получаем, так как блокирующий I/O точно так же не гарантирует отправки всех данных.

Судя по всему для разных типов дескриптора гарантии там разные...

Deleted ()
Ответ на: комментарий от gag

Почему бы и нет?

Потому что с блокирующими сокетами ты не сможешь надёжно реализовать отправку данных и получение данных одновременно с таймаутом.

Deleted ()
Ответ на: комментарий от Deleted

Что касается чтения, я не понимаю, почему Линус настаивает на том, что указывая размер буфера для чтения, мы «заставляем» read() ждать заполнения этого буфера. Ведь для блокирующего сокета логично: select()/poll() -> данные пришли -> read() столько сколько пришло, но максимум размер буфера.

А что касается блокирующего write(), то я ожидаю такое поведение: select()/poll() -> можно записывать -> write(много данных) -> write() записывает столько, насколько select()/poll() нашли места и возвращает сколько удалось записать. И если данных было больше, чем размера в ядерном буфере, то повторяем.

Т.е. после успешных poll()/select() следующая операция read()/write() должна пройти без блокировки (но для записи не обязательно для всего размера буфера). Иначе в них, пожалуй, нет смысла.

gag ★★★★★ ()
Ответ на: комментарий от Deleted

А с таймаутом вообще вопрос: чего ждём, если не знаем ни сколько сервер получил, ни даже, сколько мы ему вообще отправили.

gag ★★★★★ ()
Ответ на: комментарий от Deleted

Нет там никаких гарантий:

When the message does not fit into the send buffer of the socket, send() normally blocks

RETURN VALUE On success, these calls return the number of bytes sent.

LamerOk ★★★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.