2010-04-08 31 views
3

打断我在GNU/Linux下用C编程的多线程服务器这种怪异的行为。发送数据时,最终会被SIGPIPE中断。我设法忽略send()中的信号,并在每次操作之后处理errno,因为它。的send()总是由EPIPE

因此,它有两种单独的发送方法,一种是一次发送大量数据(或者至少尝试发送),另一种是发送近似相似的数量并将其切片成小块。最后,我试着用它来保持它发送数据。

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()和它随机给了我同样的错误。这很奇怪,因为我无法发送大数据块。我试图扩大发送缓冲区,但没有工作。

EDIT 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()。

+0

即使客户端没有切断通信,您是否确定它会提供EPIPE? – Jay 2010-04-08 14:40:39

+0

很确定,是的,因为我试着用EPIPE条件先退出。 – 2010-04-09 09:08:27

+0

你可以发布更多的代码吗? – Jay 2010-04-09 09:22:46

回答

1

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

4

正如已经建议由EJP,EPIPE自带如果对方已经关闭了套接字。此外,我认为无论发送函数返回的是否添加到“total_bytes_sent”的逻辑都是正确的,因为在某些情况下,发送可能会返回-1,您仍然可以继续操作(例如:在非阻塞套接字的情况下,你可能会得到一个errno EAGAIN,你需要再试一次)。

此外,如果发送返回0和errno不是EPIPE,那么你将连续循环我猜。

编辑:你也可以检查一个shutdown是否在套接字上被调用。即使这可能会导致这种行为。

+0

我明白了。是的,这不是一个好主意,我猜...但是EPIPE会因为我的服务器实现而被提出,还是仅仅是客户端的责任? – 2010-04-08 09:05:48

+0

AFAIK,这将是因为客户端关闭套接字而已。但是,再一次,您应该明白,这是您的逻辑错误,它不能正确处理对等连接关闭,因此应该在服务器中正确处理。 – Jay 2010-04-08 09:46:58

0

如果您使用的是面向流套接字,例如使用SOCK_STREAM创建的,你不应该发送你的数据块。

如果output_buf中包含所有数据,则只需在阻塞套接字上写入一次即可。

​​

如果您已经创建了非阻塞模式的套接字,那么你必须使用select及以上你的循环是错误的,除了一个事实,即它不处理返回值-1如指出周杰伦。

关于号评论:

从POSIX标准:

发送 - 插座

ssize_t供发送(INT插座,常量无效*缓冲液中,为size_t长度,INT上发送消息标志);

...

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

...

所以只有当出现发送函数调用不会接受的阻隔插座整个消息的错误。

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

+0

顺便说一句,有可能知道发送缓冲区有多少空闲空间?有没有内核API或什么? – 2010-04-09 17:54:37

+0

我实际上即将开始一个单独的SO线程,关于在阻塞套接字上发送的行为,因为我觉得很奇怪它可能在不发送所有数据的情况下返回。 – Ernelli 2010-04-09 19:04:56