简体   繁体   中英

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

So the only way that I know how to find which client I received from is by comparing the received endpoint in a loop of all the clients, and I was wondering if there was a more elegant way of handling this.

In tcp, every client has its own socket, and with it, it can find which client it receives from instantly. If I make every client have its own socket in udp, will it be more or less efficient?

I was also thinking of making a global socket, and making every client object listen to only their endpoint's, but I don't think that's possible, or efficient in asio.

The application code is responsible for demultiplexing. At a high-level, there are two options:

  • Use a single endpoint to conceptually function as an acceptor. Upon receiving a handshake message, the client would instantiate a new local endpoint, and inform the client to use the newly constructed endpoint for the remainder of the client's session. This results in a socket per client, and with connected UDP sockets , a client can be guaranteed to only receive messages from the expected remote endpoint. This should be no less efficient than the same approach used with TCP sockets. However, it requires making changes to the application protocol on both the sender and receiver.
  • Use a single socket. Upon receiving a message, the remote endpoint is used to demultiplex to the client object. If the application depends upon the demultiplex abstraction, then the implementation may be freely changed to best suit the application's usage. This requires no changes to the application protocol.

The first option will more easily support higher concurrency levels, as each client can control the lifetime of its asynchronous call chain. While it is possible to have a call chain per client in the second option, controlling the lifetime introduces complexity, as all asynchronous call chains are bound to the same I/O object.

On the other hand, as concurrency increase, so does memory. Hence, the first option is likely to use more memory than the second option. Furthermore, controlling overall memory is easier in the second, as the concurrency level will not be completely dynamic. In either case, reactor style operations can be used to mitigate the overall memory usage.

In the end, abstract the application from the implementation whilst keeping the code maintainable. Once the application is working, profile, identify bottlenecks, and make choices based on actual data.


To expand slightly on the second option, here is an complete minimal example of a basic client_manager that associates endpoints to client objects:

#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);
}

Note that as the application only depends upon the client_manager abstraction (ie pre and post conditions for client_manager::add() and client_manager::get() ), then the client_manager implementation can be changed without affecting the application as long as the implementation maintains the pre and post conditions. For instance, instead of using std::unordered_map , it could be implemented with a sequence container, such as std::vector , or an ordered associated container, such as std::map . Choose a container that best fits the expected usage. After profiling, if the container choice is an identified bottleneck, then change the implementation of client_manager to use a more suitable container based on the actual usage.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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