Skip to content

Commit e2bebd6

Browse files
author
Felix Exner (fexner)
authored
Make tcp_server retry binding the socket (#176)
It sometimes happens that binding the socket fails since it is already in use (e.g. when restarting the driver too fast). This commit should retry binding instead of simply throwing an exception
1 parent b304e41 commit e2bebd6

File tree

3 files changed

+47
-12
lines changed

3 files changed

+47
-12
lines changed

include/ur_client_library/comm/tcp_server.h

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
#include <unistd.h>
3636

3737
#include <atomic>
38+
#include <chrono>
3839
#include <functional>
3940
#include <thread>
4041

@@ -60,7 +61,17 @@ class TCPServer
6061
{
6162
public:
6263
TCPServer() = delete;
63-
TCPServer(const int port);
64+
/*!
65+
* \brief Create a TCPServer object
66+
*
67+
* \param port Port on which to operate. The port will be bound to the process creating the
68+
* object.
69+
* \param max_num_tries If binding the socket fails, it will be retried this many times. If 0 is
70+
* specified, binding the socket will be tried indefinitely.
71+
* \param reconnection_time Wait time in between binding attempts.
72+
*/
73+
explicit TCPServer(const int port, const size_t max_num_tries = 0,
74+
const std::chrono::milliseconds reconnection_time = std::chrono::seconds(1));
6475
virtual ~TCPServer();
6576

6677
/*!
@@ -147,7 +158,7 @@ class TCPServer
147158

148159
private:
149160
void init();
150-
void bind();
161+
void bind(const size_t max_num_tries, const std::chrono::milliseconds reconnection_time);
151162
void startListen();
152163

153164
//! Handles connection events

src/comm/tcp_server.cpp

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,11 @@ namespace urcl
4242
{
4343
namespace comm
4444
{
45-
TCPServer::TCPServer(const int port) : port_(port), maxfd_(0), max_clients_allowed_(0)
45+
TCPServer::TCPServer(const int port, const size_t max_num_tries, const std::chrono::milliseconds reconnection_time)
46+
: port_(port), maxfd_(0), max_clients_allowed_(0)
4647
{
4748
init();
48-
bind();
49+
bind(max_num_tries, reconnection_time);
4950
startListen();
5051
}
5152

@@ -124,21 +125,38 @@ void TCPServer::shutdown()
124125
}
125126
}
126127

127-
void TCPServer::bind()
128+
void TCPServer::bind(const size_t max_num_tries, const std::chrono::milliseconds reconnection_time)
128129
{
129130
struct sockaddr_in server_addr;
130131
server_addr.sin_family = AF_INET;
131132

132133
// INADDR_ANY is a special constant that signalizes "ANY IFACE",
133134
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
134135
server_addr.sin_port = htons(port_);
135-
int err = ::bind(listen_fd_, (struct sockaddr*)&server_addr, sizeof(server_addr));
136-
if (err == -1)
136+
int err = -1;
137+
size_t connection_counter = 0;
138+
do
137139
{
138-
std::ostringstream ss;
139-
ss << "Failed to bind socket for port " << port_ << " to address. Reason: " << strerror(errno);
140-
throw std::system_error(std::error_code(errno, std::generic_category()), ss.str());
141-
}
140+
err = ::bind(listen_fd_, (struct sockaddr*)&server_addr, sizeof(server_addr));
141+
if (err == -1)
142+
{
143+
std::ostringstream ss;
144+
ss << "Failed to bind socket for port " << port_ << " to address. Reason: " << strerror(errno);
145+
146+
if (connection_counter++ < max_num_tries || max_num_tries == 0)
147+
{
148+
std::this_thread::sleep_for(reconnection_time);
149+
ss << "Retrying in " << std::chrono::duration_cast<std::chrono::duration<float>>(reconnection_time).count()
150+
<< " seconds";
151+
URCL_LOG_WARN("%s", ss.str().c_str());
152+
}
153+
else
154+
{
155+
throw std::system_error(std::error_code(errno, std::generic_category()), ss.str());
156+
}
157+
}
158+
} while (err == -1 && (connection_counter <= max_num_tries || max_num_tries == 0));
159+
142160
URCL_LOG_DEBUG("Bound %d:%d to FD %d", server_addr.sin_addr.s_addr, port_, (int)listen_fd_);
143161

144162
FD_SET(listen_fd_, &masterfds_);

tests/test_tcp_server.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ TEST_F(TCPServerTest, socket_creation)
170170
comm::TCPServer server(port_);
171171

172172
// Shouldn't be able to create antoher server on same port
173-
EXPECT_THROW(comm::TCPServer server2(port_), std::system_error);
173+
EXPECT_THROW(comm::TCPServer server2(port_, 1, std::chrono::milliseconds(1)), std::system_error);
174174

175175
server.start();
176176

@@ -326,6 +326,12 @@ TEST_F(TCPServerTest, client_connections)
326326
EXPECT_FALSE(server.write(client2_fd, data, len, written));
327327
EXPECT_FALSE(server.write(client3_fd, data, len, written));
328328
}
329+
TEST_F(TCPServerTest, check_address_already_in_use)
330+
{
331+
comm::TCPServer blocking_server(12321);
332+
333+
EXPECT_THROW(comm::TCPServer test_server(12321, 2, std::chrono::milliseconds(500)), std::system_error);
334+
}
329335

330336
int main(int argc, char* argv[])
331337
{

0 commit comments

Comments
 (0)