在Windows上使用Boost.Asio的半并发ICMP ping

时间:2016-05-03 12:18:55

标签: c++ boost boost-asio icmp

我修改了示例http://www.boost.org/doc/libs/1_51_0/doc/html/boost_asio/example/icmp/ping.cpp  关于如何定期ping主机以同时ping多个主机。 首先,创建对所有主机的请求并发送到套接字。然后在第二阶段收集所有回复,直到计时器到期。

3个客户的修改示例:

// Headers from ping example:
// http://www.boost.org/doc/libs/1_51_0/doc/html/boost_asio/example/icmp/
#include "icmp_header.hpp"
#include "ipv4_header.hpp"

#include <boost/asio.hpp>
#include <iostream>

using boost::asio::ip::icmp;
using boost::asio::deadline_timer;
using boost::asio::io_service;
using boost::asio::streambuf;
using boost::system::error_code;
using std::cout;
using std::endl;
namespace posix_time = boost::posix_time;

static const std::string BODY = "ping";
static const auto PROCESS = GetCurrentProcessId();

static int gSequence;
static io_service gService;
static icmp::socket gSocket(gService, icmp::v4());
static deadline_timer gTimer(gService);
static streambuf gReply;
static icmp::endpoint gReceiver;

void StartReceive()
{
    gSocket.async_receive_from(gReply.prepare(65536), gReceiver,
        [&](const error_code& error, size_t length)
    {
        gReply.commit(length);

        ipv4_header ipv4Hdr;
        icmp_header icmpHdr;
        std::string body(BODY.size(), 0);

        std::istream is(&gReply);
        is >> ipv4Hdr >> icmpHdr;
        is.read(&body[0], BODY.size());

        auto ip = ipv4Hdr.source_address().to_string();
        auto rc = gReceiver.address().to_string();
        auto id = icmpHdr.identifier();
        auto process = PROCESS;
        auto sn = icmpHdr.sequence_number();
        auto type = icmpHdr.type();

        cout << "  Length              = " << length << endl;
        cout << "  Error               = " << error << endl;
        cout << "  IP checksum         = " << ipv4Hdr.header_checksum() << endl;
        cout << "  IP address          = " << ip << endl;
        cout << "  Receiver address    = " << rc << endl;
        cout << "  ICMP identification = " << id << endl;
        cout << "  ICMP type           = " << (int)type << endl;
        cout << "  Process             = " << process << endl;
        cout << "  Sequence            = " << sn << endl;

        if (is
            && icmpHdr.type() == icmp_header::echo_reply
            && icmpHdr.identifier() == PROCESS
            && icmpHdr.sequence_number() == gSequence
            && body == BODY)
        {
            cout << "    > " << ip << endl;
        }

        cout << endl;

        gReply.consume(length);

        StartReceive();
    });
}

int main()
{
    icmp::resolver resolver(gService);

    icmp_header echoRequest;
    echoRequest.type(icmp_header::echo_request);
    echoRequest.identifier(PROCESS);

    for (gSequence = 0; gSequence < 3; ++gSequence)
    {
        cout << "----------------------------------------------------------" << endl;
        cout << "Iteration = " << gSequence << endl;
        cout << "----------------------------------------------------------" << endl;

        echoRequest.sequence_number(gSequence);
        compute_checksum(echoRequest, BODY.begin(), BODY.end());

        streambuf request;
        std::ostream os(&request);
        os << echoRequest << BODY;

        gService.reset();

        StartReceive();

        std::vector<std::string> pool
        {
            "10.170.110.29",
            "10.170.97.39",
            "10.170.7.52"
        };

        for (const auto & ip : pool)
        {
            icmp::resolver::query query(icmp::v4(), ip, "");
            auto dest = *resolver.resolve(query);

            gSocket.send_to(request.data(), dest);
        }

        gTimer.expires_from_now(posix_time::millisec(2000));
        gTimer.async_wait([&](const error_code& error) { gService.stop(); });

        gService.run();
        gReply.commit(gReply.size());
        gReply.consume(gReply.size());
    }

    return 0;
}

第一次迭代(0)每次按预期工作,尽管收到的第一个数据包始终为零长度。但是,在所有后续迭代中,来自一个或多个客户端的响应不会被传递,而是来自另一个客户端的响应被多次传递。使用Wireshark我可以看到示例中的所有主机都非常迅速地向请求发送了一个响应。

这是产生的产出之一:

