2011-01-05 242 views
0

我不確定這是否是我遇到的已知問題,但我無法找到一個好的搜索字符串,它會給我任何有用的結果。 無論如何,這裏的基本破敗:客戶端應用程序崩潰導致服務器崩潰? (C++)

我們有一個相對簡單的應用程序,它從源(數據庫或文件)獲取數據,並將數據通過TCP流傳輸到連接的客戶端,客戶數量;客戶端:連接到服務器,設置爲讀取(超時設置爲高於服務器心跳消息頻率)。客戶端:連接到服務器,設置爲讀取(超時設置爲高於服務器心跳消息頻率)。它阻止讀取。

服務器:一個監聽線程接受連接,然後生成一個編寫器線程從數據源讀取並寫入客戶端。寫者線程也被分離(使用boost :: thread,所以只需調用.detach()函數)。它會不定地寫入,但在寫入之前會檢查errno是否有錯誤。我們使用一個perl腳本啓動服務器,併爲每個服務器進程調用「fork」。

問題: 在看似隨機的時間,客戶端將關閉「連接終止(SUCCESFUL)」,指示遠程服務器故意關閉套接字。但是,當發生這種情況時,SERVER應用程序也會關閉,沒有任何錯誤或任何事情。它只是崩潰。

現在,爲了進一步解決問題,我們有多個由運行不同文件和不同端口的啓動腳本啓動的服務器應用程序實例。當其中一臺服務器崩潰時,所有服務器崩潰。

服務器和客戶端都使用內部創建的相同「連接」庫。它主要是C套接字調用的C++包裝器。

這裏是在連接libary爲寫一些粗糙的代碼和讀取功能:

int connectionTimeout_read = 60 * 60 * 1000; 
int Socket::readUntil(char* buf, int amount) const 
    { 
     int readyFds = epoll_wait(epfd,epEvents,1,connectionTimeout_read); 
     if(readyFds < 0) 
     { 
      status = convertFlagToStatus(errno); 
      return 0; 
     } 
     if(readyFds == 0) 
     { 
      status = CONNECTION_TIMEOUT; 
      return 0; 
     } 
     int fd = epEvents[0].data.fd; 
     if(fd != socket) 
     { 
      status = CONNECTION_INCORRECT_SOCKET; 
      return 0; 
     } 
     int rec = recv(fd,buf,amount,MSG_WAITALL); 

     if(rec == 0) 
      status = CONNECTION_CLOSED; 
     else if(rec < 0) 
      status = convertFlagToStatus(errno); 
     else 
      status = CONNECTION_NORMAL; 
     lastReadBytes = rec; 
     return rec; 

    } 



int Socket::write(const void* buf, int size) const 
    { 

     int readyFds = epoll_wait(epfd,epEvents,1,-1); 
     if(readyFds < 0) 
     { 
      status = convertFlagToStatus(errno); 
      return 0; 
     } 
     if(readyFds == 0) 
     { 
      status = CONNECTION_TERMINATED; 
      return 0; 
     } 
     int fd = epEvents[0].data.fd; 
     if(fd != socket) 
     { 
      status = CONNECTION_INCORRECT_SOCKET; 
      return 0; 
     } 
     if(epEvents[0].events != EPOLLOUT) 
     { 
      status = CONNECTION_CLOSED; 
      return 0; 
     } 
     int bytesWrote = ::send(socket, buf, size,0); 
     if(bytesWrote < 0) 
      status = convertFlagToStatus(errno); 
     lastWriteBytes = bytesWrote; 
     return bytesWrote; 

    } 

任何幫助解決這個神祕的錯誤將是巨大的!至少,我希望它不會在服務器崩潰時崩潰(這對我來說真的很奇怪,因爲沒有雙向通信)。

此外,僅供參考,這裏是服務器偵聽代碼:

while(server.getStatus() == connection::CONNECTION_NORMAL) 
     { 
      connection::Socket s = server.listen(); 
       if(s.getStatus() != connection::CONNECTION_NORMAL) 
       { 
        fprintf(stdout,"failed to accept a socket. error: %s\n",connection::getStatusString(s.getStatus())); 
       } 

       DATASOURCE* dataSource; 
       dataSource = open_datasource(XXXX); /* edited */    if(dataSource == NULL) 
       { 
        fprintf(stdout,"FATAL ERROR. DATASOURCE NOT FOUND\n"); 
        return; 
       } 
        boost::thread fileSender(Sender(s,dataSource)); 
        fileSender.detach(); 


     } 

