2009-05-22 3 views
12

В настоящее время я поддерживаю некоторое программное обеспечение веб-сервера, и мне нужно выполнить много операций ввода-вывода. Сигналы read(), write(), close() и shutdown(), при использовании в сокете могут иногда поднять ошибку ENOTCONN. Что именно означает эта ошибка? Каковы условия, которые могут вызвать это? Я никогда не могу воспроизвести его локально, но есть пользователи, которые могут.Что вызывает ошибку ENOTCONN?

Прямо сейчас я просто игнорирую ENOTCONN, когда поднял close() и shutdown(), потому что кажется безобидным, но я не совсем уверен.

EDIT:

  • Я абсолютно уверен, что connect() вызов удалось. Я проверяю его возвращаемое значение.
  • ENOTCONN чаще всего поднимается close() и shutdown(). Я очень редко видел read() и write(), поднимая ENOTCONN.
+0

Какая операционная система? Я отслеживаю аналогичную проблему при работе с старой системой Solaris 10. Благодарю. – Nemo

+0

В основном FreeBSD.В то же время я обнаружил, что во FreeBSD есть ошибки ядра, которые могут вызвать close() и shutdown(), чтобы ошибочно возвращать ENOTCONN при работе с сокетами Unix. Solaris также имеет различные ошибки ядра w.r.t. Unix, но я обнаружил ошибки в connect(). – Hongli

ответ

13

Если вы уверены, что ничто на вашей стороне TCP-соединения не закрывает соединение, тогда это звучит для меня, как удаленная сторона закрывает соединение.

ENOTCONN, как указывали другие, просто означает, что разъем не подключен. Это не обязательно означает, что connect не удалось. Сокет, возможно, уже был подключен ранее, он просто не был во время вызова, который привел к ENOTCONN.

Это отличается от:

  • ECONNRESET: другой конец соединения отправил пакет сброса TCP. Это может произойти, если другой конец отказывается от соединения или не признает, что он уже подключен, между прочим.
  • ETIMEDOUT: обычно это относится только к connect. Это может произойти, если попытка подключения не будет выполнена в течение зависящего от системы времени.

EPIPE иногда могут быть возвращены некоторым сокета связанных системных вызовов в условиях, которые являются более или менее такой же, как ENOTCONN.Например, в некоторых системах EPIPE и ENOTCONN являются синонимами при возврате send.

Хотя это не редкость для shutdown вернуться ENOTCONN, так как эта функция должна рушить соединение TCP, я был бы удивлен видеть close возвращения ENOTCONN. Это действительно никогда не должно быть так.

Наконец, как указано в dwc, EBADF не должен применяться в вашем сценарии, если вы не пытаетесь выполнить какую-либо операцию над файловым дескриптором, который уже был close d. При отключении сокета (т. Е. Соединение TCP) это не то же самое, что закрытие дескриптора файла, связанного с этим сокетом.

+0

Вы ошибаетесь: другая сторона, закрывающая соединение, не является причиной отключения функции shutdown() или close(). Кроме того, ENOTCONN при выключении() не указывает, что RESET фактически не произошло на самом деле. (См. Мой ответ для более подробной информации.) –

+0

@ Robert: Я не думаю, что я это сказал. Я только вижу, что я сказал, что 'ENOTCONN' может быть возвращен' shutdown' (что абсолютно верно) и что он никогда не должен возвращаться 'close', поскольку OP говорил, что он наблюдал (также абсолютно верно) , –

+0

Вы говорите оба, что ENOTCONN был возвращен, потому что удаленная сторона закрыла соединение _and_, что оно отличается от ECONNRESET. - Я говорю, что ENOTCONN никогда не возвращается для обычного удаленного подключения. Я также считаю, что реализация shutdown() может возвращать ENOTCONN в случаях ECONNRESET, потому что последнее не должно быть возвращено shutdown(). –

0

Транспортные конечная точка не подключена

Розетка, связанная с протоколом, ориентированные на соединение и не была подключена. Обычно это ошибка программирования.

От: http://www.wlug.org.nz/ENOTCONN

+1

Я знаю имя ошибки, но что это значит *? Какова эта конечная точка транспорта, на которую она ссылается, и как она отличается от EPIPE, ECONNRESET и ETIMEDOUT? – Hongli

+0

Я думаю, вы просто не вызывали connect() или не проверяли, удалось ли connect(). –

+0

