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

C++ asio如何从客户端异步读取数据并定期写入数据(如果有的话),c++,boost,boost-asio,C++,Boost,Boost Asio,我是boost::asio的初学者,请帮助我 我需要写单线程TCP服务器。服务器应接受客户端连接,并持续从客户端套接字读取输入数据。服务器应定期向客户端发送数据。所以我有一个问题——所有的例子都描述了当我们总是有循环时的情况 异步接收() on_receive()->async_write() on_write()->转到1:) 所以我的决定是使用定时器来检查要发送到套接字的数据 我编写了测试服务器,有一个非常奇怪的行为——如果客户端连接,做一些事情,并在一段时间内一个接一个地断开连接,那么工作

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

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

  • 异步接收()
  • on_receive()->async_write()
  • on_write()->转到1:)
  • 所以我的决定是使用定时器来检查要发送到套接字的数据

    我编写了测试服务器,有一个非常奇怪的行为——如果客户端连接,做一些事情,并在一段时间内一个接一个地断开连接,那么工作正常。但如果所有客户端同时断开连接,我就 计时器处理程序尝试使用已销毁对象的成员类(锁定关键部分)时的情况

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

    [这段视频展示了它是如何复制的](“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 ;
    
    
    }
    
    #包括
    #包括
    #包括
    #包括
    #包括
    #包括
    使用名称空间boost::asio;
    使用名称空间boost::posix_time;
    类连接;
    typedef boost::shared_ptr CIncommingConnectionPtr;
    结构IIncomingServer
    {
    虚拟void OnData(CIncommingConnectionPtr pConn,const char*pData,size_t bytes)=0;
    连接的虚拟空位(CIncommingConnectionPtr pConn)=0;
    虚拟连接无效(const boost::system::error\u code&err,CIncommingConnectionPtr pConn)=0;
    };
    尾锁类
    {
    公众:
    尾锁(关键截面和cs):
    m_cs(cs)
    {
    ::EnterCriticalSection(&m_-cs);
    }
    ~CAutoLock()
    {
    ::离开关键部门(&m_-cs);
    }
    私人:
    关键工段和机械控制系统;
    };
    类CIncommingConnection:public boost::从\u启用\u共享\u
    ,boost::不可复制
    {
    公众:
    CIncommingConnection(const std::string sPeerName,boost::asio::io_服务和服务,IIncomingServer*pServer):
    m_服务(服务)
    ,短袜(服务)
    ,m_sPeerName(sPeerName)
    ,m_pServer(pServer)
    ,m_定时器(服务)
    {
    ::初始化CriticalSection(&m_-cs);
    std::cout sock(),boost::bind(&CIncomingServer::handle_accept,this,_1));
    m_timer.expires_from_now(boost::posix_time::毫秒(500));
    异步等待(boost::bind(&CIncomingServer::on_timer,this));
    }
    私人:
    无效句柄\u接受(const boost::system::error\u code&err)
    {
    m_clients.back()->start();
    CIncommingConnectionPtr pConnNew(新的CIncommingConnection(m_sPeerName,m_service,this));
    m_客户端。推回(PCONNEW);
    异步接受(pConnNew->sock(),boost::bind(&CIncomingServer::handle_accept,this,_1));
    }
    //IIncomingServer
    虚拟void OnData(CIncommingConnectionPtr pConn,const char*pData,size\u t字节)
    {
    
    std::cout长话短说:您应该将完成处理程序绑定到从\u this()
    返回的共享\u ptr,而不是普通的
    this
    (所谓的
    shared\u this
    习惯用法)。这样可以确保正确自动管理连接对象的寿命

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

  • 计时器取消(异步操作)删除
  • CIncommingConnectionPtr
    来自容器(同步 操作)

  • 在第(2)点,连接被破坏,因为没有其他的
    共享\u ptr
    持有它。现在…崩溃了!

    谢谢你的回答!看起来它是正确的,但我不能从中得出正确的结论:-(在从这个()传递共享\u之后)相反,这是异步处理程序。我的程序不再崩溃,但并不是所有CIncommingConnection实例都被破坏。看起来我错过了一些引用,对象仍然活着。我今天将尝试调查这种情况!非常感谢!@user1503944好吧,一看,我没有看到任何“永恒的”
    shared\u ptr
    s出现在上面的代码中(即使在绑定到
    shared\u from\u this
    )。也许,您的真实代码与您发布的代码不同。(顺便说一句,我建议您避免显式锁定缓冲区-相反,您可以将任何与缓冲区相关的代码发布到
    io\u服务
    )@user1503944:简单看一下代码,就可以知道
    CIncommingConnection::on\u output\u queue\u timer()
    调用链可能正在维护引用。即使
    timer.cancel(),调用链也可能会继续有关此行为的详细信息,请参阅备注。要解决此问题,请考虑在<代码> OnOutOutPuthQueLeGyTimeR()/<代码>中出错或如果<代码> Sokky.ISSUN OPEN()/代码>为false。@ Tanner Sansbury,但随着计时器取消,他调用“代码> Sokky.CuteleFor”<代码>,因此任何后续I/O在代码> Sokku无论如何都会失败。(并断开链)。@IgorR.
    wait_-for_-outgoingdata()
    链仅在
    m_-sOutBuf
    不为空时尝试对
    sock\u进行I/O操作;但是,
    m_-sOutBuf
    在发布的代码中显示为空。当
    sock\u>打开时断开链()
    is false似乎是一个合理的谓词,可以保证在调用
    stop()
    后链断开。-1.这是一个吗?我怀疑。对不起,Abyx-但我不同意你的怀疑。例如,你认为在这个示例中,什么样的细节不重要?但感谢你的批评!