Может ли кто-нибудь дать мне хорошее объяснение поведения «send» для неблокирующих сокетов? - PullRequest
6 голосов
/ 20 марта 2011

Я прочитал документацию по крайней мере 10 раз и также прочитал приблизительно 10 или около того фрагментов кода и полных программ, в которых для отправки данных используются неблокирующие сокеты. Проблема в том, что некоторые учебники либо для начинающих (Beejs f.i.), либо довольно небрежны в своих предположениях; и те, которые не являются сложными, являются специализированными примерами кода, которые не объясняют, почему они делают то, что делают. Даже SO база знаний не исчерпывающе охватывает всю гамму поведения send, на мой взгляд. Что мне нужно, так это подробности о е.е:

  • Что именно указывает код возврата 0 и стоит ли проверять errno тогда или нужно просто отказаться от соединения без дальнейшего расследования?
  • Оправдывает ли получение отрицательного возвращаемого значения закрытие соединения, или это только так, если errno не является EWOULDBLOCK, EAGAIN или EINTR (... другими)?
  • Стоит ли проверять errno, когда возвращаемое значение равно > 0? По-видимому, значение указывает количество данных, «отправленных» (в кавычках, потому что это действительно длинный процесс, верно), но поскольку сокет не является блокирующим, означает ли это, что можно сразу выполнить другой вызов, или, в зависимости от errno опять же, следует дождаться следующего случая отправки (используя select / poll / epoll)?
  • Как правило, проверяется ли сначала возвращаемое значение, а затем только значение errno? Или, может быть, send устанавливает errno при каждом вызове, независимо от возвращаемого значения? Это сделало бы проверку ошибок несколько проще ...
  • Если кто-то получит EINTR, что будет хорошим, надежным поведением для программы? Просто запишите состояние и повторите попытку при следующей отправке, например, с EWOULDBLOCK и EAGAIN?
  • Проверяет ли оба EWOULDBLOCK и EAGAIN? Можем ли мы доверять обоим, имеющим одинаковое значение, или это зависит от реализации?
  • Возвращает ли send EMSGSIZE для потоковых сокетов? Если этого не произойдет, то размер буфера не будет слишком большим, верно?
  • Может ли возвращаемое значение быть равно одному из известных кодов ошибок?

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

Ответы [ 3 ]

9 голосов
/ 21 марта 2011

Здесь много вопросов:

  • Что именно указывает код возврата 0, и стоит ли тогда проверять errno или нужно просто сбросить соединение без дальнейшего изучения?

В системе POSIX send (2) никогда не сможет вернуть 0, если вы не вызовете его с аргументом длины 0. Проверьте документы для вашей конкретной системы, чтобы убедиться, что она соответствует спецификации POSIX

  • Означает ли получение отрицательного возвращаемого значения гарантию закрытия испорченного соединения, или это только так, если только errno не является EWOULDBLOCK, EAGAIN или EINTR (... другие)?

Нет, возвращаемое значение -1 (единственно возможное отрицательное возвращаемое значение) просто означает, что данные не были отправлены.Вам нужно проверить errno, чтобы увидеть ПОЧЕМУ - смотрите страницу руководства send (2) для получения полного списка всех возможных значений errno и их значения

  • Стоит ли проверять errno, когдавозвращаемое значение> 0?По-видимому, значение указывает количество данных, «отправленных» (в кавычках, потому что это действительно длинный процесс, верно), но поскольку сокет не является блокирующим, означает ли это, что можно сразу выполнить другой вызов или, в зависимости от errno снованужно ждать следующего события отправки (используя select / poll / epoll)?

Если send вернет успех (> 0), то errno не изменится и будет содержать все, что былоbefore (что, вероятно, является ошибкой из-за какого-то более раннего системного вызова).

  • В основном, проверяется ли сначала возвращаемое значение, а затем только значение errno?Или, может быть, отправлять наборы errno при каждом вызове, независимо от возвращаемого значения?Это несколько упростит проверку ошибок ...

Сначала проверьте возвращаемое значение, а затем errno, если возвращаемое значение равно -1.Если вы действительно хотите, вы можете установить errno равным 0 перед вызовом, а затем проверить его потом

  • Если вы получите EINTR, что будет хорошим, устойчивым поведением для программы?Просто запишите состояние и повторите попытку при следующей отправке, как в случае EWOULDBLOCK и EAGAIN?

