0
  • 平台: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在它自己的线程内部运行并使用输出队列来管理它的活动。但是,我喜欢与udp_server. 例如,udp_server对象在向量中跟踪客户端。当deadline_timer到期时,我会循环访问已知的客户端并向他们发送消息。然后我重新启动计时器。这使得我的响应独立于发送到服务器的 udp 数据包。因此,当数据包到达时,它们会被放入队列中,等待处理的另一部分。然后将后面的数据放在输出队列中,然后deadline_timer处理这些响应并将它们发送给适当的客户端。

所以我的主要问题是:

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

4

1 回答 1

0

好吧,我真的很愚蠢地想着这个。

  • 首先,deadline_timer 需要完全在我希望它计时的线程内。这意味着它需要在线程内创建。
  • 其次,我需要定义线程循环中调用的函数,而不是将其设置为 io_service::run 函数。所以我把它做成了 udp_server::start 函数。在 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");
        }    
    }

现在在udp_thread 中创建deadline_timer。在主线程中创建deadline_timer 对象会导致程序无法正常加载。

于 2013-03-20T03:40:40.697 回答