2015-02-17 2 views
1

Я работаю на сети приложений с использованием ASIO и называют Chat-Server/ClientASIO класс сеанс чата бросает исключение на уничтожение: C++ ASIO

Я спросил подобный вопрос Here

Чтобы лучше объяснить, что я добавляю больше кода здесь :

Мои Cserver Class

class CServer { 
private: 

    mutable tcp::acceptor acceptor_; // only in the listener 
    asio::io_service& io_; 
    CSerSessionsManager mng_; 

    std::string ip_; 
    std::string port_; 

public: 

    CServer::CServer(asio::io_service& io_service, const std::string IP, const std::string port) : io_(io_service), acceptor_(io_service) 
     , ip_(IP), port_(port) 
    { 
     DEBUG_MSG("Listener Created"); 
    } 

    ~CServer() 
    { 
     DEBUG_MSG("Listener Destroyed"); 
     acceptor_.close(); 
    } 

    void initProtocol() 
    { 
     DEBUG_MSG(" Protocol Initiated"); 
     std::array<unsigned char, 4> ip; 
     std::string delimiter = "."; 

     //Parse the IP String 
     size_t pos = 0; 
     auto i = 0; 
     std::string token; 

     while ((pos = ip_.find(delimiter)) != std::string::npos) { 
      token = ip_.substr(0, pos); 
      ip[i] = std::stoi(token);//what if stoi fails 
      i++; 
      ip_.erase(0, pos + delimiter.length()); 
     } 

     ip[i] = std::stoi(ip_); 


     asio::ip::address_v4 address(ip); 
     tcp::endpoint ep(address, std::stoi(port_)); 


     static std::mutex m; 
     std::unique_lock<std::mutex> lck(m, std::defer_lock); 

     //Critical Section start 
     lck.lock(); 
     acceptor_ = tcp::acceptor(io_, ep);//Creating IOService 
     lck.unlock(); 
     //Critical Section End 

     listen(); 
    } 


    void listen() 
    { 
     DEBUG_MSG("!==============================================================!"); 

     ////Critical Section 
     static std::mutex m; 
     std::lock_guard<std::mutex> lock(m); 

     sessionPtr newSession = std::make_shared<CSerSession>(io_, mng_); 

     try 
     { 
      acceptor_.async_accept(newSession->socket(), std::bind(&CServer::handle_accept, /*shared_from_this()*/ this, newSession, 
       std::placeholders::_1)); 
      ///*asio::error_code ec; 
      //pSocket_->shutdown(asio::ip::tcp::socket::shutdown_send, ec);*/ 
     } 
     catch (const std::bad_weak_ptr& e) 
     { 
      DEBUG_MSG(e.what()); 
      throw e; 
     } 

     DEBUG_MSG("Listen Activated"); 
    } 


    void handle_accept(sessionPtr newSession, const asio::error_code& error) 
    { 
     if (!acceptor_.is_open()) 
     { 
      return; 
     } 

     if (!error) 
     { 
      DEBUG_MSG("Incoming Session accepted"); 
      //Do I need a Lock here? 
      //Critical Section 
      static std::mutex m; 
      std::lock_guard<std::mutex> lock(m); 
      newSession->startSession(); 
      listen(); 
     } 
     else 
     { 
      DEBUG_MSG("Listen_Error"); 
      // //throw ASIOError(Listen_Error); 
      DEBUG_MSG(error.message()); 
      return; 
     } 
    } 

}; 

Мой CSerSessionsManager Class

class CSerSessionsManager{ 
private: 

    std::set<sessionPtr> sessions_; //Active Sessions : Online Info 

public: 
    CSerSessionsManager(); 
    ~CSerSessionsManager(); 

    void addSession(sessionPtr session); 
    void dropSession(sessionPtr session); 
    }; 
CSerSessionsManager::CSerSessionsManager() 
{ 
    DEBUG_MSG("Construction"); 
} 

CSerSessionsManager::~CSerSessionsManager() 
{ 
    DEBUG_MSG("Destruction"); 
} 

void CSerSessionsManager::addSession(sessionPtr session) 
{ 
    DEBUG_MSG("Incoming Session Entry saved"); 
    //Critical Section 
    static std::mutex m; 
    std::lock_guard<std::mutex> lock(m); 
    sessions_.insert(session); 
} 

