boost::io_service 的使用 & 如何检查网络通讯正常?心跳包机制 or Detection of Half-Open (Dropped) Connections

来源:互联网 发布:java链表的实现 编辑:程序博客网 时间:2024/05/17 02:39

see link: http://blog.stephencleary.com/2009/05/detection-of-half-open-dropped.html

  1. 首先明确一点,通过ping这种方式来检查wamp服务器是否连接正常存在一些缺点。ping工作在 ip 层(ICMP),能够ping通不能保证 tcp/ip以及其他依赖tcp/ip的高级协议工作正常。
  2. 二次连接正常也不能保证通讯正常。

下面通过boost timer写了一个最简单直接的检查网络是否连接正常的模块(远端必须周期性地触发 reset 函数,否则定时器超时,表示网络连接有问题):

    class HeartBeatService    {    public:        HeartBeatService(boost::asio::io_service* p_io_service, unsigned int interval):            p_io_service_(p_io_service),            interval_(interval),  // second            timer_(new boost::asio::deadline_timer(*p_io_service)),            flag_(false)        {}        HeartBeatService(const HeartBeatService& other):            p_io_service_(other.p_io_service_),            interval_(other.interval_),            timer_(new boost::asio::deadline_timer(*other.p_io_service_)),            flag_(other.flag_)        {}        ~HeartBeatService()        {            delete timer_;        }        // Schedule the timer for the first time        // The timer will fire after interval_ from io_service start run        bool start()        {            timer_->expires_from_now(interval_);            timer_->async_wait(boost::bind(&HeartBeatService::check, this, boost::asio::placeholders::error));        }        // 远端调用, reset flag        void reset()        {            flag_ = false;        }        bool getFlag()  // For test        {            return flag_;        }    private:        void check(const boost::system::error_code& e)  // timer 定时检查, 并设置标志        {            if( e )            {                // TODO print error message                return;            }            if(flag_)            {                //TODO notify the controller offline!                std::cerr << "TimeOut warning! The connection is lost!"            }            else            {                timer_->expires_at(timer_->expires_at() + interval_);                timer_->async_wait(boost::bind(&HeartBeatService::check, this, boost::asio::placeholders::error));                flag_ = true;            }        }        boost::asio::io_service* p_io_service_;        boost::posix_time::seconds interval_; // 超时时间        boost::asio::deadline_timer* timer_;  // not copyable, so use pointer        bool flag_;    };

用 google test 编写单元测试,测试HeartBeatService类

#include <iostream>#include <thread>#include <future>#include <boost/asio.hpp>#include <gtest/gtest.h>TEST(MyProject, HeartBeatTest){    boost::asio::io_service io;    unsigned int seconds = 3;  // 设定超时时间,客户端必须在这个时间内周期性地调用 reset函数    HeartBeatServicet(&io, seconds);    EXPECT_TRUE(t.start());    // 调用 io_service::run, 定时器开始计时    // 或者:auto future = std::async(std::launch::async, [&]{io.run();});  // better    std::thread io_thread(boost::bind(&boost::asio::io_service::run, &io));    std::this_thread::sleep_for(std::chrono::seconds(2));  // 等待 2s    EXPECT_FALSE(t.getFlag());  // default is false    std::this_thread::sleep_for(std::chrono::seconds(2));  // 等待 2s    EXPECT_TRUE(t.getFlag());  // 定时器设置为3 sec,现在已经超时,所以会将flag设置为 true    t.reset();  // 清除 flag    EXPECT_FALSE(t.getFlag());    std::this_thread::sleep_for(std::chrono::seconds(4));   // 再次等待超时    EXPECT_TRUE(t.getFlag());  // 已经超时,并且设置了标志位为true    testing::internal::CaptureStderr();  // 超时了会有打印信息,捕获打印    std::this_thread::sleep_for(std::chrono::seconds(3));   // 再次等待超时,如果flag为true,并且再次等待超时则会打印信息////    std::string output1 = testing::internal::GetCapturedStdout();  // expect output    std::string output = testing::internal::GetCapturedStderr();  // expect output    EXPECT_FALSE(output.empty());    std::cerr << output << std::endl;    io.stop();    io_thread.join();}int main (int argc, char** argv){    ::testing::InitGoogleTest(&argc, argv);    return RUN_ALL_TESTS();}

io_service.run 的使用注意

如果调换上述代码的顺序:

    boost::asio::io_service io;    std::thread io_thread(boost::bind(&boost::asio::io_service::run, &io)); // 先运行 io_service::run    unsigned int seconds = 3;  // 设定超时时间,客户端必须在这个时间内周期性地调用 reset函数    HeartBeatServicet(&io, seconds);    EXPECT_TRUE(t.start()); // 再开始计时

如果按照先调用 io.run() 再初始化计时器的顺序,则 io_service 会检测没有处理的消息,会马上返回。为了避免出现这种情况,可以手动加一个 boost::asio::io_service::work 以避免 io.run() 返回, see link: http://stackoverflow.com/questions/35945490/keep-io-service-alive。

析构 boost::asio::io_service::work 并调用 io.stop() 会使得 io.run() 立刻返回。

io_service有很多非阻塞的调用方式( io.poll, io.poll_one, io.dispatch etc.) ,但是推荐最好的处理方式还是调用 io.run()

0 0
原创粉丝点击