2015-09-21 139 views
0

因此,我使用winsock2和TCP編寫了一個簡單的多線程客戶端服務器應用程序。客戶端斷開後服務器應用程序崩潰

這裏是它如何工作的快速摘要:

的服務器主線程是一個無限循環接受客戶,然後也將它們添加到持有這樣的每一個連接的客戶端服務器矢量: (只添加在重要的東西我的問題)

std::vector <Client*> clients; 
while (true){ 
    clients.push_back(&Client(accept(serverSocket, NULL, NULL), this)); 
} 

當一個新的客戶端連接到服務器,我們基本上建立與新客戶的插座和服務器本身作爲參數的新的客戶對象。

我的想法是給每個客戶端自己的線程,以便每個客戶端可以同時發送數據。

std::thread tickThread; 

Client::Client(SOCKET socket,Server* server) : 
isConnected(true), 
socket(socket), 
server(server) 
{ 
    tickThread = std::thread(&Client::tick,this); 
} 

客戶端線程,然後檢查是否客戶端發送的東西,然後將其發送到服務器。它還檢查客戶端是否仍然連接。 無效客戶::打勾(){

while (isConnected){ 
    errorHandler = recv(socket, receivedData, 255, 0); 
    if (errorHandler == SOCKET_ERROR){ 
     disconnect(); 
    } 
    else { 
     //send received data to server 
    } 
} 

如果客戶端斷開,它告訴服務器從連接的客戶端載體刪除客戶端,然後設置「isConnected」布爾爲false,這樣線程可以退出其功能。

void Client::disconnect(){ 
    isConnected = false; 
    server->removeClient(this); 
} 

這是它是如何工作的,但是隻要客戶端斷開再有錯誤的服務器崩潰:

R6010 - 中止()被調用

所有調試表明瞭我這是我的錯誤:

switch (_CrtDbgReportW(_CRT_ERROR, NULL, 0, NULL, L"%s", error_text)){ 
      case 1: _CrtDbgBreak(); msgshown = 1; break; 
      case 0: msgshown = 1; break; 
} 

所以是的,我真的不知道什麼是導致此崩潰,但我懷疑這可能與線程使用的第一個功能e客戶端在從服務器的客戶端向量中刪除時基本上被刪除。

如果事實證明是問題,你們可以給我一個更好的方式來實現每個客戶端有自己的線程的想法嗎?

編輯:改變了矢量誤差,但是崩潰仍然只要客戶端斷開

+0

'&Client()'計算出一個指向右值的指針,這個指針只有在你使用不合規的編譯器時纔有可能。不要這樣做。 –

+0

所以我應該使用一個正常的向量呢? –

+1

'所有的調試都顯示我這是我的錯誤'是真的嗎?你檢查了調用堆棧嗎?你嘗試用'try/catch'封裝一段代碼嗎?你有沒有嘗試評論'server-> removeClient(this);'看它是否會停止崩潰? – jpo38

回答

4

的錯誤是在下面的代碼塊:

while (true){ 
    clients.push_back(&Client(accept(serverSocket, NULL, NULL), this)); 
} 

Client(accept(serverSocket, NULL, NULL), this)是產生當語句執行完畢的是被破壞的臨時Client對象的表達式。但是,您將該臨時對象的地址添加到您的vector

如果你想創建Client對象並存儲它們的指針,你將需要爲它們分配內存。我建議使用std::unique_ptr來管理它們,以便您的vector聲明其內存的所有權,並在它們從vector中刪除或vector本身被銷燬時自動釋放它們。然後你的代碼變爲:

std::vector<std::unique_ptr<Client>> clients; 
while (true){ 
    clients.push_back(std::make_unique<Client>(accept(serverSocket, NULL, NULL), this)); 
} 
+0

'std :: unique_ptr'需要C++ 11。對於pre-C++ 11編譯器,你將不得不直接使用'new':'std :: vector clients; (true){client.push_back(new Client(accept(serverSocket,NULL,NULL),this)); }',並且當你將它從'vector'中移除時,一定要手動'刪除''Client'。 –

+0

謝謝,我現在改爲使用智能指針。但是,當客戶端斷開連接時它仍然崩潰。 –

+1

@Styxs你正在加入或者脫離'tickThread'嗎?如果沒有完整的代碼示例,很難發現問題出在哪裏,但是'std :: thread'必須連接到主線程或分離,否則當線程被銷燬時你可能會看到崩潰。請參閱文檔[這裏](http://en.cppreference.com/w/cpp/thread/thread)。 –

3

在這段代碼發生:

clients.push_back(&Client(accept(serverSocket, NULL, NULL), this)); 

您正在推動一個臨時對象的地址進入容器。當push_back()完成時,臨時對象被銷燬,所以地址不再有效。我想知道,什麼樣的編譯器可以讓你做到這一點。

+0

我認爲許多編譯器會允許這樣做。它在編譯時是完全有效的代碼。這只是運行時不安全的代碼。編譯器不會假定臨時指針將被用於什麼時候(在獲取指針時,臨時指針仍然有效),並且它不知道「vector」將持有指針一段時間比臨時的壽命更長。 –

+0

我正在使用visual studio 2013及其編譯器。 –

+0

@RemyLebeau,以臨時地址爲準是一個錯誤。這是MSVC的擴展,我覺得這是可惡的。例如,這是什麼g ++: main.cpp:7:5:錯誤:不能採取類型'int'的右值的地址 &(int(10)); ^ ~~~~~~~ 1生成錯誤。儘管如此, – SergeyA

相關問題