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
7577bool 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