----------------------------------------------------------
Iteration = 0
----------------------------------------------------------
  Length              = 0
  Error               = system:10022
  IP checksum         = 0
  IP address          = 0.0.0.0
  Receiver address    = 0.0.0.0
  ICMP identification = 0
  ICMP type           = 0
  Process             = 20464
  Sequence            = 0

  Length              = 32
  Error               = system:0
  IP checksum         = 595
  IP address          = 10.170.97.39
  Receiver address    = 10.170.97.39
  ICMP identification = 20464
  ICMP type           = 0
  Process             = 20464
  Sequence            = 0
    > 10.170.97.39

  Length              = 32
  Error               = system:0
  IP checksum         = 31034
  IP address          = 10.170.110.29
  Receiver address    = 10.170.110.29
  ICMP identification = 20464
  ICMP type           = 0
  Process             = 20464
  Sequence            = 0
    > 10.170.110.29

  Length              = 32
  Error               = system:0
  IP checksum         = 51432
  IP address          = 10.170.7.52
  Receiver address    = 10.170.7.52
  ICMP identification = 20464
  ICMP type           = 0
  Process             = 20464
  Sequence            = 0
    > 10.170.7.52

----------------------------------------------------------
Iteration = 1
----------------------------------------------------------
  Length              = 32
  Error               = system:0
  IP checksum         = 594
  IP address          = 10.170.97.39
  Receiver address    = 10.170.97.39
  ICMP identification = 20464
  ICMP type           = 0
  Process             = 20464
  Sequence            = 1
    > 10.170.97.39

  Length              = 32
  Error               = system:0
  IP checksum         = 51419
  IP address          = 10.170.7.52
  Receiver address    = 10.170.7.52
  ICMP identification = 20464
  ICMP type           = 0
  Process             = 20464
  Sequence            = 1
    > 10.170.7.52

  Length              = 32
  Error               = system:0
  IP checksum         = 51419
  IP address          = 10.170.7.52
  Receiver address    = 10.170.7.52
  ICMP identification = 20464
  ICMP type           = 0
  Process             = 20464
  Sequence            = 1
    > 10.170.7.52    

----------------------------------------------------------
Iteration = 2
----------------------------------------------------------
  Length              = 32
  Error               = system:0
  IP checksum         = 593
  IP address          = 10.170.97.39
  Receiver address    = 10.170.97.39
  ICMP identification = 20464
  ICMP type           = 0
  Process             = 20464
  Sequence            = 2
    > 10.170.97.39

  Length              = 32
  Error               = system:0
  IP checksum         = 51407
  IP address          = 10.170.7.52
  Receiver address    = 10.170.7.52
  ICMP identification = 20464
  ICMP type           = 0
  Process             = 20464
  Sequence            = 2
    > 10.170.7.52

  Length              = 32
  Error               = system:0
  IP checksum         = 51407
  IP address          = 10.170.7.52
  Receiver address    = 10.170.7.52
  ICMP identification = 20464
  ICMP type           = 0
  Process             = 20464
  Sequence            = 2
    > 10.170.7.52

这是Boost.Asio的正确用法和行为吗?

由于

1 个答案:

答案 0 :(得分:1)

似乎很好。它似乎对我有用。

