У меня есть класс, который управляет ресурсом (сетевой сокет).
Я написал класс ConnectionHandler
, который обрабатывает сетевой сокет, созданный из вызова к accept()
.
* 1006.* Этот класс разработан с учетом RAII, когда вызывается
accept()
, возвращаемый сокет помещается в
ConnectionHandler
, когда он выходит из области видимости, деструктор закрывает сокет.
Я такжеотслеживать все мои открытые ConnectionHandler
, сохраняя их на карте (сопоставляет адрес сокета (IP: порт) с ConnectionHandler
, который соответствует этому адресу).
У меня естьпроблема "размещения" этих ConnectionHandler
на карте, хотя.
Я сделал это так, что ConnectionHandler
не может быть скопировано (по крайней мере, я считаю, что я сделал это так),но при вызове std::map::emplace
вызывается деструктор ConnectionHandler
(предположительно для удаления временного объекта, созданного где-то вдоль линии), и сокет закрывается.
Как видите, это создает проблемупотому что теперь сокет не может использоваться дальше вниз по программе.
Есть ли ва?у меня, чтобы предотвратить вызов деструктора ConnectionHandler
при его использовании в std::map
?
Вот код для ConnectionHandler
: Заголовочный файл:
class ConnectionHandler
{
private:
constexpr static long BUFFER_SIZE = 1 << 12; // 4K Buffer
SocketAddress peer; // This is kept around to be able to produce clear exception messages when something goes wrong
SocketFileDescriptor socket; // using SocketFileDescriptor = int;
public:
ConnectionHandler() noexcept = delete; // Default Constructor
explicit ConnectionHandler(SocketFileDescriptor socket, const SocketAddress& socketAddress) noexcept; // Value Constructor
ConnectionHandler (ConnectionHandler&& handler) noexcept; // Move Constructor
ConnectionHandler (const ConnectionHandler& handler) = delete; // Delete Copy Constructor
ConnectionHandler& operator= (ConnectionHandler&& handler) noexcept; // Move Assignment Operator
ConnectionHandler& operator= (const ConnectionHandler& handler) = delete; // Delete Copy Assignment Operator
~ConnectionHandler(); // Destructor
void close() noexcept; // Allow the owner to manually close the socket if necessary
void set_blocking (bool blocking) const; // Make the socket either blocking or non-blocking
friend std::ostream& operator<< (std::ostream& stream, const ConnectionHandler& handler); // Receive data from the socket
friend std::istream& operator>> (std::istream& stream, const ConnectionHandler& handler); // Send data to the socket
};
И реализация:
ConnectionHandler::ConnectionHandler(SocketFileDescriptor socket, const SocketAddress& socketAddress) noexcept: peer(socketAddress), socket(socket)
{
}
ConnectionHandler::ConnectionHandler(ConnectionHandler&& handler) noexcept: peer(std::move(handler.peer)), socket(handler.socket)
{
}
ConnectionHandler& ConnectionHandler::operator=(ConnectionHandler&& handler) noexcept
{
this->peer = std::move(handler.peer);
this->socket = handler.socket;
return *this;
}
ConnectionHandler::~ConnectionHandler()
{
if (this->socket > 0) // Check if the socket has been closed manually
// Don't bother setting the socket to -1, the object is being destroyed anyway
{
std::cout << "Closing socket from destructor " << this->socket << std::endl;
::close(this->socket);
}
}
void ConnectionHandler::close() noexcept
{
std::cout << "Closing socket from close() " << this->socket << std::endl; // Close the socket manually and indicate it is closed by setting it's value to -1
::close(this->socket);
this->socket = -1;
}
[...]
Вот так выглядит класс SocketAddress (я знаю, что он не работает для IPv6):
class SocketAddress
{
private:
std::array<std::uint8_t, 4> ip;
std::uint16_t port;
public:
friend void swap (SocketAddress& sa1, SocketAddress& sa2) noexcept;
SocketAddress() noexcept;
explicit SocketAddress(struct sockaddr_storage* sockaddrStorage);
SocketAddress (const SocketAddress& address) = default;
SocketAddress (SocketAddress&& address) noexcept = default;
SocketAddress& operator= (SocketAddress address);
friend bool operator< (const SocketAddress& lhs, const SocketAddress& rhs) noexcept;
friend std::string to_string(const SocketAddress& address) noexcept;
};
И, наконец, вот кодэто создает ConnectionHandler и помещает его в карту:
void Server::listenLoop() // acceptLoop() would be a better name
{
struct sockaddr_storage remoteAddr;
while(!stop) // stop is a std::atomic<bool>
{
[...] // accept() connections in a loop
SocketAddress address = SocketAddress(&remoteAddr);
this->incomingSockets.emplace(std::make_pair(address, ConnectionHandler(childFileDesc, address)));
}
[...]
}
Эта функция выполняется в потоке, отдельном от основного потока, поток сохраняется в объекте Server и присоединяется в деструкторе объекта Server.