Я сделал, это удалось. Пользователи, которые сообщали об этой проблеме, заметили, что функции read() s и write() работают нормально, но при отключении() и закрытии() это вызывает повышение ENOTCONN. Я не могу воспроизвести его локально вообще, и большинство пользователей не сталкиваются с этой проблемой. – Hongli

0

Если вы уверены, что вы подключены правильно, в первую очередь, ENOTCONN, скорее всего, вызвано либо fd закрытия на вашем конце, пока (возможно, в другом потоке?) вы находитесь посередине запроса или сбрасываете соединение, когда находитесь в середине запроса.

В любом случае это означает, что разъем не подключен. Идите и очистите эту розетку. Он мертв. Нет проблем с вызовом close() или shutdown() на нем.

+0

Я уверен, что fd не закрывается другим потоком. Но предположим, что это не приведет к EBADFD? Кроме того, это часто close() и shutdown(), которые поднимают ENOTCONN, а не read() и write(). Что это значит? – Hongli

+0

Обратите внимание, что в моем ответе я указываю «в середине запроса». Таким образом, вы выполняете вызов, а выполнение переключается на другой процесс. Затем соединение падает, и ваш процесс начинает выполняться ... EBADFD не был и не прав (это действительный fd), но вы не подключены. Таким образом, вы получаете ENOTCONN. – dwc

1

Я верю, что ENOTCONN возвращается, потому что shutdown() не должен возвращать ECONNRESET или другие более точные ошибки.

Неправильно предположить, что другая сторона «просто» закрыла соединение. На уровне TCP другая сторона может только закрыть соединение (или прервать его). Соединение является обычным, полностью закрытым, если обе стороны выполняют shutdown() (или close()). Если обе стороны делают это, shutdown() на самом деле преуспевает для обоих из них!

Проблема в том, что shutdown() сделал не преуспеть в обычном (полу) закрытии соединения, ни как первом, чтобы закрыть его, ни как втором. - Из ошибок, перечисленных в документах POSIX для shutdown(), ENOTCONN является наименее неуместным, поскольку другие указывают на проблемы с аргументами, переданными shutdown() (или проблемы с локальными ресурсами для обработки запроса).

Так что случилось? В наши дни устройство NAT, расположенное где-то между двумя участвующими сторонами, могло отказаться от ассоциации и отправляет пакеты RESET в качестве реакции. Сброс соединений настолько распространен для IPv4, что вы получите их в любом месте вашего кода, даже замаскированный как ENOTCONN при завершении работы().

Ошибка кодирования также может быть причиной. Например, в неблокирующем сокете connect() может возвращать 0 без указания успешного соединения.

+0

Это уже подтверждено как ошибка ядра FreeBSD. Код ошибки ENOTCONN наблюдался в сокетах Unix, поэтому TCP все равно не имел к этому никакого отношения. – Hongli

+0

Я столкнулся с вашим вопросом, ища причины, по которым ENOTCONN возвращается во время работы с ранее подключенным TCP-сокетом. (Разве это не ваше дело, но вопрос все еще в воздухе.) - И я был вынужден ответить, потому что причина в том, что ошибка (ядро) где-то или условие ошибки соединения, которое неправильно присвоено ENOTCONN. Причина в том, что обычное дистанционное соединение закрыто. Этот ответ неверен как для сокетов домена unix (ваш случай), так и для сокетов TCP (мой случай). –

1

Это связано с тем, что в момент отключения() гнезда у вас есть данные в буфере сокета, ожидающие доставки на удаленную сторону, которая закрыла() или закрыла() свой приемный сокет. Я не понимаю, как работают сокеты, я скорее noob, и мне не удалось найти файлы, в которых реализована эта функция «shutdown», но, увидев, что практически нет руководства пользователя для всех сокетов начал пробовать все возможности, пока не получил ошибку в «контролируемой» среде. Это может быть что-то другое, но после многих попыток это объяснения, которые я установил для:

  • Если вы отправили данные после того, как удаленная сторона закрыла соединение, то при завершении работы() вы получите сообщение об ошибке.
  • Если вы отправили данные до того, как удаленная сторона закрыла соединение, но не получила его() на другом конце, вы можете выключить() один раз, а в следующий раз при попытке выключения() вы получите сообщение об ошибке.
  • Если вы не отправляли какие-либо данные, вы можете отключить все время, которое вы хотите, до тех пор, пока удаленная сторона не выключится(); как только удаленная сторона отключится(), если вы попытаетесь отключить(), а сокет уже отключен(), вы получите сообщение об ошибке.
Смежные вопросы