2011-10-05 49 views
2

我有一個啓用了http-keepalive的多線程gSOAP服務。當仍有客戶連接時,如何正常關閉服務?啓用http-keepalive的正常關機(多線程)gSOAP服務

gSoap: how to gracefully shutdown the webservice application?中提出了一個類似的問題,但答案沒有涵蓋http-keepalive方面:只有當http-keepalive會話沒有被客戶端關閉時,soap-serve函數纔會返回。因此,接受答案中的第2步將阻塞,直到客戶端決定關閉連接(或者接收超時到期,但短暫的超時將在此打破所需的http-keepalive行爲)。

gSOAP文檔中的示例存在同樣的問題。

我到目前爲止所嘗試的是爲所有掛在主線程的soap_serve調用中的soap結構調用soap_done()來中斷等待http-keepalive的連接,該連接在大多數情況下工作,但會崩潰罕見的條件(可能是一種競爭條件),所以這對我來說是無法解決的。

回答

0

我剛碰到同樣的問題,我想我已經爲您解決了問題。

正如您剛纔所說,問題在於gSoap掛在soap_serve上。發生這種情況是因爲gSOAP會爲您生成一個等待所有保持活動請求到達的內部循環,或者會出現服務器端的超時。

我所做的是抓取自動生成的服務存根中的soap_serve函數。我要列出原來soap_serve功能,讓你可以找到你的服務存根文件:

SOAP_FMAC5 int SOAP_FMAC6 soap_serve(struct soap *soap) 
    { 
    #ifndef WITH_FASTCGI 
     unsigned int k = soap->max_keep_alive; 
    #endif 

     do 
     { 
    #ifdef WITH_FASTCGI 
      if (FCGI_Accept() < 0) 
      { 
       soap->error = SOAP_EOF; 
       return soap_send_fault(soap); 
      } 
    #endif 

      soap_begin(soap); 

    #ifndef WITH_FASTCGI 
      if (soap->max_keep_alive > 0 && !--k) 
       soap->keep_alive = 0; 
    #endif 

      if (soap_begin_recv(soap)) 
      { if (soap->error < SOAP_STOP) 
       { 
    #ifdef WITH_FASTCGI 
        soap_send_fault(soap); 
    #else 
        return soap_send_fault(soap); 
    #endif 
       } 
       soap_closesock(soap); 

       continue; 
      } 

      if (soap_envelope_begin_in(soap) 
      || soap_recv_header(soap) 
      || soap_body_begin_in(soap) 
      || soap_serve_request(soap) 
      || (soap->fserveloop && soap->fserveloop(soap))) 
      { 
    #ifdef WITH_FASTCGI 
       soap_send_fault(soap); 
    #else 
       return soap_send_fault(soap); 
    #endif 
      } 

    #ifdef WITH_FASTCGI 
      soap_destroy(soap); 
      soap_end(soap); 
     } while (1); 
    #else 
     } while (soap->keep_alive); 
    #endif 
     return SOAP_OK; 
    } 

你應該提取該函數的主體,並取代舊soap_serve(mySoap)你的線程裏面調用(

do 
    { 
     if (Server::mustShutdown()) { 
      break; 
     } 

     soap_begin(mySoap); 

     // If we reached the max_keep_alive we'll exit 
     if (mySoap->max_keep_alive > 0 && !--k) 
      mySoap->keep_alive = 0; 


     if (soap_begin_recv(mySoap)) 
     { if (mySoap->error < SOAP_STOP) 
      { 
       soap_send_fault(mySoap); 
       break; 
      } 
      soap_closesock(mySoap); 

      continue; 
     } 

     if (soap_envelope_begin_in(mySoap) 
     || soap_recv_header(mySoap) 
     || soap_body_begin_in(mySoap) 
     || soap_serve_request(mySoap) 
     || (mySoap->fserveloop && mParm_Soap->fserveloop(mySoap))) 
     { 
      soap_send_fault(mySoap); 
      break; 
     } 


    } while (mySoap->keep_alive); 

請注意以下幾點:

  1. 服務器:: mustShutdown()作爲一個標誌,將是執行,因爲這些保活的有以下要求和hagns)螺紋小號等到真(外部)結束所有線程。當你想停止服務器處理新的請求時,你會使這個函數返回true並且循環結束。
  2. 我已經刪除了ifdef,WITH_FASTCGI這對我們現在不感興趣。
  3. 當你像這樣關閉連接時,連接到服務器的任何客戶端都會引發異常。例如用C#編寫的客戶端會拋出一個「底層連接被保留在活動中被服務器關閉」,這對我們來說非常合理。

但是,由於AudioComplex指出,我們尚未完成,系統仍然在等待soap_begin_recv上的請求。但我也有一個解決方案;)

連接處理池上的每個線程創建主soap上下文的副本(通過soap_copy),這些線程是我每次存儲的
這些上下文作爲駐留在主連接處理線程上的數組上的元素。 當終止主連接處理線程(即服務請求中的一個),它會「手動」的連接通過所有肥皂的上下文並最終確定使用:

for (int i = 0; i < soaps.size(); ++i) { 
    soaps[i]->fclose(soaps[i]); 
} 

這將迫使soap_serve循環來完成。它實際上將停止內部環路附近的stdsoap2.cpp_

r = select((int)soap->socket + 1, &fd, NULL, &fd, &timeout); 

這是不乾淨的解決方案921線(還沒有找到一個更清潔的一個),但它肯定會停止服務。

+0

首先,感謝您的回覆!但我認爲這不會解決我的問題。在你的解決方案中,調用soap_begin_recv()仍然會阻塞,直到發生超時或客戶端連接到套接字,對吧? – audiocomplex

+0

嗨,在那裏,剛剛回答你的問題,希望它有幫助! – svives

+0

謝謝,這確實解決了這個問題。非常感謝您的詳細解決方案! (對不起,我沒有長時間查看此線程......) – audiocomplex