(原創)如何使用boost.asio寫一個簡單的通信程序(二)


  先說下上一篇文章中提到的保持io_service::run不退出的簡單辦法。因為只要異步事件隊列中有事件,io_service::run就會一直阻塞不退出,所以只要保證異步事件隊列中一直有事件就行了,如何讓異步事件隊列中一直有事件呢?一個簡單的辦法就是循環發起異步讀操作,如果對方一直都不發數據過來,則這個異步讀事件就會一直在異步事件隊列中,這樣io_service::run就不會退出了。但是這樣有一個缺點就是io_service::run處於阻塞會阻塞當前線程,如果不希望阻塞當前線程,就還是通過work來保持io_service::run不退出。

  現在言歸正傳,看看如何用asio寫一個簡單的客戶端。我希望這個客戶端具備讀寫能力,還能自動重連。我希望用一個連接器類去實現連接以及IO事件的處理,連接器具體的職責有三個:1.連接到服務器;2.重連。3.通過事件處理器實現讀寫。其中,實現重連可以用一個專門的線程去檢測,為了簡單,不設置重連次數,保持一直重連。實現讀寫可以直接用上篇中的RWHandler。看看連接器Connctor是如何寫的:

class Connector
{
public:

    Connector(io_service& ios, const string& strIP, short port) :m_ios(ios), m_socket(ios),
        m_serverAddr(tcp::endpoint(address::from_string(strIP), port)), m_isConnected(false), m_chkThread(nullptr)
    {
        CreateEventHandler(ios);
    }

    ~Connector()
    {
    }

    bool Start()
    {        
        m_eventHandler->GetSocket().async_connect(m_serverAddr, [this](const boost::system::error_code& error)
        {
            if (error)
            {
                HandleConnectError(error);
                return;
            }
            cout << "connect ok" << endl;
            m_isConnected = true;
            m_eventHandler->HandleRead(); //連接成功后發起一個異步讀的操作
        });

        boost::this_thread::sleep(boost::posix_time::seconds(1));
        return m_isConnected;
    }

    bool IsConnected() const
    {
        return m_isConnected;
    }

    void Send(char* data, int len)
    {
        if (!m_isConnected)
            return;

        m_eventHandler->HandleWrite(data, len);
    }

    void AsyncSend(char* data, int len)
    {
        if (!m_isConnected)
            return;

        m_eventHandler->HandleAsyncWrite(data, len);
    }

private:
    void CreateEventHandler(io_service& ios)
    {
        m_eventHandler = std::make_shared<RWHandler>(ios);
        m_eventHandler->SetCallBackError([this](int connid){HandleRWError(connid); });
    }

    void CheckConnect()
    {
        if (m_chkThread != nullptr)
            return;

        m_chkThread = std::make_shared<std::thread>([this]
        {
            while (true)
            {
                if (!IsConnected())
                    Start();

                boost::this_thread::sleep(boost::posix_time::seconds(1));
            }
        });
    }

    void HandleConnectError(const boost::system::error_code& error)
    {
        m_isConnected = false;
        cout << error.message() << endl;
        m_eventHandler->CloseSocket();
        CheckConnect();
    }

    void HandleRWError(int connid)
    {
        m_isConnected = false;
        CheckConnect();
    }

private:
    io_service& m_ios;
    tcp::socket m_socket;

    tcp::endpoint m_serverAddr; //服務器地址

    std::shared_ptr<RWHandler> m_eventHandler;
    bool m_isConnected;
    std::shared_ptr<std::thread> m_chkThread; //專門檢測重連的線程
};

  注意看連接成功之后,我發起了一個異步讀操作,它的作用除了接收數據之外,還可以用來判斷連接是否斷開,因為當連接斷開時,異步接收事件會觸發,據此可以做重連操作。可以看到,在連接失敗后,或者讀寫發生錯誤之后我會關閉連接然后開始自動重連。

  再看看測試代碼:

int main()
{
    io_service ios;
    boost::asio::io_service::work work(ios);
    boost::thread thd([&ios]{ios.run(); });

    Connector conn(ios, "127.0.0.1", 9900);
    conn.Start();

    if (!conn.IsConnected())
    {
        Pause();
        return -1;
    }
    
    const int len = 512;
    char buf[len] = "";
    
    while (std::cin.getline(line, sizeof(line)))
    {
        conn.Send(line, sizeof(line));
    }    

    return 0;
}                    

  注意看我是通過work和一個專門的線程的線程去run保持io_service不退出的。 至此,一個簡單的客戶端完成了。 不過,我並沒有提到如何異步發送,異步發送稍微麻煩一點,它涉及發送隊列以及如何異步循環發送的問題,為了簡單起見就沒有專門去講它,也許后面會用專門的篇幅去講異步發送。一般情況下同步發送足夠了,如果希望更高的發送效率,可以考慮半同步半異步的線程池去發送,以提高效率。

如果你覺得這篇文章對你有用,可以點一下推薦,謝謝。

c++11 boost技術交流群:296561497,歡迎大家來交流技術。


免責聲明!

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



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