所以,我知道如何找到我收到的客户端的唯一方法是通过比较所有客户端循环中的接收端点,我想知道是否有一种更优雅的方式来处理它。
在tcp中,每个客户端都有自己的套接字,通过它,它可以找到它立即收到的客户端。如果我让每个客户端在udp中都有自己的套接字,那么它的效率会更高或更低吗?
我还在考虑创建一个全局套接字,并让每个客户端对象只听取他们的端点,但我认为这不可能,或者在asio中有效。
答案 0 :(得分:2)
应用程序代码负责解复用。在高级别,有两种选择:
第一个选项将更容易支持更高的并发级别,因为每个客户端都可以控制其异步调用链的生命周期。虽然在第二个选项中每个客户端都可以有一个调用链,但控制生命周期会带来复杂性,因为所有异步调用链都绑定到同一个I / O对象。
另一方面,随着并发性的增加,内存也会增加。因此,第一个选项可能比第二个选项使用更多内存。此外,在第二个控制整体内存更容易,因为并发级别不是完全动态的。在任何一种情况下,reactor style operations都可用于缓解整体内存使用情况。
最后,从实现中抽象出应用程序,同时保持代码的可维护性。应用程序运行后,配置文件,识别瓶颈,并根据实际数据做出选择。
要稍微扩展第二个选项,以下是将端点与客户端对象关联的基本client_manager
的完整最小example:
#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::unordered_map
)或有序关联容器(例如std::vector
)来实现,而不是使用std::map
。选择最符合预期用途的容器。分析后,如果容器选择是已识别的瓶颈,则根据实际使用情况更改client_manager
的实现以使用更合适的容器。