void CSerSessionsManager::dropSession(sessionPtr session) 
{ 
    //Properly handle Existing connections first shutdown sockets 
    DEBUG_MSG("Session dropped"); 

    //Critical Section 
    static std::mutex m; 
    std::lock_guard<std::mutex> lock(m); 

    std::set<sessionPtr>::iterator it; 
    for (it = sessions_.begin(); it != sessions_.end(); ++it) 
    { 
     if ((*it) == session) 
     { 
      sessions_.erase(session); 
      return; 
     } 
    } 
    //throw ASIOError(Session_Not_Found); 
} 

И мой CSerSession Class

class CSerSession : public std::enable_shared_from_this <CSerSession> { 
private: 

    mutable tcp::socket socket_; // client connection 
    CSerSessionsManager& manager_; 

    std::string ip_; 
    std::string port_; 

    CBuffer msg_; 

public: 
    CSerSession(asio::io_service& io_service, CSerSessionsManager& mng) : 
     manager_(mng), socket_(io_service) 
    { 
     DEBUG_MSG("Server Session Created"); 
    } 

    ~CSerSession() 
    { 
     DEBUG_MSG("Server Session Destroyed"); 
    } 

    void startSession() 
    { 
     DEBUG_MSG("Server Session Started"); 
     //Critical Section 
     static std::mutex m; 
     std::lock_guard<std::mutex> lock(m); 
     manager_.addSession(shared_from_this());//Multiple threads should not try adding section 
     read(msg_); 
    } 

    void handle_read(const asio::error_code& error /*error*/, size_t bytes_transferred /*bytes_transferred*/) 
    { 
     if (!error) 
     { 
      DEBUG_MSG("Read"); 
      //Critical Section 
      static std::mutex m; 
      std::lock_guard<std::mutex> lock(m); 

      read(msg_); 
     } 
     else 
     { 
      DEBUG_MSG("Read Error Detected : " << error.message()); 
      //Check If shared_from_this() is valid or not 
      try 
      { 
       //Check if session was already dropped e.g. server object destroying 
       //i.e. if session object exists 
       DEBUG_MSG("Dropping Session"); 
       //if (error == asio::error::operation_aborted) 
       manager_.dropSession(shared_from_this()); 
      } 
      catch (const std::bad_weak_ptr& e) 
      { 
       DEBUG_MSG(e.what()); 
       throw e; 
      } 
      return; 
     } 
    } 

    void read(CBuffer & buff) 
    { 
     DEBUG_MSG("Read"); 
     asio::async_read(socket_, asio::buffer(const_cast<char *> (buff.getReceived()), buff.buffsize), 
      std::bind(&CSerSession::handle_read, shared_from_this(), 
      std::placeholders::_1, std::placeholders::_2)); 
    } 


    tcp::socket& socket() 
    { 
     //Critical Section 
     static std::mutex m; 
     std::lock_guard<std::mutex> lock(m); 
     return socket_; 
    } 
}; 

Я создаю CServer Object в основной, как показано ниже:

void main() 
{ 
    try 
    { 
     asio::io_service io_service; 
     //CServer server(io_service, "Default", "127.0.0.1", "8000"); 
     auto sPtr = std::make_shared<CServer>(io_service, "127.0.0.1", "8000"); 
     sPtr->initProtocol(); 
     //server.initProtocol(); 
     asio::thread t(boost::bind(&asio::io_service::run, &io_service)); 

    } 
    catch (...) 
    { 
    } 

    system("Pause"); 
} 

Выход Вход я, как показано ниже:

CSerSessionsManager::CSerSessionsManager         : 183 : Construction 
CServer::CServer               : 239 : Listener Created 
CServer::initProtocol              : 250 :  Protocol Initiated 
CServer::listen               : 288 : !==============================================================! 
CSerSession::CSerSession             : 108 : Server Session Created 
CServer::listen               : 309 : Listen Activated 
CServer::~CServer               : 244 : Listener Destroyed 
CSerSessionsManager::~CSerSessionsManager         : 188 : Destruction 
CSerSession::~CSerSession             : 113 : Server Session Destroyed 

Когда CServer Object разрушает связанный CSerSession Object также разрушает , поэтому, возвращаясь из ~CSerSession() Он бросает исключение boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<std::system_error> > at memory location 0x0277F19C. при температуре ниже строк кода:

