2016-07-10 9 views
0

クライアント接続を処理するサーバーを作成していますが、クラス内のスレッドでクラスを動かすことができません。スレッドがある場合は、std::move()でベクターに移動できますスレッドはクラス内にあり、スレッドは移動不可能なオブジェクトなので、多くのエラーが発生しています。スレッド内のC++移動クラス

Core.cpp:

#define OS_LINUX 

#include <iostream> 
#include <vector> 
#include <string> 
#include <thread> 

#include <csignal> 
#include <cstdlib> 

#include <TCPServer/TCPServer.h> 
#include <TCPServer/TCPServerConnection.h> 
#include <ProcessManip/ProcessManip.h> 
#include <Log/Log.h> 

#include "ConnectionHandler.h" 

//Global 
bool TERMNINATE = false;//If true loops must end 
const int PORT = 8822; 

//Proto 
void terminate(int sig); 
void receive_message(ConnectionHandler *handler,string message); 

using namespace std; 

int main(int argc,char *argv[]) 
{ 
    //Add Terminate handler 
    signal(SIGINT,terminate); 

    //Load configuration 
    Log::logDebug("Starting ..."); 

    //Init 
    vector<ConnectionHandler> connections; 

    //Init modules 

    //Main 
    Log::logDebug("Running"); 
    TCPServer server; 

    if(!server._bind(PORT)) 
    return 1; 

    if(!server._listen()) 
    return 2; 

    ProcessManip::cleanProcess(); /* Clean dead processes */ 

    server.setBlocking(false);/* accept returns invalid socket if no connection */ 
    Log::logDebug("Listening ..."); 
    while(!TERMNINATE) 
    { 
    TCPServerConnection conn = server._accept(); 
    if(!conn.isValid()) 
     continue; 

    Log::logDebug((string)"Got connection from: "+conn.getAddress());/* Print IP address of client */ 

    ConnectionHandler ch(&TERMNINATE,std::move(conn)); 
    ch.setCallback(receive_message); 
    ch.start(); 
    connections.push_back(std::move(ch)); //here is problem 

    /*connections.push_back(ConnectionHandler(&TERMNINATE,conn)); /* Add it to vector */ 
    /*connections.back().setCallback(receive_message); 
    connections.back().start();*/ 
    Log::logDebug("Connection added to vector"); 


    } 
    server.setBlocking(true); 

    //Dispose 
    Log::logDebug("Stopping ..."); 
    /*for(auto it = connections.begin();it!=connections.end();) 
    { 
     Log::logDebug((string)"Closed connection with: "+(*it).getConnection().getAddress());/* Print IP address of client */ 
     //(*it).close(); /* Close connetion */ 
    // it = connections.erase(it); /* Delete ConnectionHandler from vector */ 
// } 

    server._close(); 

    Log::logDebug("Closed"); 
    return 0; 
} 

void terminate(int sig) 
{ 
    //Change global value to true 
    TERMNINATE = true; 
} 

void receive_message(ConnectionHandler *handler,string message) 
{ 
    Log::logDebug((string)"Message ("+handler->getConnection().getAddress()+") : "+message); 
} 

ConnectionHandler.h

#ifndef EXT_CONNECTIONHANDLER 
#define EXT_CONNECTIONHANDLER 

#include <TCPServer/TCPServerConnection.h> 
#include <thread> 
#include <string> 
#include <iostream> 
#include <functional> 

using namespace std; 

class ConnectionHandler 
{ 
public: 
    ConnectionHandler(bool *pMainTerminate,TCPServerConnection pConnection); 
    ConnectionHandler(TCPServerConnection pConnection); 
    ~ConnectionHandler(); 

    void start(); /* Start listening */ 
    void stop(); /* Stop listening */ 
    void close(); /* Stops listening + close connection */ 

    void setConnection(TCPServerConnection pConnection); 
    TCPServerConnection getConnection(); 