Что ж, проще всего отключить прерывание системных вызовов, и в этом случае вы никогда не получитеEINTR.Обрабатывать его так же, как EWOULDBLOCK / EAGAIN, тоже хорошо.

  • Проверяет ли EWOULDBLOCK и EAGAIN один?Можем ли мы доверять тому, что оба имеют одно и то же значение, или это зависит от реализации?

Зависит от реализации, хотя в целом они одинаковы.Иногда существуют странности с режимами эмуляции SysV и BSD, которые могут отличать их друг от друга и могут произойти либо

  • Возвращает ли send EMSGSIZE для потоковых сокетов?Если этого не произойдет, то размер буфера не будет слишком большим, верно?

Потоковые сокеты не имеют атомарных сообщений, а EMSGSIZE предназначен только для атомарных сообщений, поэтому нет, потоковые сокеты могутне вернуть EMSGSIZE

  • Может ли само возвращаемое значение быть равно одному из известных кодов ошибок?

Единственный код ошибки - -1.Успех - это количество записанных байтов, поэтому, если вы могли бы записать 2 ^ 32-1 байта на 32-битной машине (или 2 ^ 64-1 на 64-битной машине), это было бы проблемой, но вы не можетенапишите так много байтов (и вы, как правило, получите EINVAL или EFAULT).

3 голосов
/ 21 марта 2011

Я постараюсь ответить на ваши вопросы.

  • Возвращаемое значение 0 из send указывает, что было отправлено 0 байтов. Ошибка указывается возвращаемым значением -1. Если вы позвонили send с длиной 0, следует ожидать возврата 0. Хотя неблокирующий сокет должен возвращать -1 с ошибкой EAGAIN или EWOULDBLOCK, если он заблокирует, я не был бы чрезмерно удивлен, если бы некоторая реализация возвратила записанные 0 байтов вместо этого.
  • EWOULDBLOCK, EAGAIN и EINTR - ошибки, к которым следует повторить попытку, не закрывайте соединение при получении одного из них. Другие ошибки указывают на проблему, которая может привести к закрытию.
  • Нет, не проверяйте errno после успешного вызова библиотеки (если в документации не указано, что вы можете сделать это по какой-то причине; мне не известны какие-либо случайные действия, которые делают это). Обратите внимание, что errno может не остаться неизменным при успешном вызове библиотеки, так как этот вызов мог совершать другие вызовы, которые возвращали ошибки, которые ожидались и обрабатывались должным образом (например, вызов мог попытаться определить файл, полностью ожидая, что он может не существовать; errno тогда будет ENOENT, хотя не было никакой реальной ошибки). Если send возвращает короткую запись, вы можете попробовать еще раз (и, возможно, получить EWOULDBLOCK / EAGAIN) или подождать следующего select.
  • Да, сначала проверьте возвращаемое значение. errno ничего не говорит вам, если вызов был успешным.
  • В EINTR вы можете немедленно повторить попытку или подождать в следующий раз через цикл select.
  • Вы должны проверить EAGAIN и EWOULDBLOCK; Я полагаю, вы могли бы сделать #if EAGAIN == EWOULDBLOCK, если производительность особенно критична (но помните, профиль затем оптимизировать).
  • Все это будет зависеть от базового протокола, но обычно я ожидаю, что потоковый протокол не будет содержать атомарных сообщений (если, возможно, при использовании MSG_OOB). Для TCP любой размер буфера должен быть в порядке.
  • Конечно, возвращаемое значение может быть равным любой из ошибочных констант, но это ничего не значит. Например, в моей системе, если записано 11 байт, возвращаемое значение будет равно EAGAIN.

НТН.

2 голосов
/ 04 сентября 2012

На EINTR и системных звонках:

  • если вы используете GLIBC, вам не нужно беспокоиться об этом, по крайней мере, в контексте системных вызовов.Я получил это из Glibc FAQ , grep для "Почему больше не прерываются системные вызовы сигналов?"

  • если вы используете LINUX, то вы, вероятно, нене нужно беспокоиться о странной семантике системного вызова connect (), которую Дэвид Мадор имеет здесь около .В противном случае будьте готовы к поведению, отличному от обычного, для асинхронного вызова connect ().

...