#include "string"
#include <boost/asio.hpp>
#include <boost/asio/io_context.hpp>
using namespace boost::asio::ip;

class A {
    public:
    std::string address;
    std::string port;
    boost::asio::io_context io_context;
    udp::socket socketInstance = udp::socket(io_context);
    udp::endpoint endpointSetup;

    explicit A(std::string addrs, std::string port) {
        this->address = addrs;
        this->port = port;
    }

    A(const A& a) {
        this->address = a.address;
        this->port = a.port;
    }

    void attachListener() {
        endpointSetup = udp::endpoint(make_address(address), std::stoi(port));
        socketInstance = udp::socket(io_context, endpointSetup);
    }
};

class B {
    public:
    A aClass;

    explicit B(const A& a) : aClass(a) {}

    void getData() {
        std::array<char, 1024> recv_buffer{};

        aClass.socketInstance.receive_from(boost::asio::buffer(recv_buffer), aClass.endpointSetup);
    }
};

int main() {
    A a("192.168.1.49", "5080");
    a.attachListener();
    B b(a);
    b.getData();
}
aClass.socketInstance.receive_from(boost::asio::buffer(recv_buffer), aClass.endpointSetup);部分创建错误Bad file descriptor
我假设io_context中的class A属性以某种方式被破坏了。基本上,我要实现的是在单独的类中监听某些UDP端口。我该如何解决?

最佳答案

问题在于您在aClass中声明了B成员:

class B {
    public:
    A aClass;

    explicit B(const A& a) : aClass(a) {}
...
aClass成员的类型为A,因此,当您执行aClass(a)时,将执行复制。您已经为A定义了一个复制构造函数,但是它不会复制已经打开的套接字,而在拷贝中留下了一个默认初始化(因此未打开)的套接字。
考虑以下示例类:
class C {
public:
  std::string foo_ = "foo";
  C(const std::string &foo) : foo_{foo} {}
  C(const C &c) {}
};
如果您将运行以下代码:
  C c{"bar"};
  C c2{c};
  std::cout << c2.foo_ << std::endl;
您将获得输出foo。这是因为C的复制构造函数不会复制foo_成员。因此,在初始化c2时,它不会从bar中获取值c,而是将foo_成员默认初始化为foo。同样的问题也适用于A类的套接字成员。
解决方案1
通过使B保留指向A的指针来避免复制:
class B {
public:
  A* aClass;

  explicit B(A* a) : aClass(a) {}

  void getData() {
    std::array<char, 1024> recv_buffer{};

    aClass->socketInstance.receive_from(boost::asio::buffer(recv_buffer),
                                       aClass->endpointSetup);
  }
};
然后在main中执行:
B b(&a);
这样,当您创建B时,它仍然可以访问为原始A初始化的套接字。
如果您想表达所有权,即A*拥有std::unique_ptr<A>并因此由std::shared_ptr<A>拥有套接字,那么也可以使用智能指针(即BA)来代替A
解决方案2
如果您想将A传递给A,但又想让B拥有B实例持有的套接字的所有权,而又不使用(智能)指针的间接方式,则另一种解决方案是move-construct A
这样,您可以利用udp::socket的move-constructor。但是,这将需要您将io_contextA中移出,因为io_context没有move-constructor。
#include <boost/asio.hpp>
#include <boost/asio/io_context.hpp>

using namespace boost::asio::ip;

class A {
public:
  std::string address;
  uint16_t port;

  boost::asio::io_context &io_context;

  udp::socket socketInstance;
  udp::endpoint endpointSetup;

  explicit A(boost::asio::io_context &io_context, std::string addrs,
             uint16_t port)
      : io_context{io_context}, address{addrs}, port{port}, socketInstance{
                                                                io_context} {}

  void attachListener() {
    endpointSetup = udp::endpoint{make_address(address), port};
    socketInstance = udp::socket{io_context, endpointSetup};
  }
};

class B {
public:
  A aClass;

  explicit B(A &&a) : aClass{std::move(a)} {}

  void getData() {
    std::array<char, 1024> recv_buffer{};

    aClass.socketInstance.receive_from(boost::asio::buffer(recv_buffer),
                                       aClass.endpointSetup);
  }
};

int main() {
  boost::asio::io_context io_context{};

  A a{io_context, "127.0.0.1", 5080};
  a.attachListener();
  B b{std::move(a)};
  b.getData();
}
两种解决方案都可以进行测试,例如和
netcat --udp localhost 5080

关于c++ - 从receive_from:使用boost::asio时错误的文件描述符,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/63881752/

10-11 19:58