【问题标题】:BOOST::ASIO - UDP - endpoint gets overwrittenBOOST::ASIO - UDP - 端点被覆盖
【发布时间】:2018-12-06 19:06:56
【问题描述】:

我正在尝试使用 BOOST::ASIO 在 UDP 中实现一些保活服务,这些是一般步骤:

  1. 向同一台机器上的 2 个进程发送 keep-alives,它们正在使用不同的端口侦听同一个 ip。

  2. 循环向两者发送 async_send_to,回调是一个函数,它使用回调 F() 调用 async_receive_from。 两者都引用相同的端点和数据缓冲区。

  3. 内部带有 io_service.run_one() 的 while 循环。

进程立即回复。

问题是,当我检查端点的端口(通缉的情况)F() 运行时,我偶尔会得到 2 个不同的端口,或者,我会得到两个相同的端口。

似乎端点缓冲区(可能还有数据)被后面的数据包覆盖了。

我在想,因为我使用的是 run_one(),所以数据包应该一个一个地处理,不会有覆盖。

初始发送 -

        void GetInstancesHeartbeat(udp::endpoint &sender_endpoint)
        {
            int instanceIndex = 0;
            for (; instanceIndex <= amountOfInstances ; instanceIndex++)
            {
                udp::endpoint endpoint = udp::endpoint(IP, Port+ instanceIndex);
                m_instancesSocket->async_send_to(
                      boost::asio::buffer((char*)&(message),
                      sizeof(message)),endpoint,
                      boost::bind(&ClusterManager::handle_send_to_instance, 
                      this, boost::asio::placeholders::error,
                      boost::asio::placeholders::bytes_transferred,
                      sender_endpoint));
            }
        }

然后是处理程序 -

        void handle_send_to_instance(const boost::system::error_code& error, size_t 
                                         bytes_recvd, udp::endpoint &sender_endpoint)
        {
            m_instancesSocket->async_receive_from(
                boost::asio::buffer(m_dataBuffer, m_maxLength), m_endpoint,
                boost::bind(&ClusterManager::handle_receive_from_instance, this,
                boost::asio::placeholders::error,
                boost::asio::placeholders::bytes_transferred,
                sender_endpoint));
        }

While 循环 -

        while(true){
            io_service.run_one();
        }

句柄接收端口结果两次相同的地方-

        void handle_receive_from_instance(const boost::system::error_code& error, size_t 
                                             bytes_recvd, udp::endpoint&sender_endpoint)
            {
                if (!error && bytes_recvd > 0)
                {
                    int instancePort = m_endpoint.port();
                } else {
                    //PRINT ERROR
                }
            }

【问题讨论】:

    标签: c++ sockets udp boost-asio


    【解决方案1】:

    实际操作是异步的,因此不知道何时写入端点引用。这就是异步调用的本质。

    因此,您需要的是每个异步调用都有一个端点接收变量(您可以按实例索引存储它)。

    还有许多其他非常可疑的地方:

    • message 的类型是什么?对于大多数类型,您只需编写boost::asio::buffer(message)(处理T []std::vector&lt;T&gt;array&lt;T&gt; 等)。这适用于 Tchar 或任何 POD 类型。

      如果message 实际上是某种类型的结构,请考虑使用单元素数组以避免进行危险的强制转换:

      Live On Coliru

      POD message[1] = {pod};
      
      s.async_send_to(boost::asio::buffer(message), udp::endpoint{{}, 6767}, [](boost::system::error_code ec, size_t transferred) {
              std::cout << "Transferred: " << transferred << " (" << ec.message() << ")\n";
          });
      

      (在典型系统上发送 12 个字节)。

      无论你做什么,都不要编写不安全的 C 风格转换 (Why use static_cast<int>(x) instead of (int)x?)。

    • 你有while(true) { io.run_one(); },这是一个无限循环。更好的写法是:while(io.run_one()) {}

    • 但是,这与io.run(); 基本相同,但正确性和效率较低(参见https://www.boost.org/doc/libs/1_68_0/boost/asio/detail/impl/scheduler.ipp 第138 行),那么为什么不使用它呢?

    【讨论】:

    • 消息是一个结构,我对这个转换没有任何问题,但我会调查它。我考虑了具有端点数组的解决方案,但据我了解,虽然我在调用 async_receive_from 时通过特定实例(和唯一端口)的相应 async_send_to 调用 handle_receive_from_instance 我不知道它是否真的来自之前启动该异步的特定实例。如果我错了,请纠正我,但任何 async_receive_from 都会简单地接收第一个回复实例。如果处理程序同步,我可以将 id 作为参数传递..
    • 是的,两者都需要。拥有封装与端点相关的所有状态的连接类型是很常见的,因此所有处理程序都可以对此进行轰炸。即使 UDP 是无连接的,您也可以在这里使用相同的想法
    • 我想我明白了如何使用端点数组来完成它。如果我写入端点和数据缓冲区数组并将索引传递给处理程序,那么我检查返回索引上端点的端口,那么我应该拥有它。我试试,谢谢!
    猜你喜欢
    • 2014-07-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多