boost :: asio如何从客户端异步读取数据并定期写入数据(如果有的话)

时间:2013-10-02 12:15:32

标签: c++ boost boost-asio

我是boost :: asio的初学者,所以请帮助我。

我需要编写单线程TCP服务器。服务器应接受客户端连接并连续从客户端套接字读取输入数据。服务器应定期向客户端发送数据。所以我遇到了一些问题 - 所有例子都描述了我们总是有循环的情况

  1. async_receive()
  2. on_receive() - > ASYNC_WRITE()
  3. on_write() - >转到1:)
  4. 所以我决定使用timer来检查要发送到套接字的数据。

    我写了测试服务器,并且有非常奇怪的行为 - 如果客户端连接,执行某些操作并且使用一些时间增量一个接一个地断开连接,它可以正常工作。但如果所有客户端同时断开连接我都有 计时器处理程序尝试使用已经DESTROYED对象的成员类时的情况(锁定关键部分)。

    我无法描述为什么!请帮忙!

    [此视频展示了如何复制](http://www.youtube.com/watch?v=NMWkD7rqf7Y&feature=youtu.be“1080p”)

    谢谢!

    #include <boost/none.hpp>
    #include <boost/bind.hpp>
    #include <boost/asio.hpp>
    #include <boost/shared_ptr.hpp>
    #include <boost/enable_shared_from_this.hpp>
    
    #include <iostream>
    
    
    
    
    using namespace boost::asio;
    using namespace boost::posix_time;
    
    
    
    class CIncommingConnection ;
    typedef boost::shared_ptr<CIncommingConnection> CIncommingConnectionPtr;
    
    
    struct IIncomingServer
    {
        virtual  void OnData(CIncommingConnectionPtr pConn, const char *pData, size_t bytes) = 0;
        virtual  void OnConnected(CIncommingConnectionPtr pConn) = 0;
        virtual  void OnDisconnected(const boost::system::error_code& err, CIncommingConnectionPtr pConn) = 0;
    };
    
    
    
    class CAutoLock
    {
    public:
        CAutoLock(CRITICAL_SECTION &cs)  :
          m_cs(cs)
        {
            ::EnterCriticalSection(&m_cs);
        }
    
       ~CAutoLock()
       {
           ::LeaveCriticalSection(&m_cs);
       }
    
    private:
        CRITICAL_SECTION &m_cs;
    };
    
    class CIncommingConnection :  public boost::enable_shared_from_this<CIncommingConnection>                      
                                 ,boost::noncopyable 
    {
    public:
    
        CIncommingConnection(const std::string sPeerName, boost::asio::io_service  &service, IIncomingServer *pServer) :
         m_service(service)
        ,sock_(service)
        ,m_sPeerName(sPeerName) 
        ,m_pServer(pServer)
        ,m_timer(service)
        {
            ::InitializeCriticalSection(&m_cs);
    
            std::cout << "CIncommingConnection()"  << std::endl ;
        }
    
    
        ~CIncommingConnection()
        {
            std::cout << "CIncommingConnection()~"  << std::endl ;
            ::DeleteCriticalSection(&m_cs);
        }
    
    
        ip::tcp::socket & sock()
        {
            return sock_;
        }
    
    
    
        void start()
        {
            m_pServer->OnConnected(shared_from_this());
            do_read();
            wait_for_outgoingdata();
        }
    
    
    private:
    
        void stop()
        {      
            sock_.close();
            m_timer.cancel();
        }
    
    
    
        void do_read()
        {
            sock_.async_receive(buffer(read_buffer_), boost::bind(&CIncommingConnection::handler_read, this, _1, _2) );
        }
    
    
    
        void do_error(const boost::system::error_code& error)
        {
            CIncommingConnectionPtr pConn = shared_from_this();
    
            stop() ;
    
            m_pServer->OnDisconnected(error, pConn);
        }
    
    
    
        void handler_read(const boost::system::error_code& error, std::size_t bytes)
        {
            if (error)
            {
                do_error(error);
                return ;
            }
    
            CIncommingConnectionPtr pConn = shared_from_this() ;
    
            m_pServer->OnData(pConn, read_buffer_, bytes);
    
            do_read();
        }
    
    
    
        void wait_for_outgoingdata()
        {
            m_timer.expires_from_now( boost::posix_time::millisec( 100 ) );
            m_timer.async_wait( boost::bind( &CIncommingConnection::on_output_queue_timer, this, _1 ) );
        }
    
    
    
        void on_output_queue_timer(const boost::system::error_code& error)
        {
            if (error == boost::asio::error::operation_aborted)
            {
                return ;
            }
    
            CAutoLock oLock(m_cs);
    
            if (!m_sOutBuf.empty())
                sock_.async_send(buffer(m_sOutBuf), boost::bind(&CIncommingConnection::handler_write, this, _1, _2) );
            else
                wait_for_outgoingdata();
        }
    
    
        void handler_write(const boost::system::error_code& error, std::size_t bytes)
        {    
            if (error)
                return ;
    
    
            if (bytes)
            {
                m_sOutBuf = m_sOutBuf.substr(bytes, m_sOutBuf.length()-bytes);
            }
    
            wait_for_outgoingdata();
        }
    
    
    
    private:
        ip::tcp::socket sock_;
    
        enum { max_msg = 1024 };
        char read_buffer_[max_msg];
        char write_buffer_[max_msg];
    
    
        boost::asio::io_service        &m_service ;   
        std::string                     m_sPeerName ;
        std::string                     m_sOutBuf;
        CRITICAL_SECTION                m_cs ;
        IIncomingServer                *m_pServer;
        boost::asio::deadline_timer     m_timer;
    };
    
    
    
    
    
    
    class CIncomingServer :   public boost::enable_shared_from_this<CIncomingServer>                      
                             , public IIncomingServer
                             , boost::noncopyable 
    {
    
    public:
    
        CIncomingServer(boost::asio::io_service  &service,
            unsigned int port,
            bool bAllowManyConnections,
            const std::string sPeerName) :
    
          m_acceptor (service, ip::tcp::endpoint(ip::tcp::v4(), port), false)
         ,m_sPeerName(sPeerName)
         ,m_port(port) 
         ,m_service(service)
         ,m_timer(service)
         ,m_bAllowManyConnections(bAllowManyConnections)
        {
        }
    
    
    
        ~CIncomingServer()
        {
        }
    
    
    
        void run()
        {
            CIncommingConnectionPtr pConn (new CIncommingConnection(m_sPeerName, m_service, this));
            m_clients.push_back( pConn );
    
    
            m_acceptor.async_accept(pConn->sock(), boost::bind(&CIncomingServer::handle_accept, this, _1));
    
            m_timer.expires_from_now( boost::posix_time::millisec( 500 ) );
            m_timer.async_wait( boost::bind( &CIncomingServer::on_timer, this ) );
        }
    
    
    
    
    private:
    
        void handle_accept(const boost::system::error_code & err)
        {
            m_clients.back()->start();
    
            CIncommingConnectionPtr pConnNew (new CIncommingConnection(m_sPeerName, m_service, this));
            m_clients.push_back( pConnNew );
    
            m_acceptor.async_accept(pConnNew->sock(), boost::bind(&CIncomingServer::handle_accept, this,  _1));
        }
    
    
        //IIncomingServer
        virtual  void OnData(CIncommingConnectionPtr pConn, const char *pData, size_t bytes)
        {
            std::cout << "Data received" << std::endl ;
        }
    
    
        virtual  void OnConnected(CIncommingConnectionPtr pConn)
        {
            std::cout << "Client connected" << std::endl ;
        }
    
    
        virtual  void OnDisconnected(const boost::system::error_code& err, CIncommingConnectionPtr pConn)
        {
            std::cout << "Client disconnected" << std::endl ;
    
            auto it = std::find(m_clients.begin(), m_clients.end(), pConn) ;
            if (it != m_clients.end())
            {
                m_clients.erase(it);
            }
    
        }
    
    
    
        void on_timer()
        {
            //if (NeedTerminate())
            //{
            //    m_service.stop();
            //    return ;
            //}
    
            m_timer.expires_from_now( boost::posix_time::millisec( 500 ) );
            m_timer.async_wait( boost::bind( &CIncomingServer::on_timer, this ) );
        }
    
    
    
    private:
        ip::tcp::acceptor  m_acceptor ;
    
        std::vector<CIncommingConnectionPtr> m_clients;
        std::string m_sPeerName ;
        unsigned int m_port ;
        boost::asio::io_service       &m_service ;
        boost::asio::deadline_timer    m_timer;
        bool                           m_bAllowManyConnections; 
    };
    
    
    int _tmain(int argc, _TCHAR* argv[])
    {
    
        boost::asio::io_service  service ;
    
    
        boost::shared_ptr<CIncomingServer> pServer;
    
        try
        {
            pServer.reset( new CIncomingServer(service, 8000,  false, "BS Server"));        
            pServer->run();
        }
        catch (const boost::system::system_error &err)
        {
            std::cout << "Error : " << err.what() << std::endl ;
            return 0 ;
        }
    
        service.run();
    
        return 0 ;
    
    
    }
    

1 个答案:

答案 0 :(得分:2)

长话短说:您应该将完成处理程序绑定到从shared_from_this()返回的shared_ptr,而不是简单this(所谓的shared_from_this成语)。这样可以确保正确自动管理连接对象的生命周期。

从技术上讲,现在发生以下情况:do_error导致2个操作发生:

  1. 计时器取消(即异步操作)删除
  2. 来自容器的
  3. CIncommingConnectionPtr(同步 操作)。
  4. 在第(2)点,连接被破坏,因为没有其他shared_ptr持有它。现在是timer completion handler comes ......崩溃!