• win32下boost::asio进一步封装


    在网络通信中,我个人比较喜欢异步的方式。这样我程序就不会因为I/O的读写而导致线程阻塞。理想的工作方式是通知窗口的事件通知。windows中socket的底层其实是支持窗口事件通知的,但由于boost库比较强大,我就基于asio的库来实现这样的机制。

    由于是异步方式,当事件处理完成后,我希望将结果传递给回调函数,因此类中有下面3个函数:

        virtual void handler_connect(const boost::system::error_code& error);
        virtual void handler_send(const boost::system::error_code& error);
        virtual void handler_receive(const boost::system::error_code& error);

    参数传递过来的只是是否有错误。

    在主线程中,我只需要调用

    void connect();
    
    template<typename ConstBufferSequence>
    void send(const ConstBufferSequence& buffers);
    
    template<typename MutableBufferSequence>
    void receive(const MutableBufferSequence& buffers)

    这三个函数即可,处理完成后会自动调用上面的回调函数,使用回调函数来处理结果。

    asio中io_service的run会一直阻塞线程,所以需要将run在辅助线程中运行,但这样的话,回调函数就会在辅助线程中执行,为了保证线程安全性和消除MFC中不同线程执行后的代码异常,我需要将回调函数转入main线程中执行,这就应用了SendMessage函数了通知主线程窗口,下面详细的代码:

    #define WM_ASIO_MESSAGE (WM_USER + 1)
    
    using boost::asio::ip::tcp;
    using boost::asio::deadline_timer;
    
    template <typename CWinWnd>
    class Win32TcpClient
    {
        typedef Win32TcpClient<CWinWnd> MyClassName;
        typedef void (MyClassName::*handler_ptr)(const boost::system::error_code&);
    public:
        Win32TcpClient(boost::asio::io_service& ios,tcp::endpoint endpoint,CWinWnd* pWinWnd)
            :io_service_(ios),win_wnd_(pWinWnd),socket_(ios),
            endpoint_(endpoint),deadline_(ios),stop_(false),connect_timeout_(TIME_INF),handler_ptr_(NULL)
        {
            set_timeout(connect_timeout_);
        }
    
        enum {TIME_INF = -1};
        void connect()
        {
            deadline_.async_wait(boost::bind(&MyClassName::check_deadline, this));
            socket_.async_connect(endpoint_,
                boost::bind(&MyClassName::run_main_thread_handler,this,&MyClassName::handler_connect,
                boost::asio::placeholders::error));
        }
        void set_timeout(int timeout_seconds)
        {
            if (timeout_seconds == TIME_INF)
                deadline_.expires_at(boost::posix_time::pos_infin);
            else
                deadline_.expires_from_now(boost::posix_time::seconds(timeout_seconds));
        }
    
        void close()
        {
            stop_ = true;
            socket_.close();
        }
        
        template<typename ConstBufferSequence>
        void send(const ConstBufferSequence& buffers)
        {
    
            boost::asio::async_write(socket_,buffers,
                boost::bind(&MyClassName::run_main_thread_handler,this,&MyClassName::handler_send,
                boost::asio::placeholders::error));
        }
    
        template<typename MutableBufferSequence>
        void receive(const MutableBufferSequence& buffers)
        {
            boost::asio::async_read(socket_,buffers,
                boost::bind(&MyClassName::run_main_thread_handler, this,&MyClassName::handler_receive,
                boost::asio::placeholders::error));
    
        }
    
        void win_proc()
        {
            if(handler_ptr_ != NULL)
                (this->*handler_ptr_)(error_code_);
        }
        virtual void handler_connect(const boost::system::error_code& error){}
        virtual void handler_send(const boost::system::error_code& error){}
        virtual void handler_receive(const boost::system::error_code& error){}
    private:
        
        void run_main_thread_handler(handler_ptr handler, const boost::system::error_code& error)
        {
            handler_ptr_ = handler;
            error_code_ = error;
            ::SendMessage(win_wnd_->m_hWnd,WM_ASIO_MESSAGE,NULL,(LPARAM)this);
        }
    
        void check_deadline()
        {
            if(stop_)return;
            if (deadline_.expires_at() <= deadline_timer::traits_type::now())
            {
                close();
                deadline_.expires_at(boost::posix_time::pos_infin);
            }
            deadline_.async_wait(boost::bind(&MyClassName::check_deadline, this));
        }
    protected:
        CWinWnd* win_wnd_;
        boost::asio::io_service& io_service_;
        tcp::socket socket_;
        tcp::endpoint endpoint_;
        deadline_timer deadline_;
        bool stop_;
        std::size_t connect_timeout_;
    
        handler_ptr handler_ptr_;
        boost::system::error_code error_code_;    
    };

    实际使用时,可以从上面的类中继承:

    class CollectClient : public Win32TcpClient<CTestBoostTimerDlg>
    {
    public:
        CollectClient(boost::asio::io_service& ios,tcp::endpoint endpoint,CTestBoostTimerDlg* pWinWnd):Win32TcpClient<CTestBoostTimerDlg>(ios,endpoint,pWinWnd){ memset(receive_buffer,0,100);}
        virtual void handler_connect(const boost::system::error_code& error);
        virtual void handler_send(const boost::system::error_code& error);
        virtual void handler_receive(const boost::system::error_code& error);
    private:
        char receive_buffer[200] ;
    };
    
    void CollectClient::handler_connect( const boost::system::error_code& error )
    {
        char sendMessage[] = "0020abcdefghijklsdkjaslk";
        if (!error)
        {    
            win_wnd_->MessageBox("success");
            receive(boost::asio::buffer(receive_buffer,30));
            send(boost::asio::buffer(sendMessage,strlen(sendMessage)));
        }
        else
        {    
            win_wnd_->MessageBox("fail");
            if(!stop_)
                close();
        }
    }
    
    void CollectClient::handler_send( const boost::system::error_code& error )
    {
        if(!error)
        {
            //win_wnd_->MessageBox("send success");
        }
        //close();
    }
    
    void CollectClient::handler_receive( const boost::system::error_code& error )
    {
        if(!error)
        {
            win_wnd_->MessageBox(receive_buffer);
            receive(boost::asio::buffer(receive_buffer,30));
        }
        else
        {
            win_wnd_->MessageBox(error.message().c_str());
        
        }
    }

    窗口类中需要添加处理代码是:

    BEGIN_MESSAGE_MAP(CTestBoostTimerDlg, CDialogEx)
        ON_WM_SYSCOMMAND()
        ON_WM_PAINT()
        ON_WM_QUERYDRAGICON()
        ON_MESSAGE(WM_ASIO_MESSAGE,&CTestBoostTimerDlg::OnAsioProc)
    END_MESSAGE_MAP()

    红色为自行添加的消息处理。

    OnAsioProc的代码也是基本固定的。

    声明:

    afx_msg LRESULT OnAsioProc(WPARAM wParam,LPARAM lParam);

    实现:

    LRESULT CTestBoostTimerDlg::OnAsioProc( WPARAM wParam,LPARAM lParam )
    {
        CollectClient* pc = (CollectClient*)lParam;
        pc->win_proc();
        return 0;
    }

    在dialog的初始化OnInitDialog中

    boost::asio::ip::tcp::endpoint endpoint(
            boost::asio::ip::address::from_string("127.0.0.1"), 830);
        pClient = new CollectClient(ios, endpoint,this);
        pClient->connect();

    这里的pClient和ios可以声明为全局变量,或者声明在窗口类中。

    boost::asio::io_service ios;
    CollectClient* pClient;

     为了是异步工作起来,需要启动另一个线程!

    boost::thread th(boost::bind(&io_service::run,&ios));
    th.detach();

    ok,这样的client就可以应用了,关于网络的连接我们其实仅抽象了6个函数connect, handler_connect, send, handler_send, receive, handler_receive。

    还有比较有用的是

      set_timeout 这个函数设置超时的秒数。到这个时间后,socket会自动关闭,默认为inf,不会超时。

      endpoint_ 该变量可查看连接的ip地址和端口号。

      win_wnd_ 该变量为窗口类的指针,可以针对窗口做一系列的操作。

  • 相关阅读:
    C# 设计模式
    FutureTask、Fork/Join、 BlockingQueue
    线程的几种创建方式
    行锁、表锁、乐观锁、悲观锁
    J.U.C之AQS
    同步容器并发容器
    线程不安全类
    线程封闭
    不可变对象
    安全发布对象—单例模式
  • 原文地址:https://www.cnblogs.com/zhangyonghugo/p/2614923.html
Copyright © 2020-2023  润新知