OBSE和Boost.Asio:在同一个io_service上使用deadline_timer的线程异步UDP服务器

时间:2013-03-19 20:30:42

标签: multithreading plugins boost udp boost-asio

  • 平台:Windows 7 Professional 64位
  • 编译器:VS2010 Express
  • 提升:版本1.49
  • 插件系统:OBSE 20(Bethesda的Oblivion游戏)

我有一个基于异步udp示例的类。我将io服务本身作为一个线程运行。这是该类的代码:

    // udp buffer queues
    extern concurrent_queue<udp_packet> udp_input_queue; // input from external processes
    extern concurrent_queue<udp_packet> udp_output_queue; // output to external processes

    using boost::asio::ip::udp;
    class udp_server
    {
    public:
        udp_server(boost::asio::io_service& io_service, short port)
            : io_service_(io_service),
              socket_(io_service_, udp::endpoint(boost::asio::ip::address_v4::from_string(current_address), port))//, // udp::v4()  
        {
            // start udp receive
            socket_.async_receive_from(
                boost::asio::buffer(recv_buf), sender_endpoint_,
                boost::bind(&udp_server::handle_receive_from, this,
                  boost::asio::placeholders::error,
                  boost::asio::placeholders::bytes_transferred));  

            send_timer_ = NULL;            
        }

        ~udp_server(){
            io_service_.stop();
            if(send_timer_){
                send_timer_->cancel();
                delete send_timer_;
            }
        }

        void start(){
            // start send timer                
            send_timer_ = new boost::asio::deadline_timer(io_service_, boost::posix_time::milliseconds(500));
            send_timer_restart();
        }

        void handle_send_to(const boost::system::error_code& error, size_t bytes_recvd);
        void handle_receive_from(const boost::system::error_code& error, size_t bytes_recvd);

        //void handle_send_timer(const boost::system::error_code& error);
        void handle_send_timer();
        void send_timer_restart();

        void stop()
        {
            io_service_.stop();
        }

        private:
            boost::asio::io_service& io_service_;
            udp::socket socket_;
            udp::endpoint sender_endpoint_; 
            std::vector<udp::endpoint> clientList;
            //std::auto_ptr<boost::asio::io_service::work> busy_work;
            udp_buffer recv_buf;
            boost::asio::deadline_timer* send_timer_;
    };

现在我像这样实例化类和线程:

    udp_server *udp_server_ptr=NULL;
    boost::asio::deadline_timer* dlineTimer=NULL;
    static void PluginInit_PostLoadCallback()
    {   
        _MESSAGE("NetworkPipe: PluginInit_PostLoadCallback called");

        if(!g_Interface->isEditor)
        {
            _MESSAGE("NetworkPipe: Starting UDP");
            udp_server_ptr = new udp_server(io_service, current_port);
            //dlineTimer = new boost::asio::deadline_timer(io_service);
            udp_thread = new boost::thread(boost::bind(&boost::asio::io_service::run, &io_service));         
            //
            _MESSAGE("NetworkPipe: UDP Started");
            NetworkPipeEnable = true;
        }
        else
        {
            _MESSAGE("NetworkPipe: Running in editor, not starting UDP");
        }    
    }

现在请注意,dlineTimer在上面已注释掉了。如果我启用它就会停止运行。我可以让dlineTimer与此io服务一起使用的唯一方法是在udp_server::handle_receive_from调用期间创建它。我认为这是因为它在另一个线程内部运行。因此,由于某种原因,deadline_timer对象不喜欢在它需要在内部运行的线程之外创建。

现在,为了与主线程进行通信,我使用了concurrent_queue个对象。所以这些允许我非常简单地将消息发送进出线程。理论上我可以在自己的线程中运行dlineTimer并使用输出队列来管理它的活动。但是,我喜欢has的简单性与udp_server在同一个线程中。例如,udp_server对象跟踪向量中的客户端。当deadline_timer到期时,我循环通过已知客户端并向其发送消息。然后我重新启动计时器。这使我的响应独立于发送到服务器的udp数据包。因此,当数据包到达时,它们将被放入队列中以进行另一部分过程。然后,稍后的数据被放置在输出队列中,deadline_timer处理这些响应并将它们发送给适当的客户端。

