[英]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. 在tcp中,每个客户端都有自己的套接字,通过它,它可以立即找到它接收的客户端。 If I make every client have its own socket in udp, will it be more or less efficient?
如果我让每个客户端在udp中都有自己的套接字,那么它的效率会更高或更低吗?
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. 我也在考虑创建一个全局套接字,并使每个客户端对象只监听它们的端点,但我认为这不是可能的,也不是高效的asio。
The application code is responsible for demultiplexing. 应用程序代码负责解复用。 At a high-level, there are two options:
在高级别,有两种选择:
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.
虽然在第二个选项中每个客户端都可以有一个调用链,但控制生命周期会带来复杂性,因为所有异步调用链都绑定到同一个I / O对象。
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: 要稍微扩展第二个选项,这里是一个完整的最小示例 ,它将端点与客户端对象关联起来的基本
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);
}
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. 请注意,由于应用程序仅依赖于
client_manager
抽象(即client_manager::add()
和client_manager::get()
前置和后置条件),因此只要实现维护,就可以更改client_manager
实现而不会影响应用程序。前后条件。 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
. 例如,它可以使用序列容器(如
std::vector
)或有序关联容器(如std::map
来实现,而不是使用std::unordered_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. 在分析之后,如果容器选择是已识别的瓶颈,则根据实际使用情况更改
client_manager
的实现以使用更合适的容器。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.