#ifndef BOOST_EXCEPTION_DISABLE 
    throw enable_current_exception(enable_error_info(e)); 
#else 
    throw e; 
#endif 
} 

Я пытался отладить много, и пытались использовать signal mechanism также, как описано в HTTP Server, но Я застрял здесь и не смог продолжить дальше.

Полный код можно проверить здесь: MyCode

Как решить это?

ответ

3

С фиксированной версии связанного кода: Live On Coliru я

CSerSessionsManager : 184 : Construction 
CServer    : 240 : Listener Created 
initProtocol   : 251 :  Protocol Initiated 
~CServer    : 245 : Listener Destroyed 
~CSerSessionsManager : 189 : Destruction 

Примечание: это потому, что у меня уже было что-то прослушивает порт 8000

(яй для сообщения об ошибках!)

Был ли порядок инициализации полей исправлен? Или в моей системе что-то не работает (из-за состояния гонки на моей более быстрой машине?).

Похоже, последний becuase на Coliru я получил

CSerSessionsManager : 184 : Construction 
CServer    : 240 : Listener Created 
initProtocol   : 251 :  Protocol Initiated 
listen     : 289 : !===================================! 
CSerSession   : 109 : Server Session Created 
listen     : 310 : Listen Activated 
~CServer    : 245 : Listener Destroyed 
~CSerSessionsManager : 189 : Destruction 
~CSerSession   : 114 : Server Session Destroyed 

Итак, давайте поближе:

  1. почему вы разбор строке IP? Вот что такое address_v4 для. И ip::tcp::resolver.

    DEBUG_MSG(" Protocol Initiated"); 
    asio::ip::address_v4 address = asio::ip::address_v4::from_string(ip_); 
    tcp::endpoint ep(address, std::stoi(port_)); 
    
  2. с использованием static mutex редко бывает полезна.Вы имели в виду синхронизацию доступа к общим ресурсам? Тогда вам также нужен общий мьютекс

  3. Почему вы используете defer-lock? Использование прицелы

    { 
        //Critical Section start 
        std::lock_guard<std::mutex> lck(mutex_); 
        acceptor_ = tcp::acceptor(io_, ep);//Creating IOService 
        //Critical Section End 
    } 
    
  4. основная нить просто выходит, никогда не присоединиться к Ио нити. По крайней мере, присоединиться. Или сделать это правильно завершать работу перед завершением программы:

    t.join(); 
    
  5. венгерского именование действительно бесполезно здесь. sPtr ничего мне не говорит. server или, если вы настаиваете, server_ptr - это то, что вам нужно знать.

  6. у вас есть вне границ написать здесь:

    received_[str.size()] = '\0'; 
    

    вы хотели

    received_[len] = '\0'; 
    
  7. ваш empty не нужно петли

    bool empty() const 
    { 
        return !received_[0]; 
    } 
    
  8. почему вы зацикливаете, чтобы найти материал в упорядоченном наборе?

    std::set<sessionPtr>::iterator it; 
    for (it = sessions_.begin(); it != sessions_.end(); ++it) 
    { 
        if ((*it) == session) 
        { 
         sessions_.erase(session); 
         return; 
        } 
    } 
    

    должно быть

    sessions_.erase(session); 
    
  9. addSession/dropSession внутренне замок; вам не нужно устанавливать доступ к ним в критической секции

  10. throw e - это противотанковое устройство; только throw; - это повторный бросок

  11. У вас есть избыточная трассировка почти везде (для этого нужны отладчики). Например. DEBUG_MSG("Read")

  12. Блокировка здесь является фальшивкой:

    tcp::socket& socket() 
    { 
        // Critical Section 
        std::lock_guard<std::mutex> lock(mutex_); 
        return socket_; 
    } 
    

    Ссылка, возвращаемая не будет защищена в любом случае, и гнездо только инициализируется один раз.

  13. все запирающий нить кажется излишним, так как есть только одна служба нить

  14. CBuffer msg является поддельным параметр read() в тот же буфер передается всегда. Это может быть довольно хорошо (это в том же сеансе), поэтому просто используйте его.

  15. этого

    acceptor_ = tcp::acceptor(io_, ep); 
    

    должно быть

    acceptor_.bind(ep); 
    

    , а не в критической секции (сервер только создается один раз); Следовательно, функция initProtocol может быть

    void initProtocol() 
    { 
        acceptor_.bind(tcp::endpoint(asio::ip::address_v4::from_string(ip_), std::stoi(port_))); 
        listen(); 
    } 
    
  16. в listen вы ловли bad_weak_ptr, который не может даже произойти

  17. здесь:

    //Do I need a Lock here? 
    //Critical Section 
    std::lock_guard<std::mutex> lock(mutex_); 
    newSession->startSession(); 
    

    вам не нужно замок. newSession был связан с локальной переменной. Это невозможно для совместного использования, если вы не скопировали обработчик завершения (вы этого не сделали).