注意:

  • 使用streambuf似乎过于复杂 - 我想知道streambuf的重复使用会导致相同内容的重复发现

  • 如果其中一个池地址解析为本地NIC地址(因为您将收到自己的ICMP数据包),这些东西会变得混乱

  • 你永远不会在第一场比赛之后解决任何地址,也不会检查该决议是否有效;而且你每次都解决(这可能是设计,但也可能是一个错误.DNS请求也可能会干扰你的观察(特别是如果你有一个本地DNS缓存/网关?)。

    考虑使用boost::asio::async_resolve并将其从循环中取出,这样就不会影响时间。

这是一个简化版本:

// Headers from ping example:
// http://www.boost.org/doc/libs/1_51_0/doc/html/boost_asio/example/icmp/
#include "icmp_header.hpp"
#include "ipv4_header.hpp"

#include <sys/types.h>
#include <unistd.h>
#include <boost/asio.hpp>
#include <iostream>
#include <sstream>

using boost::asio::ip::icmp;
using boost::asio::deadline_timer;
using boost::asio::io_service;
using boost::asio::streambuf;
using boost::system::error_code;
using std::cout;
using std::endl;
namespace posix_time = boost::posix_time;

static const std::string BODY = "ping";
static const auto PROCESS = getpid();

static int gSequence;
static io_service gService;
static icmp::socket gSocket(gService, icmp::v4());
static char gReply[65536];
static icmp::endpoint gReceiver;

void StartReceive() {
    gSocket.async_receive_from(boost::asio::buffer(gReply), gReceiver, [&](const error_code &error, size_t length) {

        ipv4_header ipv4Hdr;
        icmp_header icmpHdr;
        std::string body(BODY.size(), 0);

        std::istringstream is(std::string(gReply, length));
        is >> ipv4Hdr >> icmpHdr;
        is.read(&body[0], BODY.size());

        auto ip      = ipv4Hdr.source_address().to_string();
        auto rc      = gReceiver.address().to_string();
        auto id      = icmpHdr.identifier();
        auto process = PROCESS;
        auto sn      = icmpHdr.sequence_number();
        auto type    = icmpHdr.type();

        cout << " Length="              << length <<
                " Error="               << error <<
                " IP checksum="         << ipv4Hdr.header_checksum() <<
                " IP address="          << ip <<
                " Receiver address="    << rc <<
                " ICMP identification=" << id <<
                " ICMP type="           << (int)type <<
                " Process="             << process <<
                " Sequence="            << sn << "\n";

        if (is && icmpHdr.type() == icmp_header::echo_reply && icmpHdr.identifier() == PROCESS &&
            icmpHdr.sequence_number() == gSequence && body == BODY) {
            cout << "    > " << ip << endl;
        }

        cout << endl;

        StartReceive();
    });
}

int main() {
    icmp::resolver resolver(gService);

    icmp_header echoRequest;
    echoRequest.type(icmp_header::echo_request);
    echoRequest.identifier(PROCESS);

    for (gSequence = 0; gSequence < 3; ++gSequence) {
        cout << "----------------------------------------------------------" << endl;
        cout << "Iteration=" << gSequence << endl;
        cout << "----------------------------------------------------------" << endl;

        echoRequest.sequence_number(gSequence);
        compute_checksum(echoRequest, BODY.begin(), BODY.end());

        streambuf request;
        std::ostream os(&request);
        os << echoRequest << BODY;

        gService.reset();

        StartReceive();

        for (std::string ip : { "www.msn.com", "www.google.com" }) {
            icmp::resolver::query query(icmp::v4(), ip, "");
            auto dest = *resolver.resolve(query);

            gSocket.send_to(request.data(), dest);
            std::cout << "Sent to " << dest.endpoint() << "\n";
        }

        deadline_timer gTimer(gService);
        gTimer.expires_from_now(posix_time::millisec(2000));
        gTimer.async_wait([&](error_code) { gService.stop(); });

        gService.run();
    }
}

打印,例如

----------------------------------------------------------
Iteration=0
----------------------------------------------------------
Sent to 204.79.197.203:0
Sent to 216.58.212.164:0
 Length=32 Error=system:0 IP checksum=49241 IP address=204.79.197.203 Receiver address=204.79.197.203 ICMP identification=8041 ICMP type=0 Process=8041 Sequence=0
    > 204.79.197.203

 Length=32 Error=system:0 IP checksum=5449 IP address=216.58.212.164 Receiver address=216.58.212.164 ICMP identification=8041 ICMP type=0 Process=8041 Sequence=0
    > 216.58.212.164

----------------------------------------------------------
Iteration=1
----------------------------------------------------------
Sent to 204.79.197.203:0
Sent to 216.58.212.164:0
 Length=32 Error=system:0 IP checksum=49240 IP address=204.79.197.203 Receiver address=204.79.197.203 ICMP identification=8041 ICMP type=0 Process=8041 Sequence=1
    > 204.79.197.203

 Length=32 Error=system:0 IP checksum=5449 IP address=216.58.212.164 Receiver address=216.58.212.164 ICMP identification=8041 ICMP type=0 Process=8041 Sequence=1
    > 216.58.212.164

----------------------------------------------------------
Iteration=2
----------------------------------------------------------
Sent to 204.79.197.203:0
Sent to 216.58.212.164:0
 Length=32 Error=system:0 IP checksum=49239 IP address=204.79.197.203 Receiver address=204.79.197.203 ICMP identification=8041 ICMP type=0 Process=8041 Sequence=2
    > 204.79.197.203

 Length=32 Error=system:0 IP checksum=5449 IP address=216.58.212.164 Receiver address=216.58.212.164 ICMP identification=8041 ICMP type=0 Process=8041 Sequence=2
    > 216.58.212.164