繁体   English   中英

我应该如何在UDP中找到我在Boost Asio中收到的客户端?

[英]How should I find which client I am receiving from in Boost Asio in UDP?

因此,我知道如何找到我收到的客户端的唯一方法是通过在所有客户端的循环中比较接收到的端点,我想知道是否有更优雅的方式来处理它。

在tcp中,每个客户端都有自己的套接字,通过它,它可以立即找到它接收的客户端。 如果我让每个客户端在udp中都有自己的套接字,那么它的效率会更高或更低吗?

我也在考虑创建一个全局套接字,并使每个客户端对象只监听它们的端点,但我认为这不是可能的,也不是高效的asio。

应用程序代码负责解复用。 在高级别,有两种选择:

  • 使用单个端点在概念上充当接受器。 在接收到握手消息时,客户端将实例化新的本地端点,并通知客户端将新构造的端点用于客户端会话的剩余部分。 这导致每个客户端的套接字,并且通过连接的UDP套接字 ,可以保证客户端仅接收来自预期远程端点的消息。 这应该不比使用TCP套接字的方法有效。 但是,它需要在发送方和接收方上更改应用程序协议。
  • 使用单个插槽。 在接收到消息时,远程端点用于解复用到客户端对象。 如果应用程序依赖于解复用抽象,则可以自由地更改实现以最适合应用程序的使用。 这不需要更改应用程序协议。

第一个选项将更容易支持更高的并发级别,因为每个客户端都可以控制其异步调用链的生命周期。 虽然在第二个选项中每个客户端都可以有一个调用链,但控制生命周期会带来复杂性,因为所有异步调用链都绑定到同一个I / O对象。

另一方面,随着并发性的增加,内存也会增加。 因此,第一个选项可能比第二个选项使用更多内存。 此外,在第二个控制整体内存更容易,因为并发级别不是完全动态的。 在任何一种情况下, 反应器样式操作都可用于减轻总体内存使用量。

最后,从实现中抽象出应用程序,同时保持代码的可维护性。 应用程序运行后,分析,识别瓶颈,并根据实际数据做出选择。


要稍微扩展第二个选项,这里是一个完整的最小示例 ,它将端点与客户端对象关联起来的基本client_manager

#include <memory>
#include <unordered_map>
#include <boost/asio.hpp>

namespace ip = boost::asio::ip;

/// @brief Mockup client.
class client:
  public std::enable_shared_from_this<client>
{
public:

  explicit client(ip::udp::endpoint endpoint)
    : endpoint_(endpoint)
  {}

  const ip::udp::endpoint& endpoint() const { return endpoint_; }

private:

  ip::udp::endpoint endpoint_;
};

/// @brief Basic class that manages clients.  Given an endpoint, the
///        associated client, if any, can be found.
class client_manager
{
private:

  // The underlying implementation used by the manager.
  using container_type = std::unordered_map<
    ip::udp::endpoint, std::shared_ptr<client>,
    std::size_t (*)(const ip::udp::endpoint&)>;

  /// @brief Return a hash value for the provided endpoint.
  static std::size_t get_hash(const ip::udp::endpoint& endpoint)
  {
    std::ostringstream stream;
    stream << endpoint;
    std::hash<std::string> hasher;
    return hasher(stream.str());
  }

public:

  using key_type = container_type::key_type;
  using mapped_type = container_type::mapped_type;

  /// @brief Constructor.
  client_manager()
    : clients_(0, &client_manager::get_hash)
  {}

// The public abstraction upon which the application will depend.
public:

  /// @brief Add a client to the manager.
  void add(mapped_type client)
  {
    clients_[client->endpoint()] = client;
  }

  /// @brief Given an endpoint, retrieve the associated client.  Return
  ///        an empty shared pointer if one is not found.
  mapped_type get(key_type key) const
  {
    auto result = clients_.find(key);
    return clients_.end() != result
      ? result->second // Found client.
      : mapped_type(); // No client found.
  }

private:

  container_type clients_;
};

int main()
{
  // Unique endpoints.
  ip::udp::endpoint endpoint1(ip::address::from_string("11.11.11.11"), 1111);
  ip::udp::endpoint endpoint2(ip::address::from_string("22.22.22.22"), 2222);
  ip::udp::endpoint endpoint3(ip::address::from_string("33.33.33.33"), 3333);

  // Create a client for each endpoint.
  auto client1 = std::make_shared<client>(endpoint1);
  auto client2 = std::make_shared<client>(endpoint2);
  auto client3 = std::make_shared<client>(endpoint3);

  // Add the clients to the manager.
  client_manager manager;
  manager.add(client1);
  manager.add(client2);
  manager.add(client3);

  // Locate a client based on the endpoint.
  auto client_result = manager.get(endpoint2);
  assert(client1 != client_result);
  assert(client2 == client_result);
  assert(client3 != client_result);
}

请注意,由于应用程序仅依赖于client_manager抽象(即client_manager::add()client_manager::get()前置和后置条件),因此只要实现维护,就可以更改client_manager实现而不会影响应用程序。前后条件。 例如,它可以使用序列容器(如std::vector )或有序关联容器(如std::map来实现,而不是使用std::unordered_map 选择最符合预期用途的容器。 在分析之后,如果容器选择是已识别的瓶颈,则根据实际使用情况更改client_manager的实现以使用更合适的容器。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM