Skip to content

Commit ad2467e

Browse files
committed
refactor: TCPSocket::setup to support timeouts and improve error handling
1 parent de90b90 commit ad2467e

2 files changed

Lines changed: 90 additions & 6 deletions

File tree

include/ur_client_library/comm/tcp_socket.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,12 +62,14 @@ class TCPSocket
6262
}
6363

6464
bool setup(const std::string& host, const int port, const size_t max_num_tries = 0,
65-
const std::chrono::milliseconds reconnection_time = DEFAULT_RECONNECTION_TIME);
65+
const std::chrono::milliseconds reconnection_time = DEFAULT_RECONNECTION_TIME,
66+
const std::chrono::milliseconds timeout = DEFAULT_CONNECTION_TIME);
6667

6768
std::unique_ptr<timeval> recv_timeout_;
6869

6970
public:
7071
static constexpr std::chrono::milliseconds DEFAULT_RECONNECTION_TIME{ 10000 };
72+
static constexpr std::chrono::milliseconds DEFAULT_CONNECTION_TIME{ 500 };
7173
/*!
7274
* \brief Creates a TCPSocket object
7375
*/

src/comm/tcp_socket.cpp

Lines changed: 87 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
#ifndef _WIN32
2929
# include <arpa/inet.h>
3030
# include <netinet/tcp.h>
31+
# include <fcntl.h>
32+
# include <sys/select.h>
3133
#endif
3234

3335
#include "ur_client_library/log.h"
@@ -73,7 +75,7 @@ void TCPSocket::setupOptions()
7375
}
7476

7577
bool TCPSocket::setup(const std::string& host, const int port, const size_t max_num_tries,
76-
const std::chrono::milliseconds reconnection_time)
78+
const std::chrono::milliseconds reconnection_time, const std::chrono::milliseconds timeout)
7779
{
7880
// This can be removed once we remove the setReconnectionTime() method
7981
auto reconnection_time_resolved = reconnection_time;
@@ -111,16 +113,96 @@ bool TCPSocket::setup(const std::string& host, const int port, const size_t max_
111113
URCL_LOG_ERROR("Failed to get address for %s:%d", host.c_str(), port);
112114
return false;
113115
}
114-
// loop through the list of addresses untill we find one that's connectable
116+
117+
std::error_code socket_error;
118+
115119
for (struct addrinfo* p = result; p != nullptr; p = p->ai_next)
116120
{
117121
socket_fd_ = ::socket(p->ai_family, p->ai_socktype, p->ai_protocol);
118122

119-
if (socket_fd_ != -1 && open(socket_fd_, p->ai_addr, p->ai_addrlen))
123+
if (socket_fd_ == -1)
124+
{
125+
socket_error = getLastSocketErrorCode();
126+
continue;
127+
}
128+
129+
#ifndef _WIN32
130+
int flags = ::fcntl(socket_fd_, F_GETFL, 0);
131+
::fcntl(socket_fd_, F_SETFL, flags | O_NONBLOCK);
132+
133+
int connect_res = ::connect(socket_fd_, p->ai_addr, static_cast<socklen_t>(p->ai_addrlen));
134+
bool connect_success = false;
135+
136+
if (connect_res == 0)
137+
{
138+
connect_success = true;
139+
}
140+
else if (errno == EINPROGRESS)
141+
{
142+
auto timeout_ms = std::chrono::duration_cast<std::chrono::milliseconds>(timeout).count();
143+
struct timeval tv;
144+
tv.tv_sec = timeout_ms / 1000;
145+
tv.tv_usec = (timeout_ms % 1000) * 1000;
146+
147+
fd_set write_fds;
148+
FD_ZERO(&write_fds);
149+
FD_SET(socket_fd_, &write_fds);
150+
151+
int select_res = ::select(socket_fd_ + 1, nullptr, &write_fds, nullptr, &tv);
152+
153+
if (select_res > 0)
154+
{
155+
int so_error = 0;
156+
socklen_t len = sizeof(so_error);
157+
::getsockopt(socket_fd_, SOL_SOCKET, SO_ERROR, &so_error, &len);
158+
if (so_error == 0)
159+
{
160+
connect_success = true;
161+
}
162+
else
163+
{
164+
socket_error = std::error_code(so_error, std::generic_category());
165+
}
166+
}
167+
else if (select_res == 0)
168+
{
169+
socket_error = std::make_error_code(std::errc::timed_out);
170+
}
171+
else
172+
{
173+
socket_error = getLastSocketErrorCode();
174+
}
175+
}
176+
else
177+
{
178+
socket_error = getLastSocketErrorCode();
179+
}
180+
181+
::fcntl(socket_fd_, F_SETFL, flags);
182+
183+
if (connect_success)
184+
{
185+
connected = true;
186+
break;
187+
}
188+
else
189+
{
190+
::ur_close(socket_fd_);
191+
socket_fd_ = INVALID_SOCKET;
192+
}
193+
#else
194+
if (open(socket_fd_, p->ai_addr, p->ai_addrlen))
120195
{
121196
connected = true;
122197
break;
123198
}
199+
else
200+
{
201+
socket_error = getLastSocketErrorCode();
202+
::ur_close(socket_fd_);
203+
socket_fd_ = INVALID_SOCKET;
204+
}
205+
#endif
124206
}
125207

126208
freeaddrinfo(result);
@@ -136,8 +218,8 @@ bool TCPSocket::setup(const std::string& host, const int port, const size_t max_
136218
else
137219
{
138220
std::stringstream ss;
139-
ss << "Failed to connect to robot on IP " << host_name << ":" << port
140-
<< ". Please check that the robot is booted and reachable on " << host_name << ". Retrying in "
221+
ss << "Failed to connect to robot on IP " << host_name << ":" << port << ". Reason: " << socket_error.message()
222+
<< ". Retrying in "
141223
<< std::chrono::duration_cast<std::chrono::duration<float>>(reconnection_time_resolved).count()
142224
<< " seconds.";
143225
URCL_LOG_ERROR("%s", ss.str().c_str());

0 commit comments

Comments
 (0)