zoukankan      html  css  js  c++  java
  • (原创)如何使用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,欢迎大家来交流技术。

  • 相关阅读:
    C. Shaass and Lights 解析(思維、組合)
    D. Binary String To Subsequences(队列)(贪心)
    CodeForces 1384B2. Koa and the Beach (Hard Version)(贪心)
    CodeForces 1384B1. Koa and the Beach (Easy Version)(搜索)
    CodeForces 1384C. String Transformation 1(贪心)(并查集)
    CodeForces 1384A. Common Prefixes
    POJ-2516 Minimum Cost(最小费用最大流)
    POJ3261-Milk Patterns(后缀数组)
    HDU-1300 Pearls(斜率DP)
    HDU-4528 小明系列故事-捉迷藏(BFS)
  • 原文地址:https://www.cnblogs.com/qicosmos/p/3471314.html
Copyright © 2011-2022 走看看