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

  • 相关阅读:
    08mybatis映射文件及别名
    2016年阅读书单
    Java的位运算
    Python线程操作
    Java之Jackson框架
    Java线程
    Canvas 知识体系简单总结
    图片轮播
    media页面布局2
    利用@media screen实现网页布局的自适应
  • 原文地址:https://www.cnblogs.com/qicosmos/p/3471314.html
Copyright © 2020-2023  润新知