Вот более устроились версия:

Live On Coliru

#include <iostream> 
#include <boost/asio.hpp> 
#include <memory> 
#include <deque> 
#include <set> 
#include <iomanip> 
#include <mutex> 
#include <boost/bind.hpp> 
#include <boost/thread.hpp> 

#define DEBUG ON 

#ifdef DEBUG 
#define DEBUG_MSG(str) do {std::cout << std::setw(75) << std::left << __FUNCTION__ \ 
    << std::setw(3) << std::left << ":" << std::setw(5) << std::left << __LINE__ \ 
    << std::setw(5) << std::left << ":"\ 
    << std::left << str \ 
    << std::endl;} while(false) 
#else 
#define DEBUG_MSG(str) do { } while (false) 
#endif 

namespace asio = boost::asio; 
using asio::ip::tcp; 

class CSerSession; 
using sessionPtr = std::shared_ptr<CSerSession>; 

class CSerSessionsManager { 
    private: 
    mutable std::mutex mutex_; 
    std::set<sessionPtr> sessions_; // Active Sessions : Online Info 

    public: 
    CSerSessionsManager(); 
    ~CSerSessionsManager(); 

    void addSession(sessionPtr session); 
    void dropSession(sessionPtr session); 
}; 

class CBuffer { 

    public: 
    enum { buffsize = 32 }; 

    private: 
    char received_[buffsize]; 

    public: 
    CBuffer() : received_{} {} 

    CBuffer(const std::string str) 
    { 
     // Truncate if Overflow 
     auto len = str.size(); 
     if (len >= buffsize) { 
      len = buffsize - 1; 
     } 
     std::copy(str.begin(), str.begin() + len, received_); 
     received_[len] = '\0'; 
    } 

    bool empty() const 
    { 
     return !received_[0]; 
    } 
    const std::string getString() const { return std::string(received_); } 
    const char* getReceived() const { return received_; } 
}; 

class CSerSession : public std::enable_shared_from_this<CSerSession> { 
    private: 
    mutable std::mutex mutex_; 
    mutable tcp::socket socket_; // client connection 
    CSerSessionsManager& manager_; 

    std::string ip_; 
    std::string port_; 

    CBuffer msg_; 

    public: 
    CSerSession(asio::io_service& io_service, CSerSessionsManager& mng) : socket_(io_service), manager_(mng) 
    { 
     DEBUG_MSG("Server Session Created"); 
    } 

    ~CSerSession() { DEBUG_MSG("Server Session Destroyed"); } 

    void startSession() 
    { 
     DEBUG_MSG("Server Session Started"); 
     manager_.addSession(shared_from_this()); // Multiple threads should not try adding section 

     read(); 
    } 

    tcp::socket& socket() { return socket_; } 
    private: 
    void handle_read(const boost::system::error_code& error /*error*/, size_t /*bytes_transferred*/) 
    { 
     if (!error) { 
      read(); 
     } else { 
      DEBUG_MSG("Read Error Detected : " << error.message()); 
      manager_.dropSession(shared_from_this()); // might throw 
     } 
    } 

    void read() 
    { 
     std::lock_guard<std::mutex> lock(mutex_); 
     DEBUG_MSG("Read"); 
     asio::async_read(socket_, asio::buffer(const_cast<char*>(msg_.getReceived()), msg_.buffsize), 
         std::bind(&CSerSession::handle_read, shared_from_this(), std::placeholders::_1, std::placeholders::_2)); 
    } 
}; 


CSerSessionsManager::CSerSessionsManager() 
{ 
    DEBUG_MSG("Construction"); 
} 

CSerSessionsManager::~CSerSessionsManager() 
{ 
    DEBUG_MSG("Destruction"); 
} 

