Метод обработки ошибки неверного файлового дескриптора

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

Вопрос. В приведенном выше сценарии, когда клиентское соединение не существует, как мне обработать эти BAD FILE DESCRIPTORs . Могу ли я вызвать recv() на них?


person user1495948    schedule 11.10.2012    source источник


Ответы (2)


Подпись для select():

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

Разве набор errorfds не дает вам список дескрипторов, вызвавших ошибку, например EBADF?

Вы можете вызвать recv() или любую другую функцию, которая принимает файловый дескриптор с неверным файловым дескриптором; вы, скорее всего, вернете ошибку EBADF из вызванной функции (если только она не обнаружит другое состояние ошибки до обнаружения EBADF).

person Jonathan Leffler    schedule 11.10.2012
comment
recv() возвращает 0 в этом случае, как если бы соединение с клиентом было закрыто, но select возвращает неверный дескриптор файла для того же дескриптора сокета. - person user1495948; 11.10.2012
comment
Возможно, вам придется различать то, что система считает «плохим файловым дескриптором», и то, что вы считаете плохим файловым дескриптором. Система считает, что файловый дескриптор, который был закрыт (или никогда не открывался), является плохим. Тот факт, что в дальнем конце дескриптора чтения нет отправителя, не является ошибкой; дескриптор файла, вероятно, сообщается как «готовый», и когда вы читаете из него, вы получаете индикацию EOF (что означает чтение нулевых байтов). Дескриптор файла неплох; он просто никогда не вернет никаких данных. Когда вызов recv() возвращает ноль, вы заключаете, что клиент ушел. - person Jonathan Leffler; 11.10.2012
comment
Я закрываю соединение на стороне клиента, в то же время на стороне сервера, если я выбираю, fd устанавливается, и я делаю recv(), который возвращает 0, и я делаю вывод, что клиент закрыт. Но в некоторых сценариях выбор вернет плохой файл ошибка дескриптора. Знаете ли вы, в каком сценарии select возвращает EBADF для дескриптора? - person user1495948; 12.10.2012

Я считаю, что вы испортили разные ситуации.

Если соединение закрывается на стороне клиента, и вы вызываете select() на сокете на стороне сервера, сработает FD_READ и recv() вернет ноль, тогда вы можете закрыть сокет на стороне сервера.

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

Ошибка «плохой файловый дескриптор» возникает только тогда, когда вы вызываете API на уже закрытом сокете.

person ciphor    schedule 11.10.2012
comment
то есть вы имеете в виду, что в полуоткрытой ситуации TCP select вернет EBADF? - person user1495948; 12.10.2012
comment
Нет. Для полуоткрытого сокета TCP select() ничего не вернет, ни события чтения, ни события записи. Обычно таймер сердцебиения используется для обнаружения полуоткрытого сокета TCP и последующего его закрытия. - person ciphor; 12.10.2012
comment
Почему для дескриптора сокета, который стал неверным, select возвращает неверный файловый дескриптор, а recv возвращает 0? - person user1495948; 12.10.2012
comment
Я думаю, что дело обстоит так: 1) recv() возвращает 0, что означает, что одноранговый узел закрыл сокет; 2) сокет закрыт; 3) select() возвращает неверный дескриптор. Я не верю, что recv() может вернуть 0 для уже закрытого сокета. - person ciphor; 12.10.2012
comment
Если я использую fcntl(F_GETFD..), чтобы определить, какой сокет fd вышел из строя, fcntl возвращает 1, а select возвращает EBADF. В каком сценарии это возможно? - person user1495948; 13.10.2012