所以我的主要问题是:

如何使用与deadline_timer对象相同的线程和io_service更干净地创建udp_server对象?

1 个答案:

答案 0 :(得分:0)

好的,我真的很傻。

  • 首先,deadline_timer需要完全位于我希望它进入的线程内。这意味着它需要在线程内部创建。
  • 其次我需要定义线程循环中调用的函数,而不是将其设置为io_service :: run函数。所以我把它变成了udp_server :: start函数。在开始通话中,我创建了deadline_timer。

所以这是课程:

    class udp_server
    {
    public:
        udp_server(boost::asio::io_service& io_service, short port)
            : io_service_(io_service),
              socket_(io_service_, udp::endpoint(boost::asio::ip::address_v4::from_string(current_address), port))//, // udp::v4()  
        {
            // start udp receive
            socket_.async_receive_from(
                boost::asio::buffer(recv_buf), sender_endpoint_,
                boost::bind(&udp_server::handle_receive_from, this,
                  boost::asio::placeholders::error,
                  boost::asio::placeholders::bytes_transferred));  

            send_timer_ = NULL;            
        }

        ~udp_server(){
            io_service_.stop();
            if(send_timer_){
                send_timer_->cancel();
                delete send_timer_;
            }
        }

        void start();

        void startSendTimer();

        void handle_send_to(const boost::system::error_code& error, size_t bytes_recvd);
        void handle_receive_from(const boost::system::error_code& error, size_t bytes_recvd);

        void handle_send_timer();
        void send_timer_restart();

        void stop()
        {
            io_service_.stop();
        }

        private:
            boost::asio::io_service& io_service_;
            udp::socket socket_;
            udp::endpoint sender_endpoint_; 
            std::vector<udp::endpoint> clientList;               
            udp_buffer recv_buf;
            boost::asio::deadline_timer* send_timer_;                
    };

以下是相关功能:

    void udp_server::start(){
        // startup timer
        startSendTimer();

        // run ioservice
        io_service_.run();
    }

    void udp_server::startSendTimer(){            
        // start send timer 
        if(!send_timer_)
            send_timer_ = new boost::asio::deadline_timer(io_service_, boost::posix_time::milliseconds(500));
        send_timer_restart();
    }

    void udp_server::send_timer_restart(){    
        if(send_timer_){
            // restart send timer
            send_timer_->expires_from_now(boost::posix_time::milliseconds(500));
            send_timer_->async_wait(boost::bind(&udp_server::handle_send_timer, this));
        }
    }        

    void udp_server::handle_send_timer(){            
        for(std::vector<udp::endpoint>::iterator itr = clientList.begin(); itr != clientList.end(); ++itr){
            socket_.async_send_to(
                boost::asio::buffer("heart beat", strlen("heart beat")), *itr,              
                boost::bind(&udp_server::handle_send_to, this,
                boost::asio::placeholders::error,
                boost::asio::placeholders::bytes_transferred));
        }

        send_timer_restart(); 
    }

所以我首先想到这一切都是错的。我需要定义线程开始执行的起点。我可以创建需要驻留在线程内的该线程中的对象。

udp_server现在就像这样启动:

    static void PluginInit_PostLoadCallback()
    {   
        _MESSAGE("NetworkPipe: PluginInit_PostLoadCallback called");

        if(!g_Interface->isEditor)
        {
            _MESSAGE("NetworkPipe: Starting UDP");
            udp_server_ptr = new udp_server(io_service, current_port);                 
            udp_thread = new boost::thread(boost::bind(&udp_server::start, udp_server_ptr));         
            _MESSAGE("NetworkPipe: UDP Started");
            NetworkPipeEnable = true;
        }
        else
        {
            _MESSAGE("NetworkPipe: Running in editor, not starting UDP");
        }    
    }

deadline_timer创建现在在udp_thread内发生。在主线程中创建deadline_timer对象将导致程序无法正确加载。