void CSerSessionsManager::addSession(sessionPtr session) 
{ 
    std::lock_guard<std::mutex> lock(mutex_); 
    DEBUG_MSG("Incoming Session Entry saved"); 
    sessions_.insert(session); 
} 

void CSerSessionsManager::dropSession(sessionPtr session) 
{ 
    std::lock_guard<std::mutex> lock(mutex_); 
    DEBUG_MSG("Session dropped"); 
    sessions_.erase(session); 
} 

class CServer { 
private: 
    mutable std::mutex mutex_; 
    asio::io_service& io_; 
    mutable tcp::acceptor acceptor_; // only in the listener 
    CSerSessionsManager mng_; 
public: 

    CServer(asio::io_service& io_service, const std::string& IP, int port) 
     : io_(io_service), acceptor_(io_, tcp::endpoint(asio::ip::address::from_string(IP), port)) 
    { 
     DEBUG_MSG("Listener Created"); 
    } 

    ~CServer() 
    { 
     DEBUG_MSG("Listener Destroyed"); 
     acceptor_.close(); // likely to be redundant 
    } 

    void initProtocol() 
    { 
     listen(); 
    } 

    private: 
    void listen() 
    { 
     DEBUG_MSG("!==============================================================!"); 

     sessionPtr newSession = std::make_shared<CSerSession>(io_, mng_); 

     std::lock_guard<std::mutex> lock(mutex_); 
     acceptor_.async_accept(newSession->socket(), std::bind(&CServer::handle_accept, this, newSession, 
        std::placeholders::_1)); 
    } 

    void handle_accept(sessionPtr newSession, const boost::system::error_code& error) 
    { 
     if (error || !acceptor_.is_open()) { 
      DEBUG_MSG("Listen_Error"); 
      DEBUG_MSG(error.message()); 
      return; 
     } 

     DEBUG_MSG("Incoming Session accepted"); 
     newSession->startSession(); 
     listen(); 
    } 
}; 

int main() 
{ 
    try 
    { 
     asio::io_service io_service; 
     auto server = std::make_shared<CServer>(io_service, "127.0.0.1", 8973); 
     server->initProtocol(); 
     boost::thread t(boost::bind(&asio::io_service::run, &io_service)); 

     boost::this_thread::sleep_for(boost::chrono::seconds(3)); 

     t.join(); 
    } 
    catch (...) 
    { 
    } 
} 

печать (для одного подключения):

CSerSessionsManager : 123 : Construction 
CServer    : 156 : Listener Created 
listen    : 173 : !==============================================================! 
CSerSession   : 86 : Server Session Created 
handle_accept  : 190 : Incoming Session accepted 
startSession  : 93 : Server Session Started 
addSession   : 134 : Incoming Session Entry saved 
read    : 114 : Read 
listen    : 173 : !==============================================================! 
CSerSession   : 86 : Server Session Created 
handle_read   : 106 : Read Error Detected : End of file 
dropSession   : 141 : Session dropped 
~CSerSession  : 89 : Server Session Destroyed 
+0

Общая информация: [очищены версия кода] (http: // p aste.ubuntu.com/10280512/) ** 178 строк ** короче [вашего связанного кода] (http://pastie.org/9956768). – sehe

+0

Ссылаясь на вашу первую фиксированную версию кода, и она по-прежнему выдает исключение, когда вызывается '~ CSerSession()' (VS13), то есть когда 'CServer()' выходит за пределы области видимости. Более поздний 'более фиксированный код версии' не вызывает' ~ CServer() ', поскольку' t.join() 'не позволяет' CServer() 'уничтожить объект. Мое требование - создавать и уничтожать экземпляры 'CServer(), когда это необходимо. Но после успешного запуска 'async.accept()' оно бросает исключение, когда объект разрушается, т. Е. В базовом '~ CSerSession()'. Как обеспечить чистое уничтожение для 'CServer'. Я застрял здесь и не смог продолжить дальше. Можете ли вы помочь? –

+0

't.join()', конечно же, не предотвращает разрушение CServer. Он просто гарантирует, что это произойдет, когда это необходимо. Вы не поняли, что вы «CServer» - это обычный класс? Он не использует 'enable_shared_from_this', и, конечно же, это означает, что вы должны явно управлять своей жизнью. – sehe

Смежные вопросы