tcp_socket.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2019, FZI Forschungszentrum Informatik (refactor)
3  *
4  * Copyright 2017, 2018 Jarek Potiuk (low bandwidth trajectory follower)
5  *
6  * Copyright 2017, 2018 Simon Rasmussen (refactor)
7  *
8  * Copyright 2015, 2016 Thomas Timm Andersen (original version)
9  *
10  * Licensed under the Apache License, Version 2.0 (the "License");
11  * you may not use this file except in compliance with the License.
12  * You may obtain a copy of the License at
13  *
14  * http://www.apache.org/licenses/LICENSE-2.0
15  *
16  * Unless required by applicable law or agreed to in writing, software
17  * distributed under the License is distributed on an "AS IS" BASIS,
18  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19  * See the License for the specific language governing permissions and
20  * limitations under the License.
21  */
22 
23 #include <endian.h>
24 #include <chrono>
25 #include <cstring>
26 #include <sstream>
27 #include <thread>
28 
29 #ifndef _WIN32
30 # include <arpa/inet.h>
31 # include <netinet/tcp.h>
32 #endif
33 
34 #include "ur_client_library/log.h"
36 
37 namespace urcl
38 {
39 namespace comm
40 {
42  : socket_fd_(INVALID_SOCKET), state_(SocketState::Invalid), reconnection_time_(std::chrono::seconds(10))
43 {
44 #ifdef _WIN32
45  WSAData data;
46  ::WSAStartup(MAKEWORD(1, 1), &data);
47 #endif // _WIN32
48 }
50 {
51  close();
52 }
53 
55 {
56  int flag = 1;
57  ur_setsockopt(socket_fd_, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(int));
58  ur_setsockopt(socket_fd_, IPPROTO_TCP, TCP_QUICKACK, &flag, sizeof(int));
59 
60  if (recv_timeout_ != nullptr)
61  {
62 #ifdef _WIN32
63  DWORD value = recv_timeout_->tv_sec * 1000;
64  value += recv_timeout_->tv_usec / 1000;
65  ur_setsockopt(socket_fd_, SOL_SOCKET, SO_RCVTIMEO, &value, sizeof(value));
66 #else
67  ur_setsockopt(socket_fd_, SOL_SOCKET, SO_RCVTIMEO, recv_timeout_.get(), sizeof(timeval));
68 #endif
69  }
70 }
71 
72 bool TCPSocket::setup(const std::string& host, const int port, const size_t max_num_tries,
73  const std::chrono::milliseconds reconnection_time)
74 {
75  // This can be removed once we remove the setReconnectionTime() method
76  auto reconnection_time_resolved = reconnection_time;
78  {
79  URCL_LOG_WARN("TCPSocket::setup(): Reconnection time was modified using `setReconnectionTime()` which is "
80  "deprecated. Please change your code to set reconnection_time through the `setup()` method "
81  "directly. The value passed to this function will be ignored.");
82  reconnection_time_resolved = reconnection_time_;
83  }
84 
86  return false;
87 
88  URCL_LOG_DEBUG("Setting up connection: %s:%d", host.c_str(), port);
89 
90  // gethostbyname() is deprecated so use getadderinfo() as described in:
91  // https://beej.us/guide/bgnet/html/#getaddrinfoprepare-to-launch
92 
93  const char* host_name = host.empty() ? nullptr : host.c_str();
94  std::string service = std::to_string(port);
95  struct addrinfo hints, *result;
96  std::memset(&hints, 0, sizeof(hints));
97 
98  hints.ai_family = AF_UNSPEC;
99  hints.ai_socktype = SOCK_STREAM;
100  hints.ai_flags = AI_PASSIVE;
101 
102  size_t connect_counter = 0;
103  bool connected = false;
104  while (!connected)
105  {
106  if (getaddrinfo(host_name, service.c_str(), &hints, &result) != 0)
107  {
108  URCL_LOG_ERROR("Failed to get address for %s:%d", host.c_str(), port);
109  return false;
110  }
111  // loop through the list of addresses untill we find one that's connectable
112  for (struct addrinfo* p = result; p != nullptr; p = p->ai_next)
113  {
114  socket_fd_ = ::socket(p->ai_family, p->ai_socktype, p->ai_protocol);
115 
116  if (socket_fd_ != -1 && open(socket_fd_, p->ai_addr, p->ai_addrlen))
117  {
118  connected = true;
119  break;
120  }
121  }
122 
123  freeaddrinfo(result);
124 
125  if (!connected)
126  {
128  if (++connect_counter >= max_num_tries && max_num_tries > 0)
129  {
130  URCL_LOG_ERROR("Failed to establish connection for %s:%d after %d tries", host.c_str(), port, max_num_tries);
131  return false;
132  }
133  else
134  {
135  std::stringstream ss;
136  ss << "Failed to connect to robot on IP " << host_name << ":" << port
137  << ". Please check that the robot is booted and reachable on " << host_name << ". Retrying in "
138  << std::chrono::duration_cast<std::chrono::duration<float>>(reconnection_time_resolved).count()
139  << " seconds.";
140  URCL_LOG_ERROR("%s", ss.str().c_str());
141  std::this_thread::sleep_for(reconnection_time_resolved);
142  }
143  }
144  }
145  setupOptions();
147  URCL_LOG_DEBUG("Connection established for %s:%d", host.c_str(), port);
148  return connected;
149 }
150 
152 {
153  if (socket_fd_ >= 0)
154  {
158  }
159 }
160 
161 std::string TCPSocket::getIP() const
162 {
163  sockaddr_in name;
164  socklen_t len = sizeof(name);
165  int res = ::getsockname(socket_fd_, (sockaddr*)&name, &len);
166 
167  if (res < 0)
168  {
169  URCL_LOG_ERROR("Could not get local IP");
170  return std::string();
171  }
172 
173  char buf[128];
174  inet_ntop(AF_INET, &name.sin_addr, buf, sizeof(buf));
175  return std::string(buf);
176 }
177 
178 bool TCPSocket::read(char* character)
179 {
180  size_t read_chars;
181  // It's inefficient, but in our case we read very small messages
182  // and the overhead connected with reading character by character is
183  // negligible - adding buffering would complicate the code needlessly.
184  return read((uint8_t*)character, 1, read_chars);
185 }
186 
187 bool TCPSocket::read(uint8_t* buf, const size_t buf_len, size_t& read)
188 {
189  read = 0;
190 
192  return false;
193 
194 #ifdef _WIN32
195  ssize_t res = ::recv(socket_fd_, reinterpret_cast<char*>(buf), static_cast<const socklen_t>(buf_len), 0);
196 #else
197  ssize_t res = ::recv(socket_fd_, buf, buf_len, 0);
198 #endif
199 
200  if (res == 0)
201  {
203  return false;
204  }
205  else if (res < 0)
206  {
207  res = 0;
208 #ifdef _WIN32
209  int code = ::WSAGetLastError();
210  if (code != WSAETIMEDOUT && code != WSAEWOULDBLOCK)
211  {
213  }
214 #else
215  if (!(errno == EAGAIN || errno == EWOULDBLOCK))
216  {
217  // any permanent error should be detected early
219  }
220 #endif
221  return false;
222  }
223 
224  read = static_cast<size_t>(res);
225  return true;
226 }
227 
228 bool TCPSocket::write(const uint8_t* buf, const size_t buf_len, size_t& written)
229 {
230  written = 0;
231 
233  {
234  URCL_LOG_ERROR("Attempt to write on a non-connected socket");
235  return false;
236  }
237 
238  size_t remaining = buf_len;
239 
240  // handle partial sends
241  while (written < buf_len)
242  {
243  ssize_t sent =
244  ::send(socket_fd_, reinterpret_cast<const char*>(buf + written), static_cast<socklen_t>(remaining), 0);
245 
246  if (sent <= 0)
247  {
248  URCL_LOG_ERROR("Sending data through socket failed.");
249  return false;
250  }
251 
252  written += static_cast<size_t>(sent);
253  remaining -= sent;
254  }
255 
256  return true;
257 }
258 
259 void TCPSocket::setReceiveTimeout(const timeval& timeout)
260 {
261  recv_timeout_.reset(new timeval(timeout));
262 
264  {
265  setupOptions();
266  }
267 }
268 
269 void TCPSocket::setReconnectionTime(const std::chrono::milliseconds reconnection_time)
270 {
271  URCL_LOG_ERROR("Calling setReconnectionTime is deprecated. Reconnection timeout is passed to the setup method "
272  "directly.");
273  reconnection_time_ = reconnection_time;
275 }
276 
277 } // namespace comm
278 } // namespace urcl
urcl::comm::TCPSocket::setReceiveTimeout
void setReceiveTimeout(const timeval &timeout)
Setup Receive timeout used for this socket.
Definition: tcp_socket.cpp:259
ur_setsockopt
#define ur_setsockopt
Definition: socket_t.h:63
urcl::comm::TCPSocket::TCPSocket
TCPSocket()
Creates a TCPSocket object.
Definition: tcp_socket.cpp:41
urcl::comm::TCPSocket::state_
std::atomic< SocketState > state_
Definition: tcp_socket.h:52
INVALID_SOCKET
#define INVALID_SOCKET
Definition: socket_t.h:60
urcl
Definition: bin_parser.h:36
urcl::comm::TCPSocket::setupOptions
void setupOptions()
Definition: tcp_socket.cpp:54
URCL_LOG_ERROR
#define URCL_LOG_ERROR(...)
Definition: log.h:26
urcl::comm::SocketState::Closed
@ Closed
Connection to socket got closed.
urcl::comm::TCPSocket::getIP
std::string getIP() const
Determines the local IP address of the currently configured socket.
Definition: tcp_socket.cpp:161
URCL_LOG_DEBUG
#define URCL_LOG_DEBUG(...)
Definition: log.h:23
urcl::comm::SocketState::Disconnected
@ Disconnected
Socket is disconnected and cannot be used.
urcl::comm::SocketState::Invalid
@ Invalid
Socket is initialized or setup failed.
urcl::comm::TCPSocket::setup
bool setup(const std::string &host, const int port, const size_t max_num_tries=0, const std::chrono::milliseconds reconnection_time=DEFAULT_RECONNECTION_TIME)
Definition: tcp_socket.cpp:72
urcl::comm::TCPSocket::write
bool write(const uint8_t *buf, const size_t buf_len, size_t &written)
Writes to the socket.
Definition: tcp_socket.cpp:228
urcl::comm::TCPSocket::socket_fd_
std::atomic< socket_t > socket_fd_
Definition: tcp_socket.h:51
urcl::comm::TCPSocket::close
void close()
Closes the connection to the socket.
Definition: tcp_socket.cpp:151
urcl::comm::TCPSocket::reconnection_time_modified_deprecated_
bool reconnection_time_modified_deprecated_
Definition: tcp_socket.h:54
log.h
urcl::comm::TCPSocket::read
bool read(char *character)
Reads one byte from the socket.
Definition: tcp_socket.cpp:178
urcl::comm::SocketState::Connected
@ Connected
Socket is connected and ready to use.
urcl::comm::TCPSocket::open
static bool open(socket_t socket_fd, struct sockaddr *address, size_t address_len)
Definition: tcp_socket.h:59
urcl::comm::TCPSocket::setReconnectionTime
void setReconnectionTime(const std::chrono::milliseconds reconnection_time)
Set reconnection time, if the server is unavailable during connection this will set the time before t...
Definition: tcp_socket.cpp:269
URCL_LOG_WARN
#define URCL_LOG_WARN(...)
Definition: log.h:24
urcl::comm::TCPSocket::~TCPSocket
virtual ~TCPSocket()
Definition: tcp_socket.cpp:49
urcl::comm::TCPSocket::recv_timeout_
std::unique_ptr< timeval > recv_timeout_
Definition: tcp_socket.h:67
ur_close
#define ur_close
Definition: socket_t.h:64
urcl::comm::SocketState
SocketState
State the socket can be in.
Definition: tcp_socket.h:37
urcl::comm::TCPSocket::reconnection_time_
std::chrono::milliseconds reconnection_time_
Definition: tcp_socket.h:53
tcp_socket.h
endian.h


ur_client_library
Author(s): Thomas Timm Andersen, Simon Rasmussen, Felix Exner, Lea Steffen, Tristan Schnell
autogenerated on Mon May 26 2025 02:35:58