ASIO聊天会话类在销毁时抛出异常:C ++ ASIO

时间:2015-02-17 14:45:00

标签: c++ boost-asio

我正在使用ASIO处理网络应用程序,并已提及Chat-Server/Client

我问了类似问题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_;
    }
};

我在main中创建了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

我该如何解决?

1 个答案:

答案 0 :(得分:3)

从链接代码的固定版本开始:Live On Coliru

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

注意:这是因为我已经在端口8000上监听了某些内容(yay用于错误报告!)

字段的初始化顺序是否修复了它?或者我的系统上有没有运行的东西(因为我的速度更快的机器上存在竞争条件?)。

看起来后者因为我得到了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. 你为什么要使用延迟锁?使用范围

    {
        //Critical Section start
        std::lock_guard<std::mutex> lck(mutex_);
        acceptor_ = tcp::acceptor(io_, ep);//Creating IOService
        //Critical Section End
    }
    
  4. 主线程刚刚退出,从未加入io线程。至少加入。或者在终止程序之前使其正确关闭:

    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_;
    }
    

    无论如何都不会保护返回的引用,socket只会被初始化一次。

  13. 所有线程锁定似乎都是多余的,因为只有一个服务线程

  14. CBuffer msgread()的伪造参数,因为始终传递相同的缓冲区。这可能很好(它在同一个会话中),所以,只需使用它。

  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绑定了一个局部变量。除非你复制完成处理程序(你没有),否则它不可能被共享。

  18. 这是一个更加固定的版本:

    <强> 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