...和這裏是催生孩子發送線程:

::signal(SIGPIPE,SIG_IGN); 

    //const int headerNeeds = 29; 
    const int BUFFERSIZE = 2000; 
    char buf[BUFFERSIZE]; 

    bool running = true; 
    while(running) 
    { 
      memset(buf,'\0',BUFFERSIZE*sizeof(char)); 
     unsigned int readBytes = 0; 
     while((readBytes = read_datasource(buf,sizeof(unsigned char),BUFFERSIZE,dataSource)) == 0) 
     { 
      boost::this_thread::sleep(boost::posix_time::milliseconds(1000)); 
     } 
     socket.write(buf,readBytes); 
     if(socket.getStatus() != connection::CONNECTION_NORMAL) 
      running = false; 

    } 
    fprintf(stdout,"socket error: %s\n",connection::getStatusString(socket.getStatus())); 
    socket.close(); 
    fprintf(stdout,"sender exiting...\n"); 

任何見解會受到歡迎!提前致謝。

+1

@Nik如果你想讓更多的人幫助你,你應該接受一些你以前的問題的答案。 0%將推遲很多潛在的追求者。 – chrisaycock 2011-01-05 02:45:34

+0

什麼操作系統?有任何核心轉儲? (去魚) – bmargulies 2011-01-05 02:46:14

+0

@chrisaycock剛剛接受了前面的線程,謝謝。 – Nik 2011-01-05 02:49:35

回答

2

你已經可能一切倒退...當服務器崩潰時,操作系統將關閉所有套接字。因此,服務器崩潰首先發生並導致客戶端獲取斷開連接消息(實際上是TCP段中的FIN標誌),崩潰不是套接字關閉的結果。

由於您有多個服務器進程同時崩潰,我會查看他們共享的資源以及所有服務器將嘗試同時執行的任何計劃任務。

編輯:你沒有一個客戶端連接到多個服務器,是嗎?請注意,TCP連接始終是雙向的,因此如果客戶端斷開連接,服務器進程將獲得反饋。一些互聯網提供商甚至被發現在連接上生成RST數據包,而這些數據包未通過可疑流量的測試。

編寫一個信號處理程序。確保它只使用原始I/O函數記錄問題(打開,寫入,關閉,而不是fwrite,而不是printf)。

檢查返回值。檢查套接字上的write負數返回值,但檢查所有返回值。

+0

感謝您的快速發佈! ATM是他們似乎共享的唯一資源是DataSource本身,但所發生的一切都是fread或數據庫調查。在文件的情況下,文件本身被附加到一個單獨的進程,並且不通過套接字發送數據;純粹數據接收然後輸出到文件。 將進一步查看服務器代碼,看看我能否找到任何東西。 – Nik 2011-01-05 02:54:34

+0

我之前有類似的問題,但意識到問題稍有不同。這裏是過去發生的事情: 之前,write函數實際上在epoll_wait上有一個超時用於寫入。由於在客戶端應用程序中發生了一些處理,套接字在超時時間內不會「準備好」,從而導致崩潰。 Fix是爲了寫入不確定(暫時)而設置的時間,並且它被SEEMED解決。 – Nik 2011-01-05 03:05:01

+0

參考您的編輯:不,一個客戶端只能連接到一臺服務器。在tcp級別,我明白有來回的通信,但在應用程序級別上沒有任何客戶端通信服務器;它的所有單向。 我使用send()atm,它應該和write()一樣嗎?有什麼區別嗎? – Nik 2011-01-05 03:43:00

0

感謝您的所有意見和建議。 按照Ben的建議查看代碼並添加信號處理之後,應用程序本身更加穩定。感謝您的所有意見。

然而,最初的問題是由於一個流氓腳本,其中一個管理員以root身份運行,會隨機殺死服務器端計算機上的某些進程(我不會進入它正在嘗試執行的操作在現實中;安全地說它是越野車)。 獲得的經驗:檢查環境。

謝謝大家的建議。