Сокеты: почему блокировка read () завершается с помощью ENOTCONN? - PullRequest
2 голосов
/ 16 мая 2011

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

Я также пытался убедиться, что сокет находится в режиме блокировки и что большой тайм-аут установлен с помощью:

int setBlockingIO(int fd)
{
       int flags = fcntl(fd, F_GETFL);
       fcntl(fd, F_SETFL, flags & (~O_NONBLOCK));
       int nTimeout = 30000; // 30 seconds
       setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&nTimeout, sizeof(int));
}

Но это ничего не изменило.

Мой вопрос:

  • Что я должен сделать, что read() будет really block?
  • Есть ли какие-нибудь подводные камни, на которые я могу попасть? (ошибка в моей программе?)

Я знаю, что есть другой вопрос по этой теме, но там я не могу найти ответ на свой вопрос.

UPDATE

Без установки таймаута read() также возвращает (субъективно) немедленно -1

ОБНОВЛЕНИЕ 2

errno равно 107 (ENOTCONN, Transport endpoint is not connected). Но клиентская сторона не закрыла соединение в это время (гарантируется длительным sleep() после write())

Ответы [ 6 ]

4 голосов
/ 16 мая 2011

Что вы ожидаете?Вы сказали, что у вас есть неблокирующий сокет, поэтому, конечно, он не блокируется.Поведение для неблокирующего сокета на read заключается в немедленном возврате с некоторыми данными (возможно, короче запрошенного объема), если данные доступны для чтения, и возврате -1 с errno, установленным на EAGAIN или EWOULDBLOCK если данные недоступны.

Если вам не нужно неблокирующее поведение, почему вы установили сокет неблокирующим?

Редактировать: Grr, вы изменили свой вопрос.Причина ENOTCONN в том, что вы пытаетесь читать из неподключенного сокета.Если сокет не был получен с помощью accept или socketpair, вы должны вызвать connect на нем, чтобы подключить его к удаленному адресу, прежде чем read будет работать.

3 голосов
/ 16 мая 2011

Это означает, что истекло время ожидания или, возможно, сигнал прервал чтение. Вы можете использовать результаты errno в errno.h, чтобы увидеть, что это за ошибка, и если вы хотите, чтобы ошибка была в удобочитаемом формате, вы можете использовать либо strerror() или perror() из string.h, либо stdlib.h

Обновление: В соответствии со спецификацией POSIX вы должны передать struct timeval (определенный в sys/time.h), установленный на желаемое количество секунд и микросекунд, прежде чем истечет время ожидания до setsockopt, когда указание флага SO_RCVTIMEO вместо преобразования int в const char*. Таким образом, даже если ваш клиент может вести себя неправильно и прямо сейчас вызывать другую ошибку, вы все равно можете столкнуться с проблемами в дальнейшем, если отправляете неправильные типы аргументов в функцию.

2 голосов
/ 16 мая 2011

Причина в ошибке. Вероятные сценарии:

  • [ECONNRESET] Аргумент d относится к сокету и удаленному конец гнезда принудительно закрыт.
  • [EAGAIN] Файл был помечен для неблокирующего ввода-вывода и отсутствия данных были готовы к прочтению.

По сути, вы делаете неблокирующую блокировку, используя:

do
{
    read(...);
} while(errno == EAGAIN);
1 голос
/ 16 мая 2011

Возвращает -1, когда истекает время ожидания.

Не устанавливайте тайм-аут, и он будет блокироваться навсегда (в любом случае, по крайней мере, пока сокет все еще подключен).

0 голосов
/ 16 мая 2011

read() может заблокировать, поэтому, если нет доступных данных, он вернет -1 и установит errno в EAGAIN или EWOULDBLOCK.

Если вы используете палочку для использования read(),вы можете использовать select() или poll() для ожидания доступности данных в сокете.

0 голосов
/ 16 мая 2011

Я решил проблему.
Спасибо за все ваши комментарии, они очень помогли.

Проблема заключалась в использовании неправильного файлового дескриптора .

Я передал файловый дескриптор сокета сервера read() вместо файлового дескриптора клиентского сокета , который accept() вернул.

...