    void setCallback(function<void(ConnectionHandler*,string)> pFunction); 


private: 
    bool *mainTerminate = NULL; 
    bool handler_terminate = false; 
    short status = 0; 
    TCPServerConnection connection; 

    bool needTerminate(); 
    void run(); 
    void waitForEnd(); 

    function<void(ConnectionHandler*,string)> callback = NULL; 

    std::thread m_thread; 

}; 



#endif 

ConnectionHandler.cpp

#include "ConnectionHandler.h" 
ConnectionHandler::ConnectionHandler(bool *pMainTerminate,TCPServerConnection pConnection) 
{ 
    this->mainTerminate = pMainTerminate; 
    this->connection = pConnection; 
} 
ConnectionHandler::ConnectionHandler(TCPServerConnection pConnection) 
{ 
    this->mainTerminate = NULL; 
    this->connection = pConnection; 
} 
ConnectionHandler::~ConnectionHandler() 
{ 
    this->close(); 
} 
void ConnectionHandler::start() 
{ 
    m_thread = std::thread(&ConnectionHandler::run, this); 
    this->status = 1; 
} 

void ConnectionHandler::waitForEnd() 
{ 
    if(this->m_thread.joinable()) 
    this->m_thread.join(); 
} 

bool ConnectionHandler::needTerminate() 
{ 
    if(mainTerminate!=NULL) 
    return this->handler_terminate||*(this->mainTerminate); 
    else 
    return this->handler_terminate; 
} 

void ConnectionHandler::run() 
{ 
    string message = ""; 
    string tmp = ""; 
    this->connection.setBlocking(false); // So we can terminate any time 
    while(!this->needTerminate()) 
    { 
    message = this->connection._receive(); 
    if(message!="") 
    { 
     do 
     { 
     tmp = this->connection._receive(); 
     message+=tmp; 
     }while(tmp!=""); /* If we get longer message than we can grab at one time */ 
     this->connection._send(message); /* TODO Remove */ 

     if(this->callback!=NULL) 
     this->callback(this,message); 


     message = ""; 
    } 
    } 
    this->connection.setBlocking(true); 
} 

void ConnectionHandler::stop() 
{ 
    this->handler_terminate = true; /* Signals thread to stop */ 
    this->waitForEnd(); 
    this->status = 2; 
} 

void ConnectionHandler::close() 
{ 
    this->stop(); 
    this->connection._close(); /* Close connection */ 
    this->status = 3; 
} 

TCPServerConnection ConnectionHandler::getConnection() 
{ 
    return this->connection; 
} 

void ConnectionHandler::setConnection(TCPServerConnection pConnection) 
{ 
    this->connection = pConnection; 
} 
void ConnectionHandler::setCallback(function<void(ConnectionHandler*,string)> pFunction) 
{ 
    this->callback = pFunction; 
} 
+0

** TL; DR; **なぜ実際にそのクラスを第1位に移動する必要がありますか?これは深刻な設計上の欠陥のようだ。 –

+1

http://en.cppreference.com/w/cpp/thread/thread/thread第2のコンストラクタ。 'std :: thread'は確実に移動可能です。あなたのクラスにデフォルトの移動コンストラクタを追加するだけです。 –

+3

** TL; DR; **これは実際にはサーバーのための非常に**悪い**デザインです - 新しい接続ごとに新しいスレッドを生成することは、1000回の同時接続後にフリーズするだけのサーバーのレシピです。サーバーの書き込みを停止し、非同期IOとスレッドプールについて読み込み、次に移動してください。 –

答えて

3

このため、クラスviolates the Rule Of Threeの場合、std::threadの問題が解決されても、他の問題が発生する可能性があります。おそらく不思議なランタイムバグの形を取ります。

std::threadでのコンパイルの問題は問題ではなく、実際の問題の単なる症状です。このクラスは移動またはコピーしないでください。このクラスはnewで構成され、std::shared_ptr(または妥当なファクシミリ)に詰め込まれ、破壊されるまでそこにとどまります。 std::shared_ptrのみが渡され、ベクトルなどに詰め込まれます。

関連する問題