2010-04-08 34 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