send()总是被EPIPE打断

时间:2010-04-08 08:34:24

标签: c linux

我在GNU / Linux下用C语言编写的多线程服务器中有这种奇怪的行为。当它发送数据时,最终将被SIGPIPE中断。我设法忽略send()中的信号,并在每次操作后处理错误。

因此,它有两个单独的发送方法,一个发送大量数据(或至少尝试发送),另一个发送几乎相似的数量并将其切成小块。最后,我试着用它来发送数据。

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));

这段丑陋的代码在某些情况下起作用,但并非总是如此。

我很确定这不是硬件或ISP问题,因为这台服务器在六台欧洲服务器上运行,四台在德国,两台在法国。

有什么想法吗?

提前致谢。

编辑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 个答案:

答案 0 :(得分:4)

正如EJP已经建议的那样,如果对方已关闭套接字,则EPIPE会出现。此外,我认为你添加到“total_bytes_sent”的逻辑并不是什么发送函数返回是正确的,因为,在某些你仍然可以继续操作的情况下,send可能会返回-1(例如:在非阻塞套接字的情况下,你可能会得到一个错误的EAGAIN,你需要再试一次)。

此外,如果send返回0并且errno不是EPIPE,那么我将继续循环。

编辑:您还可以检查套接字上是否正在调用shutdown。即使这样也会导致这种行为。

答案 1 :(得分:1)

这意味着您正在写入另一端已关闭的套接字或管道。这是一个应用程序协议错误。

答案 2 :(得分:1)

如果您使用的是面向流的套接字,例如使用SOCK_STREAM创建,您不必以块的形式发送数据。

如果你在output_buf中有所有数据,你应该只需要在阻塞套接字上写一次。

send(client_sd, output_buf, output_buf_len, MSG_NOSIGNAL);

如果您已经在非阻塞模式下创建了套接字,那么您必须使用select并且上面的循环是错误的,除了它没有像Jay所指出的那样处理返回值-1这一事实。

关于评论:

从POSIX标准:

发送 - 在套接字上发送消息

ssize_t send(int socket,const void * buffer,size_t length,int flags);

...

如果发送套接字上没有空间来保存要传输的消息,并且套接字文件描述符没有设置O_NONBLOCK,则send()将阻塞,直到空间可用。如果发送套接字上没有空间来保存要传输的消息,并且套接字文件描述符确实设置了O_NONBLOCK,则send()将失败。 select()和poll()函数可用于确定何时可以发送更多数据。

...

因此,只有在发生错误时,发送函数调用才会接受阻塞套接字上的整个消息。

不幸的是,即使没有发生错误,似乎某些操作系统确实在发送时返回的字节数少于长度字节。这就是W. Richard Stevens libunp使用他自己的写作函数的原因。