2014-09-24 50 views
2

我有一個用C++編寫的Windows服務,它用作偵聽傳入連接的TCP服務器。在Windows C++服務中阻塞兩個線程

我初始化服務器套接字並將accept代碼放在一個單獨的線程中。這將接受並處理傳入的連接。

但是,如果服務收到STOP信號,我還需要停止該線程。所以我想用CreateEvent創建一個事件對象,然後等待它發出信號。這種等待會發生在創建接受線程的線程中。所以我可以使用TerminateThread函數在接收到STOP信號時停止接受線程。

然而,MSDN

TerminateThread是隻能在最極端的情況下使用危險的功能。

這應該如何嚴格遵守並且我的方法正確?還有什麼可以做到這一點?

+0

非常嚴格。在你的情況下,我認爲你可以使用CancelSynchronousIo來取消accept()函數。 – 2014-09-24 06:36:17

回答

2

在Windows中,只需調用closesocket即可喚醒來自其他線程的阻止accept調用。阻塞accept調用將返回-1,並且您的代碼有機會通過檢查您已經設置的某個其他退出條件(例如全局變量)來突破它所處的任何循環。

這也適用於Mac(和可能的BSD衍生物)與close函數,但不是Linux。對此問題更通用的UNIX解決方案是here

以下Windows解決方案的一些pseduo代碼。

SOCKET _listenSocket; 
bool _needToExit = false; 
HANDLE _hThread; 

void MakeListenThreadExit() 
{ 
    _needToExit = true; 
    closesocket(_listenSocket); 
    _listenSocket = INVALID_SOCKET; 

    // wait for the thread to exit 
    WaitForSingleObject(_hThread, INFINITE);  
} 

DWORD __stdcall ListenThread(void* context) 
{ 
    while (_needToExit == false) 
    {    
     SOCKET client = accept(_listenSocket, (sockaddr*)&addr, &addrSize); 
     if ((client == -1) || _needToExit) 
     { 
      break; 
     } 
     ProcessClient(client); 
    }  
    return 0; 
} 
+0

從未想過這個!感謝您的解決方案。 – Cygnus 2014-09-24 07:09:42

+0

這不起作用,不應該使用。您有以下競爭條件:您即將打電話'接受'。你調用'closesocket'。另一個線程(可能在你不控制的庫中)分配一個套接字並獲得剛剛關閉的相同描述符。您現在在錯誤的*套接字上調用accept,並從庫中竊取連接。當你開始調用'closesocket'時,根本沒有辦法確保你仍然被'accept'阻塞。 – 2014-09-24 08:13:26

+0

@DavidSchwartz - 它實際上**做**工作。至於它是否應該被用於辯論。 OP應仔細考慮你的觀點。我打算仔細檢查,但我相當肯定Win32使用遞增的套接字句柄標識符或合理的算法來確保套接字句柄值不會被快速重用 - 用於處理這些類型的錯誤。在你描述的競態條件中,你必須在競爭條件內創建(2^32)-1套接字句柄以獲得先前關閉的重複套接字句柄。 – selbie 2014-09-24 08:32:20

1

在這種情況下,請勿在阻塞套接字上使用accept()。改爲使用非阻塞套接字。然後,您可以使用select()並超時,以便您的線程可以定期檢查終止條件。或者更好的是,使用WSACreateEvent()WSASelectEvent()。創建兩個事件對象,一個用於檢測客戶端連接,另一個用於檢測線程終止。然後您可以使用WSAWaitForMultipleEvents()同時等待兩個事件。使用WSASetEvent()可在需要時發出終止事件的信號,並且每當發出其他事件時,請致電accept()WSAAccept()WSAWaitForMultipleEvents()會告訴你要採取哪一個事件。