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_ 該變量為窗口類的指針,可以針對窗口做一系列的操作。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM