send () всегда прерывается EPIPE - PullRequest
4 голосов
/ 08 апреля 2010

У меня странное поведение на многопоточном сервере, запрограммированном в C под GNU / Linux.Пока он отправляет данные, SIGPIPE в итоге будет прерван.Мне удалось игнорировать сигналы в send () и обрабатывать errno после каждого действия из-за этого.

Итак, у него есть два отдельных метода отправки, один из которых отправляет большое количество данных одновременно (или, по крайней мере, пытается) и другой, который отправляет почти одинаковое количество и нарезает его небольшими кусочками.Наконец, я попытался с этим сохранить отправку данных.

do
{
    total_bytes_sent += send(client_sd, output_buf + total_bytes_sent,
                             output_buf_len - total_bytes_sent, MSG_NOSIGNAL);
}
while ((total_bytes_sent < output_buf_len) && (errno != EPIPE));

Этот некрасивый кусок кода работает в определенных ситуациях, но не всегда.

Я почти уверен, что это не такпроблема с оборудованием или провайдером, поскольку этот сервер работает на шести европейских серверах, четыре в Германии и два во Франции.

Есть идеи?

Заранее спасибо.

РЕДАКТИРОВАТЬ1: да, я заметил, что этот кусок кода дерьмовый (спасибо Джей).Первоначально я имел в виду, что этот код дает мне EPIPE всякий раз, когда клиент прерывает связь или нет.

РЕДАКТИРОВАТЬ 2: Я пытался с одной send (), и он выдает мне ту же ошибку случайноЭто странно, потому что я не могу отправить большой кусок данных.Я попытался увеличить буфер отправки, но не сработал.

РЕДАКТИРОВАТЬ 3: В соответствии с запросом это большой фрагмент кода.

data_buf_len = cur_stream->iframe_offset[cur_stream->iframe_num - 1] - first_offset;
data_buf = cur_stream->data;
output_buf = compose_reply(send_params, data_buf, data_buf_len, &output_buf_len);

/* Obviously, time measuring is *highly* unaccurate, only for
 * design consistency purposes (it should return something).
 * */
clock_gettime(CLOCK_REALTIME, &start_time);
total_bytes_sent = send(client_sd, output_buf, output_buf_len, MSG_NOSIGNAL);
clock_gettime(CLOCK_REALTIME, &stop_time);
spent_time = (((int64_t)stop_time.tv_sec * NANOSEC_IN_SEC) +
    (int64_t)stop_time.tv_nsec) - (((int64_t)start_time.tv_sec * NANOSEC_IN_SEC) +
    (int64_t)start_time.tv_nsec);

free(output_buf);
unload_video(cur_video);

if (total_bytes_sent < 0)
{
    log_message(MESSAGE, __func__, IMSG_VIDEOSTOP, cur_video->path);
    log_message(MESSAGE, __func__, IMSG_VIDEOSTOP, NULL);   
}

/* Hope it will not serve >2147483647 seconds (~68 years) of video... */
return ((int)spent_time);

Только один вызов send () сбольшой буфер.Есть еще один пример, слишком большой для размещения, который делит каждый буфер на более мелкие куски и вызывает send () для каждого.

Ответы [ 3 ]

4 голосов
/ 08 апреля 2010

Как уже было предложено EJP, EPIPE появляется, если другая сторона закрыла сокет. Кроме того, я не думаю, что ваша логика добавления к «total_bytes_sent» того, что возвращает функция отправки, правильна, потому что send может возвращать -1 в некоторых случаях, когда вы все еще можете продолжать работать (например, в случае неблокирующего сокета Вы можете получить ошибку EAGAIN, где вам нужно попробовать еще раз).

Кроме того, если send возвращает 0 и errno не EPIPE, то, я думаю, вы будете непрерывно зацикливаться.

РЕДАКТИРОВАТЬ: Вы также можете проверить, вызывается ли shutdown для сокета. Даже это может вызвать такое поведение.

1 голос
/ 08 апреля 2010

Если вы используете потоково-ориентированный сокет, например, созданный с помощью SOCK_STREAM, вам не нужно отправлять данные порциями.

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

send(client_sd, output_buf, output_buf_len, MSG_NOSIGNAL);

Если вы создали свой сокет в неблокирующем режиме, то вы должны использовать select, и ваш цикл выше неверен, за исключением того факта, что он не обрабатывает возвращаемое значение -1, как указал Jay.

Относительно комментария Nos:

Из стандарта POSIX:

отправить - отправить сообщение в сокет

отправка ssize_t (int-сокет, const void * буфер, длина size_t, int-флаги);

...

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

...

Таким образом, только при возникновении ошибок вызов функции send не будет принимать все сообщение в блокирующем сокете.

К сожалению, похоже, что некоторые операционные системы на самом деле возвращают меньше байтов длины при отправке, даже если ошибок не возникает. Именно поэтому В. Ричард Стивенс libunp использует свою собственную письменную функцию.

1 голос
/ 08 апреля 2010

Это означает, что вы пишете в сокет или канал, другой конец которого уже закрыт. Это ошибка протокола приложения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...