diff --git a/include/ur_client_library/comm/tcp_server.h b/include/ur_client_library/comm/tcp_server.h index feb91065..8cad8dde 100644 --- a/include/ur_client_library/comm/tcp_server.h +++ b/include/ur_client_library/comm/tcp_server.h @@ -35,6 +35,7 @@ #include #include +#include #include #include @@ -60,7 +61,17 @@ class TCPServer { public: TCPServer() = delete; - TCPServer(const int port); + /*! + * \brief Create a TCPServer object + * + * \param port Port on which to operate. The port will be bound to the process creating the + * object. + * \param max_num_tries If binding the socket fails, it will be retried this many times. If 0 is + * specified, binding the socket will be tried indefinitely. + * \param reconnection_time Wait time in between binding attempts. + */ + explicit TCPServer(const int port, const size_t max_num_tries = 0, + const std::chrono::milliseconds reconnection_time = std::chrono::seconds(1)); virtual ~TCPServer(); /*! @@ -147,7 +158,7 @@ class TCPServer private: void init(); - void bind(); + void bind(const size_t max_num_tries, const std::chrono::milliseconds reconnection_time); void startListen(); //! Handles connection events diff --git a/src/comm/tcp_server.cpp b/src/comm/tcp_server.cpp index 8fa7bd81..fae39351 100644 --- a/src/comm/tcp_server.cpp +++ b/src/comm/tcp_server.cpp @@ -42,10 +42,11 @@ namespace urcl { namespace comm { -TCPServer::TCPServer(const int port) : port_(port), maxfd_(0), max_clients_allowed_(0) +TCPServer::TCPServer(const int port, const size_t max_num_tries, const std::chrono::milliseconds reconnection_time) + : port_(port), maxfd_(0), max_clients_allowed_(0) { init(); - bind(); + bind(max_num_tries, reconnection_time); startListen(); } @@ -124,7 +125,7 @@ void TCPServer::shutdown() } } -void TCPServer::bind() +void TCPServer::bind(const size_t max_num_tries, const std::chrono::milliseconds reconnection_time) { struct sockaddr_in server_addr; server_addr.sin_family = AF_INET; @@ -132,13 +133,30 @@ void TCPServer::bind() // INADDR_ANY is a special constant that signalizes "ANY IFACE", server_addr.sin_addr.s_addr = htonl(INADDR_ANY); server_addr.sin_port = htons(port_); - int err = ::bind(listen_fd_, (struct sockaddr*)&server_addr, sizeof(server_addr)); - if (err == -1) + int err = -1; + size_t connection_counter = 0; + do { - std::ostringstream ss; - ss << "Failed to bind socket for port " << port_ << " to address. Reason: " << strerror(errno); - throw std::system_error(std::error_code(errno, std::generic_category()), ss.str()); - } + err = ::bind(listen_fd_, (struct sockaddr*)&server_addr, sizeof(server_addr)); + if (err == -1) + { + std::ostringstream ss; + ss << "Failed to bind socket for port " << port_ << " to address. Reason: " << strerror(errno); + + if (connection_counter++ < max_num_tries || max_num_tries == 0) + { + std::this_thread::sleep_for(reconnection_time); + ss << "Retrying in " << std::chrono::duration_cast>(reconnection_time).count() + << " seconds"; + URCL_LOG_WARN("%s", ss.str().c_str()); + } + else + { + throw std::system_error(std::error_code(errno, std::generic_category()), ss.str()); + } + } + } while (err == -1 && (connection_counter <= max_num_tries || max_num_tries == 0)); + URCL_LOG_DEBUG("Bound %d:%d to FD %d", server_addr.sin_addr.s_addr, port_, (int)listen_fd_); FD_SET(listen_fd_, &masterfds_); diff --git a/tests/test_tcp_server.cpp b/tests/test_tcp_server.cpp index 61d10c45..0cec4b21 100644 --- a/tests/test_tcp_server.cpp +++ b/tests/test_tcp_server.cpp @@ -170,7 +170,7 @@ TEST_F(TCPServerTest, socket_creation) comm::TCPServer server(port_); // Shouldn't be able to create antoher server on same port - EXPECT_THROW(comm::TCPServer server2(port_), std::system_error); + EXPECT_THROW(comm::TCPServer server2(port_, 1, std::chrono::milliseconds(1)), std::system_error); server.start(); @@ -326,6 +326,12 @@ TEST_F(TCPServerTest, client_connections) EXPECT_FALSE(server.write(client2_fd, data, len, written)); EXPECT_FALSE(server.write(client3_fd, data, len, written)); } +TEST_F(TCPServerTest, check_address_already_in_use) +{ + comm::TCPServer blocking_server(12321); + + EXPECT_THROW(comm::TCPServer test_server(12321, 2, std::chrono::milliseconds(500)), std::system_error); +} int main(int argc, char* argv[]) {