C++ 使用Boost.asio并行Ping(ICMP)多个目的地

C++ 使用Boost.asio并行Ping(ICMP)多个目的地,c++,multithreading,asynchronous,networking,boost-asio,C++,Multithreading,Asynchronous,Networking,Boost Asio,我已将ICMP ping实现()修改为并发ping多个目标,而不是按顺序ping,如示例所示。我尝试了std::thread和std::async(以及futures) 但是,只有当所有的目的地都无法到达时,它才能像预期的那样工作。难道不能同时进行吗?我在pinger类中禁用了在结果/超时时重新Ping const char* ping(const char* destination) { asio::io_context io_context; pinger p(io_contex

我已将ICMP ping实现()修改为并发ping多个目标,而不是按顺序ping,如示例所示。我尝试了std::thread和std::async(以及futures)

但是,只有当所有的目的地都无法到达时,它才能像预期的那样工作。难道不能同时进行吗?我在pinger类中禁用了在结果/超时时重新Ping

const char* ping(const char* destination)
{
   asio::io_context io_context;
   pinger p(io_context, destination);
   io_context.run();
   return p.get();
}
   
 int main()
{
   std::future<const char*> a1 = std::async(std::launch::async, ping, "10.2.7.196");
   std::future<const char*> a2 = std::async(std::launch::async, ping, "10.2.7.19");
   std::cout<<a1.get()<<std::endl;
   std::cout<<a2.get()<<std::endl; 
}
const char*ping(const char*destination)
{
asio::io_上下文io_上下文;
pinger p(io_上下文,目的地);
io_context.run();
返回p.get();
}
int main()
{
std::future a1=std::async(std::launch::async,ping,“10.2.7.196”);
std::future a2=std::async(std::launch::async,ping,“10.2.7.19”);

std::cout您不需要
std::async
imk_

但是从你展示的一点点代码中,我可以猜到你的错误正在返回原始的
char const*
。很有可能他们引用了
pinger
中的数据,当未来完成时,这些数据显然不再有效(
pinger
将超出范围)

发生这种情况的典型方式是,将输出存储在
std::string
成员中,并使用
.c_str()
get()
返回该输出

如果
get()
只返回一个字符串文本,比如
return“unreachable”
,那么它对无法到达的目标“起作用”的一个原因就是,它不会有上述的生存期问题

抛弃水晶球 因此,想象一种返回结果的正确方法:

#include <boost/asio.hpp>
#include <boost/bind/bind.hpp>
namespace asio = boost::asio;

#include "icmp_header.hpp"
#include "ipv4_header.hpp"

using asio::steady_timer;
using asio::ip::icmp;
namespace chrono = asio::chrono;

class pinger {
  public:
    pinger(asio::io_context& io_context, const char* destination)
            : resolver_(io_context), socket_(io_context, icmp::v4()),
              timer_(io_context), sequence_number_(0), num_replies_(0) {
        destination_ = *resolver_.resolve(icmp::v4(), destination, "").begin();

        start_send();
        start_receive();
    }

    std::string get() { auto r = _output.str(); _output.str(""); return r; }
  private:
    void start_send() {
        std::string body("\"Hello!\" from Asio ping.");

        // Create an ICMP header for an echo request.
        icmp_header echo_request;
        echo_request.type(icmp_header::echo_request);
        echo_request.code(0);
        echo_request.identifier(get_identifier());
        echo_request.sequence_number(++sequence_number_);
        compute_checksum(echo_request, body.begin(), body.end());

        // Encode the request packet.
        asio::streambuf request_buffer;
        std::ostream os(&request_buffer);
        os << echo_request << body;

        // Send the request.
        time_sent_ = steady_timer::clock_type::now();
        socket_.send_to(request_buffer.data(), destination_);

        // Wait up to five seconds for a reply.
        num_replies_ = 0;
        timer_.expires_at(time_sent_ + chrono::seconds(5));
        timer_.async_wait(boost::bind(&pinger::handle_timeout, this));
    }

    void handle_timeout() {
        if (num_replies_ == 0)
            _output << "Request timed out";

        //// Requests must be sent no less than one second apart.
        //timer_.expires_at(time_sent_ + chrono::seconds(1));
        //timer_.async_wait(boost::bind(&pinger::start_send, this));
    }

    void start_receive() {
        // Discard any data already in the buffer.
        reply_buffer_.consume(reply_buffer_.size());

        // Wait for a reply. We prepare the buffer to receive up to 64KB.
        socket_.async_receive(reply_buffer_.prepare(65536),
                              boost::bind(&pinger::handle_receive, this,
                                          boost::placeholders::_2));
    }

    void handle_receive(std::size_t length) {
        // The actual number of bytes received is committed to the buffer so
        // that we can extract it using a std::istream object.
        reply_buffer_.commit(length);

        // Decode the reply packet.
        std::istream is(&reply_buffer_);
        ipv4_header ipv4_hdr;
        icmp_header icmp_hdr;
        is >> ipv4_hdr >> icmp_hdr;

        // We can receive all ICMP packets received by the host, so we need to
        // filter out only the echo replies that match the our identifier and
        // expected sequence number.
        if (is && icmp_hdr.type() == icmp_header::echo_reply &&
            icmp_hdr.identifier() == get_identifier() &&
            icmp_hdr.sequence_number() == sequence_number_) {
            // If this is the first reply, interrupt the five second timeout.
            if (num_replies_++ == 0)
                timer_.cancel();

            // Print out some information about the reply packet.
            chrono::steady_clock::time_point now = chrono::steady_clock::now();
            chrono::steady_clock::duration elapsed = now - time_sent_;
            _output
                << length - ipv4_hdr.header_length() << " bytes from "
                << ipv4_hdr.source_address()
                << ": icmp_seq=" << icmp_hdr.sequence_number()
                << ", ttl=" << ipv4_hdr.time_to_live() << ", time="
                << chrono::duration_cast<chrono::milliseconds>(elapsed).count();
        }

        //start_receive();
    }

    static unsigned short get_identifier() {
#if defined(ASIO_WINDOWS)
        return static_cast<unsigned short>(::GetCurrentProcessId());
#else
        return static_cast<unsigned short>(::getpid());
#endif
    }

    std::ostringstream _output;

    icmp::resolver resolver_;
    icmp::endpoint destination_;
    icmp::socket socket_;
    steady_timer timer_;
    unsigned short sequence_number_;
    chrono::steady_clock::time_point time_sent_;
    asio::streambuf reply_buffer_;
    std::size_t num_replies_;
};

std::string ping1(const char* destination) {
    asio::io_context io_context;
    pinger p(io_context, destination);
    io_context.run();
    return p.get();
}

#include <list>
#include <iostream>
int main(int argc, char** argv) {
    std::list<std::future<std::string> > futures;
    for (char const* arg : std::vector(argv+1, argv+argc)) {
        futures.push_back(std::async(std::launch::async, ping1, arg));
    }

    for (auto& f : futures) {
        std::cout << f.get() << std::endl;
    }
}
#include <boost/asio.hpp>
#include <boost/bind/bind.hpp>
namespace asio = boost::asio;

#include "icmp_header.hpp"
#include "ipv4_header.hpp"

using asio::steady_timer;
using asio::ip::icmp;
namespace chrono = asio::chrono;

class pinger {
  public:
    pinger(asio::io_context& io_context, const char* destination)
            : resolver_(io_context), socket_(io_context, icmp::v4()),
              timer_(io_context), sequence_number_(0), num_replies_(0) {
        destination_ = *resolver_.resolve(icmp::v4(), destination, "").begin();

        start_send();
        start_receive();
    }

    std::string get() { auto r = _output.str(); _output.str(""); return r; }
  private:
    void start_send() {
        std::string body("\"Hello!\" from Asio ping.");

        // Create an ICMP header for an echo request.
        icmp_header echo_request;
        echo_request.type(icmp_header::echo_request);
        echo_request.code(0);
        echo_request.identifier(get_identifier());
        echo_request.sequence_number(++sequence_number_);
        compute_checksum(echo_request, body.begin(), body.end());

        // Encode the request packet.
        asio::streambuf request_buffer;
        std::ostream os(&request_buffer);
        os << echo_request << body;

        // Send the request.
        time_sent_ = steady_timer::clock_type::now();
        socket_.send_to(request_buffer.data(), destination_);

        // Wait up to five seconds for a reply.
        num_replies_ = 0;
        timer_.expires_at(time_sent_ + chrono::seconds(5));
        timer_.async_wait(boost::bind(&pinger::handle_timeout, this));
    }

    void handle_timeout() {
        if (num_replies_ == 0) {
            socket_.cancel(); // _output is set in response to error_code
        }
    }

    void start_receive() {
        // Discard any data already in the buffer.
        reply_buffer_.consume(reply_buffer_.size());

        // Wait for a reply. We prepare the buffer to receive up to 64KB.
        socket_.async_receive(reply_buffer_.prepare(65536),
                              boost::bind(&pinger::handle_receive, this,
                                 boost::asio::placeholders::error(),
                                 boost::asio::placeholders::bytes_transferred()));
    }

    void handle_receive(boost::system::error_code ec, std::size_t length) {
        if (ec) {
            if (ec == boost::asio::error::operation_aborted) {
                _output << "Request timed out";
            } else {
                _output << "error: " << ec.message();
            }
            return;
        }
        // The actual number of bytes received is committed to the buffer so
        // that we can extract it using a std::istream object.
        reply_buffer_.commit(length);

        // Decode the reply packet.
        std::istream is(&reply_buffer_);
        ipv4_header ipv4_hdr;
        icmp_header icmp_hdr;
        is >> ipv4_hdr >> icmp_hdr;

        // We can receive all ICMP packets received by the host, so we need to
        // filter out only the echo replies that match the our identifier and
        // expected sequence number.
        if (is && icmp_hdr.type() == icmp_header::echo_reply &&
            icmp_hdr.identifier() == get_identifier() &&
            icmp_hdr.sequence_number() == sequence_number_) {
            // If this is the first reply, interrupt the five second timeout.
            if (num_replies_++ == 0)
                timer_.cancel();

            // Print out some information about the reply packet.
            chrono::steady_clock::time_point now = chrono::steady_clock::now();
            chrono::steady_clock::duration elapsed = now - time_sent_;
            _output
                << length - ipv4_hdr.header_length() << " bytes from "
                << ipv4_hdr.source_address()
                << ": icmp_seq=" << icmp_hdr.sequence_number()
                << ", ttl=" << ipv4_hdr.time_to_live() << ", time="
                << chrono::duration_cast<chrono::milliseconds>(elapsed).count();
        } else start_receive();
    }

    static unsigned short get_identifier() {
#if defined(ASIO_WINDOWS)
        return static_cast<unsigned short>(::GetCurrentProcessId());
#else
        return static_cast<unsigned short>(::getpid());
#endif
    }

    std::ostringstream _output;

    icmp::resolver resolver_;
    icmp::endpoint destination_;
    icmp::socket socket_;
    steady_timer timer_;
    unsigned short sequence_number_;
    chrono::steady_clock::time_point time_sent_;
    asio::streambuf reply_buffer_;
    std::size_t num_replies_;
};

#include <list>
#include <iostream>
int main(int argc, char** argv) {
    asio::io_context io_context;

    std::list<pinger> pingers;
    for (char const* arg : std::vector(argv+1, argv+argc)) {
        pingers.emplace_back(io_context, arg);
    }

    io_context.run();

    for (auto& p : pingers) {
        std::cout << p.get() << std::endl;
    }
}
我得到这个输出:

  1 32 bytes from 127.0.0.12: icmp_seq=1, ttl=64, time=0
  1 32 bytes from 127.0.0.16: icmp_seq=1, ttl=64, time=0
  7 32 bytes from 127.0.0.44: icmp_seq=1, ttl=64, time=0
  1 32 bytes from 127.0.0.77: icmp_seq=1, ttl=64, time=1
  1 32 bytes from 127.0.0.82: icmp_seq=1, ttl=64, time=1
  1 32 bytes from 127.0.0.9: icmp_seq=1, ttl=64, time=0
 88 Request timed out
我不确定为什么会有这么多的超时,但关键是现在的代码是正确的。这段代码运行并完成UBSan/ASan清理。不过,请参阅下文了解稍后发现的修复程序

现在,让我们放弃未来 未来可能会产生大量的开销。事实上,你有一个
io\u服务
per ping。让我们在一个单独的平台上完成这一切

#include <list>
#include <iostream>
int main(int argc, char** argv) {
    asio::io_context io_context;

    std::list<pinger> pingers;
    for (char const* arg : std::vector(argv+1, argv+argc)) {
        pingers.emplace_back(io_context, arg);
    }

    io_context.run();

    for (auto& p : pingers) {
        std::cout << p.get() << std::endl;
    }
}
现在,
handle\u timeout
可以简化为:

void handle_timeout() {
    if (num_replies_ == 0) {
        socket_.cancel(); // _output is set in response to error_code
    }
}
事实上,我们可以简化为删除所有的
num_回复
,但我将把它留给读者作为驱魔

完整演示

#include <boost/asio.hpp>
#include <boost/bind/bind.hpp>
namespace asio = boost::asio;

#include "icmp_header.hpp"
#include "ipv4_header.hpp"

using asio::steady_timer;
using asio::ip::icmp;
namespace chrono = asio::chrono;

class pinger {
  public:
    pinger(asio::io_context& io_context, const char* destination)
            : resolver_(io_context), socket_(io_context, icmp::v4()),
              timer_(io_context), sequence_number_(0), num_replies_(0) {
        destination_ = *resolver_.resolve(icmp::v4(), destination, "").begin();

        start_send();
        start_receive();
    }

    std::string get() { auto r = _output.str(); _output.str(""); return r; }
  private:
    void start_send() {
        std::string body("\"Hello!\" from Asio ping.");

        // Create an ICMP header for an echo request.
        icmp_header echo_request;
        echo_request.type(icmp_header::echo_request);
        echo_request.code(0);
        echo_request.identifier(get_identifier());
        echo_request.sequence_number(++sequence_number_);
        compute_checksum(echo_request, body.begin(), body.end());

        // Encode the request packet.
        asio::streambuf request_buffer;
        std::ostream os(&request_buffer);
        os << echo_request << body;

        // Send the request.
        time_sent_ = steady_timer::clock_type::now();
        socket_.send_to(request_buffer.data(), destination_);

        // Wait up to five seconds for a reply.
        num_replies_ = 0;
        timer_.expires_at(time_sent_ + chrono::seconds(5));
        timer_.async_wait(boost::bind(&pinger::handle_timeout, this));
    }

    void handle_timeout() {
        if (num_replies_ == 0)
            _output << "Request timed out";

        //// Requests must be sent no less than one second apart.
        //timer_.expires_at(time_sent_ + chrono::seconds(1));
        //timer_.async_wait(boost::bind(&pinger::start_send, this));
    }

    void start_receive() {
        // Discard any data already in the buffer.
        reply_buffer_.consume(reply_buffer_.size());

        // Wait for a reply. We prepare the buffer to receive up to 64KB.
        socket_.async_receive(reply_buffer_.prepare(65536),
                              boost::bind(&pinger::handle_receive, this,
                                          boost::placeholders::_2));
    }

    void handle_receive(std::size_t length) {
        // The actual number of bytes received is committed to the buffer so
        // that we can extract it using a std::istream object.
        reply_buffer_.commit(length);

        // Decode the reply packet.
        std::istream is(&reply_buffer_);
        ipv4_header ipv4_hdr;
        icmp_header icmp_hdr;
        is >> ipv4_hdr >> icmp_hdr;

        // We can receive all ICMP packets received by the host, so we need to
        // filter out only the echo replies that match the our identifier and
        // expected sequence number.
        if (is && icmp_hdr.type() == icmp_header::echo_reply &&
            icmp_hdr.identifier() == get_identifier() &&
            icmp_hdr.sequence_number() == sequence_number_) {
            // If this is the first reply, interrupt the five second timeout.
            if (num_replies_++ == 0)
                timer_.cancel();

            // Print out some information about the reply packet.
            chrono::steady_clock::time_point now = chrono::steady_clock::now();
            chrono::steady_clock::duration elapsed = now - time_sent_;
            _output
                << length - ipv4_hdr.header_length() << " bytes from "
                << ipv4_hdr.source_address()
                << ": icmp_seq=" << icmp_hdr.sequence_number()
                << ", ttl=" << ipv4_hdr.time_to_live() << ", time="
                << chrono::duration_cast<chrono::milliseconds>(elapsed).count();
        }

        //start_receive();
    }

    static unsigned short get_identifier() {
#if defined(ASIO_WINDOWS)
        return static_cast<unsigned short>(::GetCurrentProcessId());
#else
        return static_cast<unsigned short>(::getpid());
#endif
    }

    std::ostringstream _output;

    icmp::resolver resolver_;
    icmp::endpoint destination_;
    icmp::socket socket_;
    steady_timer timer_;
    unsigned short sequence_number_;
    chrono::steady_clock::time_point time_sent_;
    asio::streambuf reply_buffer_;
    std::size_t num_replies_;
};

std::string ping1(const char* destination) {
    asio::io_context io_context;
    pinger p(io_context, destination);
    io_context.run();
    return p.get();
}

#include <list>
#include <iostream>
int main(int argc, char** argv) {
    std::list<std::future<std::string> > futures;
    for (char const* arg : std::vector(argv+1, argv+argc)) {
        futures.push_back(std::async(std::launch::async, ping1, arg));
    }

    for (auto& f : futures) {
        std::cout << f.get() << std::endl;
    }
}
#include <boost/asio.hpp>
#include <boost/bind/bind.hpp>
namespace asio = boost::asio;

#include "icmp_header.hpp"
#include "ipv4_header.hpp"

using asio::steady_timer;
using asio::ip::icmp;
namespace chrono = asio::chrono;

class pinger {
  public:
    pinger(asio::io_context& io_context, const char* destination)
            : resolver_(io_context), socket_(io_context, icmp::v4()),
              timer_(io_context), sequence_number_(0), num_replies_(0) {
        destination_ = *resolver_.resolve(icmp::v4(), destination, "").begin();

        start_send();
        start_receive();
    }

    std::string get() { auto r = _output.str(); _output.str(""); return r; }
  private:
    void start_send() {
        std::string body("\"Hello!\" from Asio ping.");

        // Create an ICMP header for an echo request.
        icmp_header echo_request;
        echo_request.type(icmp_header::echo_request);
        echo_request.code(0);
        echo_request.identifier(get_identifier());
        echo_request.sequence_number(++sequence_number_);
        compute_checksum(echo_request, body.begin(), body.end());

        // Encode the request packet.
        asio::streambuf request_buffer;
        std::ostream os(&request_buffer);
        os << echo_request << body;

        // Send the request.
        time_sent_ = steady_timer::clock_type::now();
        socket_.send_to(request_buffer.data(), destination_);

        // Wait up to five seconds for a reply.
        num_replies_ = 0;
        timer_.expires_at(time_sent_ + chrono::seconds(5));
        timer_.async_wait(boost::bind(&pinger::handle_timeout, this));
    }

    void handle_timeout() {
        if (num_replies_ == 0) {
            socket_.cancel(); // _output is set in response to error_code
        }
    }

    void start_receive() {
        // Discard any data already in the buffer.
        reply_buffer_.consume(reply_buffer_.size());

        // Wait for a reply. We prepare the buffer to receive up to 64KB.
        socket_.async_receive(reply_buffer_.prepare(65536),
                              boost::bind(&pinger::handle_receive, this,
                                 boost::asio::placeholders::error(),
                                 boost::asio::placeholders::bytes_transferred()));
    }

    void handle_receive(boost::system::error_code ec, std::size_t length) {
        if (ec) {
            if (ec == boost::asio::error::operation_aborted) {
                _output << "Request timed out";
            } else {
                _output << "error: " << ec.message();
            }
            return;
        }
        // The actual number of bytes received is committed to the buffer so
        // that we can extract it using a std::istream object.
        reply_buffer_.commit(length);

        // Decode the reply packet.
        std::istream is(&reply_buffer_);
        ipv4_header ipv4_hdr;
        icmp_header icmp_hdr;
        is >> ipv4_hdr >> icmp_hdr;

        // We can receive all ICMP packets received by the host, so we need to
        // filter out only the echo replies that match the our identifier and
        // expected sequence number.
        if (is && icmp_hdr.type() == icmp_header::echo_reply &&
            icmp_hdr.identifier() == get_identifier() &&
            icmp_hdr.sequence_number() == sequence_number_) {
            // If this is the first reply, interrupt the five second timeout.
            if (num_replies_++ == 0)
                timer_.cancel();

            // Print out some information about the reply packet.
            chrono::steady_clock::time_point now = chrono::steady_clock::now();
            chrono::steady_clock::duration elapsed = now - time_sent_;
            _output
                << length - ipv4_hdr.header_length() << " bytes from "
                << ipv4_hdr.source_address()
                << ": icmp_seq=" << icmp_hdr.sequence_number()
                << ", ttl=" << ipv4_hdr.time_to_live() << ", time="
                << chrono::duration_cast<chrono::milliseconds>(elapsed).count();
        } else start_receive();
    }

    static unsigned short get_identifier() {
#if defined(ASIO_WINDOWS)
        return static_cast<unsigned short>(::GetCurrentProcessId());
#else
        return static_cast<unsigned short>(::getpid());
#endif
    }

    std::ostringstream _output;

    icmp::resolver resolver_;
    icmp::endpoint destination_;
    icmp::socket socket_;
    steady_timer timer_;
    unsigned short sequence_number_;
    chrono::steady_clock::time_point time_sent_;
    asio::streambuf reply_buffer_;
    std::size_t num_replies_;
};

#include <list>
#include <iostream>
int main(int argc, char** argv) {
    asio::io_context io_context;

    std::list<pinger> pingers;
    for (char const* arg : std::vector(argv+1, argv+argc)) {
        pingers.emplace_back(io_context, arg);
    }

    io_context.run();

    for (auto& p : pingers) {
        std::cout << p.get() << std::endl;
    }
}

事实上,这很少/从来都不是正确的工具

用我的水晶球


显然,我们没有制作ICMP数据包的权限,更不用说在Wandbox上发送它们了,您不需要
std::async

但是从你展示的一点点代码中,我可以猜到你的错误正在返回原始的
char const*
。很有可能他们引用了
pinger
中的数据,当未来完成时,这些数据显然不再有效(
pinger
将超出范围)

发生这种情况的典型方式是,将输出存储在
std::string
成员中,并使用
.c_str()
get()
返回该输出

如果
get()
只返回一个字符串文本,比如
return“unreachable”
,那么它对无法到达的目标“起作用”的一个原因就是,它不会有上述的生存期问题

抛弃水晶球 因此,想象一种返回结果的正确方法:

#include <boost/asio.hpp>
#include <boost/bind/bind.hpp>
namespace asio = boost::asio;

#include "icmp_header.hpp"
#include "ipv4_header.hpp"

using asio::steady_timer;
using asio::ip::icmp;
namespace chrono = asio::chrono;

class pinger {
  public:
    pinger(asio::io_context& io_context, const char* destination)
            : resolver_(io_context), socket_(io_context, icmp::v4()),
              timer_(io_context), sequence_number_(0), num_replies_(0) {
        destination_ = *resolver_.resolve(icmp::v4(), destination, "").begin();

        start_send();
        start_receive();
    }

    std::string get() { auto r = _output.str(); _output.str(""); return r; }
  private:
    void start_send() {
        std::string body("\"Hello!\" from Asio ping.");

        // Create an ICMP header for an echo request.
        icmp_header echo_request;
        echo_request.type(icmp_header::echo_request);
        echo_request.code(0);
        echo_request.identifier(get_identifier());
        echo_request.sequence_number(++sequence_number_);
        compute_checksum(echo_request, body.begin(), body.end());

        // Encode the request packet.
        asio::streambuf request_buffer;
        std::ostream os(&request_buffer);
        os << echo_request << body;

        // Send the request.
        time_sent_ = steady_timer::clock_type::now();
        socket_.send_to(request_buffer.data(), destination_);

        // Wait up to five seconds for a reply.
        num_replies_ = 0;
        timer_.expires_at(time_sent_ + chrono::seconds(5));
        timer_.async_wait(boost::bind(&pinger::handle_timeout, this));
    }

    void handle_timeout() {
        if (num_replies_ == 0)
            _output << "Request timed out";

        //// Requests must be sent no less than one second apart.
        //timer_.expires_at(time_sent_ + chrono::seconds(1));
        //timer_.async_wait(boost::bind(&pinger::start_send, this));
    }

    void start_receive() {
        // Discard any data already in the buffer.
        reply_buffer_.consume(reply_buffer_.size());

        // Wait for a reply. We prepare the buffer to receive up to 64KB.
        socket_.async_receive(reply_buffer_.prepare(65536),
                              boost::bind(&pinger::handle_receive, this,
                                          boost::placeholders::_2));
    }

    void handle_receive(std::size_t length) {
        // The actual number of bytes received is committed to the buffer so
        // that we can extract it using a std::istream object.
        reply_buffer_.commit(length);

        // Decode the reply packet.
        std::istream is(&reply_buffer_);
        ipv4_header ipv4_hdr;
        icmp_header icmp_hdr;
        is >> ipv4_hdr >> icmp_hdr;

        // We can receive all ICMP packets received by the host, so we need to
        // filter out only the echo replies that match the our identifier and
        // expected sequence number.
        if (is && icmp_hdr.type() == icmp_header::echo_reply &&
            icmp_hdr.identifier() == get_identifier() &&
            icmp_hdr.sequence_number() == sequence_number_) {
            // If this is the first reply, interrupt the five second timeout.
            if (num_replies_++ == 0)
                timer_.cancel();

            // Print out some information about the reply packet.
            chrono::steady_clock::time_point now = chrono::steady_clock::now();
            chrono::steady_clock::duration elapsed = now - time_sent_;
            _output
                << length - ipv4_hdr.header_length() << " bytes from "
                << ipv4_hdr.source_address()
                << ": icmp_seq=" << icmp_hdr.sequence_number()
                << ", ttl=" << ipv4_hdr.time_to_live() << ", time="
                << chrono::duration_cast<chrono::milliseconds>(elapsed).count();
        }

        //start_receive();
    }

    static unsigned short get_identifier() {
#if defined(ASIO_WINDOWS)
        return static_cast<unsigned short>(::GetCurrentProcessId());
#else
        return static_cast<unsigned short>(::getpid());
#endif
    }

    std::ostringstream _output;

    icmp::resolver resolver_;
    icmp::endpoint destination_;
    icmp::socket socket_;
    steady_timer timer_;
    unsigned short sequence_number_;
    chrono::steady_clock::time_point time_sent_;
    asio::streambuf reply_buffer_;
    std::size_t num_replies_;
};

std::string ping1(const char* destination) {
    asio::io_context io_context;
    pinger p(io_context, destination);
    io_context.run();
    return p.get();
}

#include <list>
#include <iostream>
int main(int argc, char** argv) {
    std::list<std::future<std::string> > futures;
    for (char const* arg : std::vector(argv+1, argv+argc)) {
        futures.push_back(std::async(std::launch::async, ping1, arg));
    }

    for (auto& f : futures) {
        std::cout << f.get() << std::endl;
    }
}
#include <boost/asio.hpp>
#include <boost/bind/bind.hpp>
namespace asio = boost::asio;

#include "icmp_header.hpp"
#include "ipv4_header.hpp"

using asio::steady_timer;
using asio::ip::icmp;
namespace chrono = asio::chrono;

class pinger {
  public:
    pinger(asio::io_context& io_context, const char* destination)
            : resolver_(io_context), socket_(io_context, icmp::v4()),
              timer_(io_context), sequence_number_(0), num_replies_(0) {
        destination_ = *resolver_.resolve(icmp::v4(), destination, "").begin();

        start_send();
        start_receive();
    }

    std::string get() { auto r = _output.str(); _output.str(""); return r; }
  private:
    void start_send() {
        std::string body("\"Hello!\" from Asio ping.");

        // Create an ICMP header for an echo request.
        icmp_header echo_request;
        echo_request.type(icmp_header::echo_request);
        echo_request.code(0);
        echo_request.identifier(get_identifier());
        echo_request.sequence_number(++sequence_number_);
        compute_checksum(echo_request, body.begin(), body.end());

        // Encode the request packet.
        asio::streambuf request_buffer;
        std::ostream os(&request_buffer);
        os << echo_request << body;

        // Send the request.
        time_sent_ = steady_timer::clock_type::now();
        socket_.send_to(request_buffer.data(), destination_);

        // Wait up to five seconds for a reply.
        num_replies_ = 0;
        timer_.expires_at(time_sent_ + chrono::seconds(5));
        timer_.async_wait(boost::bind(&pinger::handle_timeout, this));
    }

    void handle_timeout() {
        if (num_replies_ == 0) {
            socket_.cancel(); // _output is set in response to error_code
        }
    }

    void start_receive() {
        // Discard any data already in the buffer.
        reply_buffer_.consume(reply_buffer_.size());

        // Wait for a reply. We prepare the buffer to receive up to 64KB.
        socket_.async_receive(reply_buffer_.prepare(65536),
                              boost::bind(&pinger::handle_receive, this,
                                 boost::asio::placeholders::error(),
                                 boost::asio::placeholders::bytes_transferred()));
    }

    void handle_receive(boost::system::error_code ec, std::size_t length) {
        if (ec) {
            if (ec == boost::asio::error::operation_aborted) {
                _output << "Request timed out";
            } else {
                _output << "error: " << ec.message();
            }
            return;
        }
        // The actual number of bytes received is committed to the buffer so
        // that we can extract it using a std::istream object.
        reply_buffer_.commit(length);

        // Decode the reply packet.
        std::istream is(&reply_buffer_);
        ipv4_header ipv4_hdr;
        icmp_header icmp_hdr;
        is >> ipv4_hdr >> icmp_hdr;

        // We can receive all ICMP packets received by the host, so we need to
        // filter out only the echo replies that match the our identifier and
        // expected sequence number.
        if (is && icmp_hdr.type() == icmp_header::echo_reply &&
            icmp_hdr.identifier() == get_identifier() &&
            icmp_hdr.sequence_number() == sequence_number_) {
            // If this is the first reply, interrupt the five second timeout.
            if (num_replies_++ == 0)
                timer_.cancel();

            // Print out some information about the reply packet.
            chrono::steady_clock::time_point now = chrono::steady_clock::now();
            chrono::steady_clock::duration elapsed = now - time_sent_;
            _output
                << length - ipv4_hdr.header_length() << " bytes from "
                << ipv4_hdr.source_address()
                << ": icmp_seq=" << icmp_hdr.sequence_number()
                << ", ttl=" << ipv4_hdr.time_to_live() << ", time="
                << chrono::duration_cast<chrono::milliseconds>(elapsed).count();
        } else start_receive();
    }

    static unsigned short get_identifier() {
#if defined(ASIO_WINDOWS)
        return static_cast<unsigned short>(::GetCurrentProcessId());
#else
        return static_cast<unsigned short>(::getpid());
#endif
    }

    std::ostringstream _output;

    icmp::resolver resolver_;
    icmp::endpoint destination_;
    icmp::socket socket_;
    steady_timer timer_;
    unsigned short sequence_number_;
    chrono::steady_clock::time_point time_sent_;
    asio::streambuf reply_buffer_;
    std::size_t num_replies_;
};

#include <list>
#include <iostream>
int main(int argc, char** argv) {
    asio::io_context io_context;

    std::list<pinger> pingers;
    for (char const* arg : std::vector(argv+1, argv+argc)) {
        pingers.emplace_back(io_context, arg);
    }

    io_context.run();

    for (auto& p : pingers) {
        std::cout << p.get() << std::endl;
    }
}
我得到这个输出:

  1 32 bytes from 127.0.0.12: icmp_seq=1, ttl=64, time=0
  1 32 bytes from 127.0.0.16: icmp_seq=1, ttl=64, time=0
  7 32 bytes from 127.0.0.44: icmp_seq=1, ttl=64, time=0
  1 32 bytes from 127.0.0.77: icmp_seq=1, ttl=64, time=1
  1 32 bytes from 127.0.0.82: icmp_seq=1, ttl=64, time=1
  1 32 bytes from 127.0.0.9: icmp_seq=1, ttl=64, time=0
 88 Request timed out
我不确定为什么会有这么多的超时,但关键是现在的代码是正确的。这段代码运行并完成UBSan/ASan清理。不过,请参阅下文了解稍后发现的修复程序

现在,让我们放弃未来 未来可能会产生大量的开销。事实上,你有一个
io\u服务
per ping。让我们在一个单独的平台上完成这一切

#include <list>
#include <iostream>
int main(int argc, char** argv) {
    asio::io_context io_context;

    std::list<pinger> pingers;
    for (char const* arg : std::vector(argv+1, argv+argc)) {
        pingers.emplace_back(io_context, arg);
    }

    io_context.run();

    for (auto& p : pingers) {
        std::cout << p.get() << std::endl;
    }
}
现在,
handle\u timeout
可以简化为:

void handle_timeout() {
    if (num_replies_ == 0) {
        socket_.cancel(); // _output is set in response to error_code
    }
}
事实上,我们可以简化为删除所有的
num_回复
,但我将把它留给读者作为驱魔

完整演示

#include <boost/asio.hpp>
#include <boost/bind/bind.hpp>
namespace asio = boost::asio;

#include "icmp_header.hpp"
#include "ipv4_header.hpp"

using asio::steady_timer;
using asio::ip::icmp;
namespace chrono = asio::chrono;

class pinger {
  public:
    pinger(asio::io_context& io_context, const char* destination)
            : resolver_(io_context), socket_(io_context, icmp::v4()),
              timer_(io_context), sequence_number_(0), num_replies_(0) {
        destination_ = *resolver_.resolve(icmp::v4(), destination, "").begin();

        start_send();
        start_receive();
    }

    std::string get() { auto r = _output.str(); _output.str(""); return r; }
  private:
    void start_send() {
        std::string body("\"Hello!\" from Asio ping.");

        // Create an ICMP header for an echo request.
        icmp_header echo_request;
        echo_request.type(icmp_header::echo_request);
        echo_request.code(0);
        echo_request.identifier(get_identifier());
        echo_request.sequence_number(++sequence_number_);
        compute_checksum(echo_request, body.begin(), body.end());

        // Encode the request packet.
        asio::streambuf request_buffer;
        std::ostream os(&request_buffer);
        os << echo_request << body;

        // Send the request.
        time_sent_ = steady_timer::clock_type::now();
        socket_.send_to(request_buffer.data(), destination_);

        // Wait up to five seconds for a reply.
        num_replies_ = 0;
        timer_.expires_at(time_sent_ + chrono::seconds(5));
        timer_.async_wait(boost::bind(&pinger::handle_timeout, this));
    }

    void handle_timeout() {
        if (num_replies_ == 0)
            _output << "Request timed out";

        //// Requests must be sent no less than one second apart.
        //timer_.expires_at(time_sent_ + chrono::seconds(1));
        //timer_.async_wait(boost::bind(&pinger::start_send, this));
    }

    void start_receive() {
        // Discard any data already in the buffer.
        reply_buffer_.consume(reply_buffer_.size());

        // Wait for a reply. We prepare the buffer to receive up to 64KB.
        socket_.async_receive(reply_buffer_.prepare(65536),
                              boost::bind(&pinger::handle_receive, this,
                                          boost::placeholders::_2));
    }

    void handle_receive(std::size_t length) {
        // The actual number of bytes received is committed to the buffer so
        // that we can extract it using a std::istream object.
        reply_buffer_.commit(length);

        // Decode the reply packet.
        std::istream is(&reply_buffer_);
        ipv4_header ipv4_hdr;
        icmp_header icmp_hdr;
        is >> ipv4_hdr >> icmp_hdr;

        // We can receive all ICMP packets received by the host, so we need to
        // filter out only the echo replies that match the our identifier and
        // expected sequence number.
        if (is && icmp_hdr.type() == icmp_header::echo_reply &&
            icmp_hdr.identifier() == get_identifier() &&
            icmp_hdr.sequence_number() == sequence_number_) {
            // If this is the first reply, interrupt the five second timeout.
            if (num_replies_++ == 0)
                timer_.cancel();

            // Print out some information about the reply packet.
            chrono::steady_clock::time_point now = chrono::steady_clock::now();
            chrono::steady_clock::duration elapsed = now - time_sent_;
            _output
                << length - ipv4_hdr.header_length() << " bytes from "
                << ipv4_hdr.source_address()
                << ": icmp_seq=" << icmp_hdr.sequence_number()
                << ", ttl=" << ipv4_hdr.time_to_live() << ", time="
                << chrono::duration_cast<chrono::milliseconds>(elapsed).count();
        }

        //start_receive();
    }

    static unsigned short get_identifier() {
#if defined(ASIO_WINDOWS)
        return static_cast<unsigned short>(::GetCurrentProcessId());
#else
        return static_cast<unsigned short>(::getpid());
#endif
    }

    std::ostringstream _output;

    icmp::resolver resolver_;
    icmp::endpoint destination_;
    icmp::socket socket_;
    steady_timer timer_;
    unsigned short sequence_number_;
    chrono::steady_clock::time_point time_sent_;
    asio::streambuf reply_buffer_;
    std::size_t num_replies_;
};

std::string ping1(const char* destination) {
    asio::io_context io_context;
    pinger p(io_context, destination);
    io_context.run();
    return p.get();
}

#include <list>
#include <iostream>
int main(int argc, char** argv) {
    std::list<std::future<std::string> > futures;
    for (char const* arg : std::vector(argv+1, argv+argc)) {
        futures.push_back(std::async(std::launch::async, ping1, arg));
    }

    for (auto& f : futures) {
        std::cout << f.get() << std::endl;
    }
}
#include <boost/asio.hpp>
#include <boost/bind/bind.hpp>
namespace asio = boost::asio;

#include "icmp_header.hpp"
#include "ipv4_header.hpp"

using asio::steady_timer;
using asio::ip::icmp;
namespace chrono = asio::chrono;

class pinger {
  public:
    pinger(asio::io_context& io_context, const char* destination)
            : resolver_(io_context), socket_(io_context, icmp::v4()),
              timer_(io_context), sequence_number_(0), num_replies_(0) {
        destination_ = *resolver_.resolve(icmp::v4(), destination, "").begin();

        start_send();
        start_receive();
    }

    std::string get() { auto r = _output.str(); _output.str(""); return r; }
  private:
    void start_send() {
        std::string body("\"Hello!\" from Asio ping.");

        // Create an ICMP header for an echo request.
        icmp_header echo_request;
        echo_request.type(icmp_header::echo_request);
        echo_request.code(0);
        echo_request.identifier(get_identifier());
        echo_request.sequence_number(++sequence_number_);
        compute_checksum(echo_request, body.begin(), body.end());

        // Encode the request packet.
        asio::streambuf request_buffer;
        std::ostream os(&request_buffer);
        os << echo_request << body;

        // Send the request.
        time_sent_ = steady_timer::clock_type::now();
        socket_.send_to(request_buffer.data(), destination_);

        // Wait up to five seconds for a reply.
        num_replies_ = 0;
        timer_.expires_at(time_sent_ + chrono::seconds(5));
        timer_.async_wait(boost::bind(&pinger::handle_timeout, this));
    }

    void handle_timeout() {
        if (num_replies_ == 0) {
            socket_.cancel(); // _output is set in response to error_code
        }
    }

    void start_receive() {
        // Discard any data already in the buffer.
        reply_buffer_.consume(reply_buffer_.size());

        // Wait for a reply. We prepare the buffer to receive up to 64KB.
        socket_.async_receive(reply_buffer_.prepare(65536),
                              boost::bind(&pinger::handle_receive, this,
                                 boost::asio::placeholders::error(),
                                 boost::asio::placeholders::bytes_transferred()));
    }

    void handle_receive(boost::system::error_code ec, std::size_t length) {
        if (ec) {
            if (ec == boost::asio::error::operation_aborted) {
                _output << "Request timed out";
            } else {
                _output << "error: " << ec.message();
            }
            return;
        }
        // The actual number of bytes received is committed to the buffer so
        // that we can extract it using a std::istream object.
        reply_buffer_.commit(length);

        // Decode the reply packet.
        std::istream is(&reply_buffer_);
        ipv4_header ipv4_hdr;
        icmp_header icmp_hdr;
        is >> ipv4_hdr >> icmp_hdr;

        // We can receive all ICMP packets received by the host, so we need to
        // filter out only the echo replies that match the our identifier and
        // expected sequence number.
        if (is && icmp_hdr.type() == icmp_header::echo_reply &&
            icmp_hdr.identifier() == get_identifier() &&
            icmp_hdr.sequence_number() == sequence_number_) {
            // If this is the first reply, interrupt the five second timeout.
            if (num_replies_++ == 0)
                timer_.cancel();

            // Print out some information about the reply packet.
            chrono::steady_clock::time_point now = chrono::steady_clock::now();
            chrono::steady_clock::duration elapsed = now - time_sent_;
            _output
                << length - ipv4_hdr.header_length() << " bytes from "
                << ipv4_hdr.source_address()
                << ": icmp_seq=" << icmp_hdr.sequence_number()
                << ", ttl=" << ipv4_hdr.time_to_live() << ", time="
                << chrono::duration_cast<chrono::milliseconds>(elapsed).count();
        } else start_receive();
    }

    static unsigned short get_identifier() {
#if defined(ASIO_WINDOWS)
        return static_cast<unsigned short>(::GetCurrentProcessId());
#else
        return static_cast<unsigned short>(::getpid());
#endif
    }

    std::ostringstream _output;

    icmp::resolver resolver_;
    icmp::endpoint destination_;
    icmp::socket socket_;
    steady_timer timer_;
    unsigned short sequence_number_;
    chrono::steady_clock::time_point time_sent_;
    asio::streambuf reply_buffer_;
    std::size_t num_replies_;
};

#include <list>
#include <iostream>
int main(int argc, char** argv) {
    asio::io_context io_context;

    std::list<pinger> pingers;
    for (char const* arg : std::vector(argv+1, argv+argc)) {
        pingers.emplace_back(io_context, arg);
    }

    io_context.run();

    for (auto& p : pingers) {
        std::cout << p.get() << std::endl;
    }
}

事实上,这很少/从来都不是正确的工具

用我的水晶球


显然,我们没有处理ICMP数据包的权限,更不用说在WangBox

TL上发送它们了;DR使用C++类型,注意生命周期,避免线程化。如果需要的话,也可以从最后一个列表中复制/粘贴(因为沿途有很多小的改进)。我尝试过类似的解决方案,但没有成功。现在也没有成功。ping环回地址后接18.0.0.1对我很有效。但是在这种情况下ping我得到的响应是kile this(第二个是无法访问的).sudo./a.out 10.2.7.194 18.0.0.2从10.2.7.194中取出32字节:icmp_-seq=1,ttl=60,time=27从10.2.7.194中取出32字节:icmp_-seq=1,ttl=60,time=27$sudo./a.out 10.2.7.194 18.0.0.2不好用为什么?10.2.7.194中的32字节:icmp_seq=1,ttl=60,time=26,10.2.7.194中的32字节:icmp_seq=1,ttl=60,time=26$sudo./a.out 127.0.0.1 18.0.2#好,127.0.0.0.0.1中的32字节:icmp_seq=1,ttl=64,time=0,time=0,timeout请求a.10$sudo./a.0.0.0.0.0 352都无法访问352#equest timed out请求timed out我认为这里的问题是ICMP是无连接的-就像UDP一样,并且您正在为同一IP接收多个响应。奇怪的是,它/说/
,从10.2.7.194开始:ICMP_seq=1
两次。这意味着您实际上在同一进程中发送了两次回显请求。我想展示一下告诉我确切的代码。为什么不像我一样在wandbox上发布它呢?我只是复制粘贴了你的代码并用gcc编译。只是更改了占位符错误,并在async_receive中将字节_传输到_1和_2。我没有更改任何其他内容。是否需要清除handle receive中的任何内容。代码中是否存在概念性错误。(就像允许从进程一次ping一个目标)TL;D