diff --git a/src/net/connection_client.cpp b/src/net/connection_client.cpp index 793ceafc9d9bd84def3a571e6228755cd9c0363a..dbe588ba141ee3baff77d198b2b1f489eedb5dc0 100644 --- a/src/net/connection_client.cpp +++ b/src/net/connection_client.cpp @@ -15,6 +15,7 @@ namespace rmrf::net { if (this->server_active) { io.stop(); + this->net_socket.reset(); } if (destructor_cb) { @@ -31,7 +32,7 @@ namespace rmrf::net { } if (events & ::ev::READ) { - if(this->in_data_cb != nullptr) + if (this->in_data_cb != nullptr) this->read_from_socket(w); } @@ -52,19 +53,27 @@ namespace rmrf::net { if (partial_write_allowed) this->write_queue.push_front(buffer); + this->data_write_active = false; } } set_new_flags(); } - + void connection_client::stop_server() { this->server_active = false; + this->net_socket.reset(); + this->write_queue.clear(); io.stop(); + async.stop(); } void connection_client::set_incomming_data_callback(const incomming_data_cb &cb) { + if (!this->server_active) { + return; + } + this->in_data_cb = cb; this->async.send(); } diff --git a/src/net/connection_client.hpp b/src/net/connection_client.hpp index 3cb2f54de9df8c91ec6c6280dadfd3a8d9d896f5..2a42a7924842433527a2436c7e93d838ae96b1d4 100644 --- a/src/net/connection_client.hpp +++ b/src/net/connection_client.hpp @@ -165,6 +165,10 @@ public: return this->rate_limit; } + [[nodiscard]] inline bool is_client_alive() { + return this->server_active; + } + protected: /** * @brief Implement the read operation of the implemented service @@ -195,8 +199,12 @@ private: void cb_async(::ev::async& w, int events); inline void set_new_flags() { + if (!this->server_active) { + return; + } + auto new_flags = 0; - if(this->in_data_cb) { + if (this->in_data_cb) { new_flags |= ::ev::READ; } if (!this->write_queue.empty()) { diff --git a/src/net/ioqueue.hpp b/src/net/ioqueue.hpp index 1a9bbc2e4ce335d18b63de64094fa98c70a08b0e..611833fae4431a4510121b6da5b4af7b79f6ef68 100644 --- a/src/net/ioqueue.hpp +++ b/src/net/ioqueue.hpp @@ -79,6 +79,10 @@ namespace rmrf::net { [[nodiscard]] inline std::deque<iorecord_type>::const_iterator end() const { return this->queue.end(); } + + void clear() { + this->queue.clear(); + } }; } diff --git a/src/net/tcp_client.cpp b/src/net/tcp_client.cpp index 70a15bf8142a4149e0b1c1d362e34c4429fec6e4..ccbfca8e3ac5469111decffd9b16b34bc15eb85b 100644 --- a/src/net/tcp_client.cpp +++ b/src/net/tcp_client.cpp @@ -35,89 +35,6 @@ tcp_client::tcp_client( this->own_address = own_address_; } -/* -// TODO move to client_factory -auto_fd tcp_client::get_connection(const std::string& peer_address_, const std::string& service_or_port, int ip_addr_family) { - std::list<socketaddr> connection_candidates = get_socketaddr_list(peer_address_, service_or_port, socket_t::TCP); - - if (ip_addr_family == AF_UNSPEC) { - ip_addr_family = connection_candidates.front().family(); - } - - if (!(ip_addr_family == AF_INET || ip_addr_family == AF_INET6)) { - throw netio_exception("Invalid IP address family."); - } - - // TODO build another nice HL structure wrapper for outbound connections - int status = 1; - - do { - if (connection_candidates.empty()) { - throw netio_exception("Unable to find suitable connection candidate."); - } - - socketaddr socket_identifier = connection_candidates.front(); - connection_candidates.pop_front(); - auto_fd socket_candidate{socket(socket_identifier.family(), SOCK_STREAM, 0)}; - - if (socket_candidate.valid()) { - if (connect(socket_candidate.get(), socket_identifier.ptr(), socket_identifier.size()) == 0) { - status = 0; - - if ( - const auto existing_fd_flags = fcntl(socket_candidate.get(), F_GETFL, 0); - existing_fd_flags == -1 || fcntl(socket_candidate.get(), F_SETFL, existing_fd_flags | O_NONBLOCK) == -1 - ) { - throw netio_exception("Failed to set socket mode. fcntl resulted in error:" + std::to_string(errno)); - } - - // Hier bin ich mir nicht sicher, wie ich das am besten mache. Auch mit socketaddr und type cast ist das irgendwie doof. - // Das Problem besteht darin, dass erst nach erfolgreichem connect der Port auf dieser Seite bekannt ist. - socketaddr sa_local; - socklen_t sa_local_len = sizeof(sockaddr_storage); - - if (getsockname(socket_candidate.get(), sa_local.ptr(), &sa_local_len)) { - // Update length field after the internal structure was modified - // TODO: Maybe make this an internal method in socketaddr to update the size - sa_local = sa_local.ptr(); - this->own_address = sa_local; - } - - // Store remote connection endpoint (that is prior to redirect) - this->peer_address = socket_identifier; - return std::forward<auto_fd>(socket_candidate); - } - } - - // We don't need to worry about closing broken fd as auto_fd handles this for us - } while (status == 1); - return null_fd{}; -} - -// TODO remove and replace with raw socketaddr constructor from factory and implement connect construcor -tcp_client::tcp_client( - const std::string& peer_address_, - const std::string& service_or_port, - int ip_addr_family -) : - connection_client{get_connection(peer_address_, service_or_port, ip_addr_family), this->get_peer_address()}, - destructor_cb(nullptr) -{} - -tcp_client::tcp_client( - const std::string &peer_address_, - const std::string &service_or_port -) : - tcp_client(peer_address_, service_or_port, AF_UNSPEC) -{} - -tcp_client::tcp_client( - const std::string &peer_address_, - const uint16_t port_ -) : - tcp_client(peer_address_, std::to_string(port_)) -{}*/ - tcp_client::~tcp_client() {} ssize_t tcp_client::push_write_queue(::ev::io& w, iorecord& buffer) { @@ -131,6 +48,31 @@ inline uint16_t tcp_client::get_port() { return optional_port.value(); } +inline std::string get_recv_error() { + switch (errno) { + case EBADF: + return "The argument sockfd is an invalid file descriptor."; + case ECONNREFUSED: + return "A remote host refused to allow the network connection."; + case EFAULT: + return "The receive buffer pointer(s) point outside the process's address space."; + case EINTR: + return "The receive was interrupted by delivery of a signal before any data was available."; + case EINVAL: + return "Invalid argument passed."; + case ENOMEM: + return "Could not allocate memory for recvmsg()."; + case ENOTCONN: + return "The socket is associated with a connection-oriented protocol and has not been connected."; + case ENOTSOCK: + return "The file descriptor sockfd does not refer to a socket."; + case 104: + return "Connection Reset by peer (Linux bug)"; + default: + return "Error Code not captured: " + std::to_string(errno); + } +} + void tcp_client::read_from_socket(::ev::io& w) { // notify incomming_data_cb uint8_t buffer[1024]; @@ -138,7 +80,12 @@ void tcp_client::read_from_socket(::ev::io& w) { ssize_t n_read_bytes = recv(w.fd, buffer, sizeof(buffer), 0); if (n_read_bytes < 0) { - throw netio_exception("Failed to read from network socket in TCP client."); + if (errno == ECONNRESET) { + this->stop_server(); + return; + } else if (errno != EAGAIN) { + throw netio_exception("Failed to read from network socket in TCP client. " + get_recv_error()); + } } if (n_read_bytes == 0) {