MRPT  2.0.4
CClientTCPSocket.cpp
Go to the documentation of this file.
1 /* +------------------------------------------------------------------------+
2  | Mobile Robot Programming Toolkit (MRPT) |
3  | https://www.mrpt.org/ |
4  | |
5  | Copyright (c) 2005-2020, Individual contributors, see AUTHORS file |
6  | See: https://www.mrpt.org/Authors - All rights reserved. |
7  | Released under BSD License. See: https://www.mrpt.org/License |
8  +------------------------------------------------------------------------+ */
9 
10 #include "comms-precomp.h" // Precompiled headers
11 
13 #include <mrpt/comms/net_utils.h>
14 #include <mrpt/core/exceptions.h>
15 #include <cstring>
16 
17 #ifdef _WIN32
18 // Windows
19 #define _WINSOCK_DEPRECATED_NO_WARNINGS
20 #include <winerror.h>
21 #include <winsock2.h>
22 #if defined(_MSC_VER)
23 #pragma comment(lib, "WS2_32.LIB")
24 #endif
25 #else
26 // Linux, Apple
27 #define INVALID_SOCKET (-1)
28 #include <arpa/inet.h>
29 #include <fcntl.h>
30 #include <netdb.h>
31 #include <netinet/in.h>
32 #include <netinet/tcp.h>
33 #include <sys/ioctl.h>
34 #include <sys/socket.h>
35 #include <sys/types.h>
36 #include <unistd.h>
37 #include <cerrno>
38 #endif
39 
40 using namespace mrpt::comms;
41 using namespace mrpt::system;
42 using namespace mrpt;
43 using namespace std;
44 
45 unsigned int CClientTCPSocket::DNS_LOOKUP_TIMEOUT_MS = 3000;
46 
48 {
50 
51 #ifdef _WIN32
52  // Init the WinSock Library:
53  // ----------------------------
54  WORD wVersionRequested;
55  WSADATA wsaData;
56 
57  wVersionRequested = MAKEWORD(2, 0);
58 
59  if (WSAStartup(wVersionRequested, &wsaData))
60  THROW_EXCEPTION("Error calling WSAStartup");
61 
62  m_hSock = INVALID_SOCKET;
63 #else
64  // Linux, Apple
65  m_hSock = -1;
66 #endif
67  MRPT_END
68 }
69 
71 {
72  try
73  {
74  close();
75  }
76  catch (const std::exception& e)
77  {
78  std::cerr << "[~CClientTCPSocket] Exception:\n"
80  }
81 #ifdef _WIN32
82  WSACleanup();
83 #else
84 // Nothing else to do.
85 #endif
86 }
87 
89 {
91 
92 #ifdef _WIN32
93  // Delete socket:
94  if (m_hSock != INVALID_SOCKET)
95  {
96  shutdown(m_hSock, 2); // SD_BOTH );
97  closesocket(m_hSock);
98  m_hSock = INVALID_SOCKET;
99  }
100 #else
101  // Delete socket:
102  if (m_hSock != -1)
103  {
104  shutdown(m_hSock, SHUT_RDWR);
105  ::close(m_hSock);
106  m_hSock = -1;
107  }
108 #endif
109  MRPT_END
110 }
111 
112 /*---------------------------------------------------------------
113  Read
114  ---------------------------------------------------------------*/
115 size_t CClientTCPSocket::Read(void* Buffer, size_t Count)
116 {
117  MRPT_START
118 
119  return readAsync(Buffer, Count);
120 
121  MRPT_END
122 }
123 
124 /*---------------------------------------------------------------
125  Write
126  ---------------------------------------------------------------*/
127 size_t CClientTCPSocket::Write(const void* Buffer, size_t Count)
128 {
129  MRPT_START
130 
131  return writeAsync(Buffer, Count);
132 
133  MRPT_END
134 }
135 
136 void CClientTCPSocket::sendString(const std::string& str)
137 {
138  Write(str.c_str(), str.size());
139 }
140 
141 /*---------------------------------------------------------------
142  connect
143  ---------------------------------------------------------------*/
145  const std::string& remotePartAddress, unsigned short remotePartTCPPort,
146  unsigned int timeout_ms)
147 {
148  MRPT_START
149 
150  // Close existing socket, if any.
151  if (m_hSock != INVALID_SOCKET) close();
152 
153  // Create the socket:
154  if (INVALID_SOCKET == (m_hSock = socket(AF_INET, SOCK_STREAM, 0)))
156  "Error creating new client socket:\n%s",
157  getLastErrorStr().c_str()));
158 
159  struct sockaddr_in otherAddress;
160 
161  otherAddress.sin_family = AF_INET;
162  otherAddress.sin_port = htons(remotePartTCPPort);
163 
164  // Resolve the IP address of the given host name
165  std::string solved_IP;
167  remotePartAddress, solved_IP, DNS_LOOKUP_TIMEOUT_MS))
169  "DNS lookup failed for '%s'", remotePartAddress.c_str());
170 
171  // Fill out from IP address text:
172  otherAddress.sin_addr.s_addr = inet_addr(solved_IP.c_str());
173  if (INADDR_NONE == otherAddress.sin_addr.s_addr)
175  "Invalid IP address provided: %s", solved_IP.c_str());
176 
177 // Set to NON-BLOCKING:
178 #ifdef _WIN32
179  unsigned long non_block_mode = 1;
180  if (ioctlsocket(m_hSock, FIONBIO, &non_block_mode))
181  THROW_EXCEPTION("Error entering non-blocking mode with ioctlsocket();");
182 #else
183  int oldflags = fcntl(m_hSock, F_GETFL, 0);
184  if (oldflags == -1) THROW_EXCEPTION("Error retrieving fcntl();of socket.");
185  oldflags |= O_NONBLOCK; // Set NON-BLOCKING
186  if (-1 == fcntl(m_hSock, F_SETFL, oldflags))
187  THROW_EXCEPTION("Error entering non-blocking mode with fcntl();");
188 #endif
189 
190  // Try to connect:
191  int r = ::connect(
192  m_hSock, (struct sockaddr*)&otherAddress, sizeof(otherAddress));
193 #ifdef _WIN32
194  int er = WSAGetLastError();
195  if (r < 0 && er != WSAEINPROGRESS && er != WSAEWOULDBLOCK)
196 #else
197  int er = errno;
198  if (r < 0 && er != EINPROGRESS)
199 #endif
201  "Error connecting to %s:%hu. Error: %s [%d]",
202  remotePartAddress.c_str(), remotePartTCPPort, strerror(er), er));
203 
204  // Wait for connect:
205  timeval timer = {0, 0};
206  fd_set ss_write, ss_errors;
207  FD_ZERO(&ss_write);
208  FD_ZERO(&ss_errors);
209  FD_SET(m_hSock, &ss_write);
210  FD_SET(m_hSock, &ss_errors);
211 
212  timer.tv_sec = timeout_ms / 1000;
213  timer.tv_usec = 1000 * (timeout_ms % 1000);
214 
215  int sel_ret = select(
216  m_hSock + 1,
217  nullptr, // For read
218  &ss_write, // For write or *connect done*
219  &ss_errors, // For errors
220  timeout_ms == 0 ? nullptr : &timer);
221 
222  if (sel_ret == 0)
224  "Timeout connecting to '%s:%hu':\n%s", remotePartAddress.c_str(),
225  remotePartTCPPort, getLastErrorStr().c_str()));
226  if (sel_ret == -1)
228  "Error connecting to '%s:%hu':\n%s", remotePartAddress.c_str(),
229  remotePartTCPPort, getLastErrorStr().c_str()));
230 
231  // Now, make sure it was not an error!
232  int valopt;
233 #ifdef _WIN32
234  int lon = sizeof(int);
235  getsockopt(m_hSock, SOL_SOCKET, SO_ERROR, (char*)(&valopt), &lon);
236 #else
237  socklen_t lon = sizeof(int);
238  getsockopt(m_hSock, SOL_SOCKET, SO_ERROR, (void*)(&valopt), &lon);
239 #endif
240 
241 #ifdef _WIN32
242  if (valopt)
244  "Error connecting to %s:%hu. Error: %i.", remotePartAddress.c_str(),
245  remotePartTCPPort, valopt));
246 #else
247  if (valopt)
249  "Error connecting to %s:%hu. Error: %s.", remotePartAddress.c_str(),
250  remotePartTCPPort, strerror(valopt)));
251 #endif
252 // Connected!
253 
254 // If connected OK, remove the non-blocking flag:
255 #ifdef _WIN32
256  non_block_mode = 0;
257  if (ioctlsocket(m_hSock, FIONBIO, &non_block_mode))
258  THROW_EXCEPTION("Error entering blocking mode with ioctlsocket();");
259 #else
260  oldflags &= ~O_NONBLOCK; // Set BLOCKING
261  if (-1 == fcntl(m_hSock, F_SETFL, oldflags))
262  THROW_EXCEPTION("Error entering blocking mode with fcntl();");
263 #endif
264 
265  // Save the IP of the other part.
266  m_remotePartIP = remotePartAddress;
267 
268  MRPT_END
269 }
270 
271 /*---------------------------------------------------------------
272  isConnected
273  ---------------------------------------------------------------*/
274 bool CClientTCPSocket::isConnected() { return (m_hSock != INVALID_SOCKET); }
275 /*---------------------------------------------------------------
276  readAsync
277  ---------------------------------------------------------------*/
279  void* Buffer, const size_t Count, const int timeoutStart_ms,
280  const int timeoutBetween_ms)
281 {
282  MRPT_START
283 
284  if (m_hSock == INVALID_SOCKET) return 0; // The socket is not connected!
285 
286  size_t remainToRead, alreadyRead = 0;
287  int readNow;
288  bool timeoutExpired = false;
289 
290  struct timeval timeoutSelect = {0, 0};
291  struct timeval* ptrTimeout;
292  fd_set sockArr;
293 
294  // Init fd_set structure & add our socket to it:
295  FD_ZERO(&sockArr);
296  FD_SET(m_hSock, &sockArr);
297 
298  // Loop until timeout expires or the socket is closed.
299  while (alreadyRead < Count && !timeoutExpired)
300  {
301  // Use the "first" or "between" timeouts:
302  int curTimeout = alreadyRead == 0 ? timeoutStart_ms : timeoutBetween_ms;
303 
304  if (curTimeout < 0)
305  ptrTimeout = nullptr;
306  else
307  {
308  timeoutSelect.tv_sec = curTimeout / 1000;
309  timeoutSelect.tv_usec = 1000 * (curTimeout % 1000);
310  ptrTimeout = &timeoutSelect;
311  }
312 
313  // Wait for received data
314  int selRet = ::select(
315  m_hSock + 1, // __nfds
316  &sockArr, // Wait for read
317  nullptr, // Wait for write
318  nullptr, // Wait for except.
319  ptrTimeout); // Timeout
320 
321  if (selRet == INVALID_SOCKET)
323  "Error reading from socket: %s", getLastErrorStr().c_str());
324 
325  if (selRet == 0)
326  {
327  // Timeout:
328  timeoutExpired = true;
329  }
330  else
331  {
332  // Compute remaining part:
333  remainToRead = Count - alreadyRead;
334 
335  // Receive bytes:
336  readNow = ::recv(
337  m_hSock, ((char*)Buffer) + alreadyRead, (int)remainToRead, 0);
338 
339  if (readNow != INVALID_SOCKET)
340  {
341  // Accumulate the received length:
342  alreadyRead += readNow;
343  }
344  else
345  {
346  // Error: Socket closed?
347  this->close();
348  return alreadyRead;
349  }
350 
351  if (readNow == 0 && remainToRead != 0)
352  {
353  // We had an event of data available, so if we have now a zero,
354  // the socket has been gracefully closed:
355  timeoutExpired = true;
356  close();
357  }
358  }
359  } // end while
360 
361  return alreadyRead;
362 
363  MRPT_END
364 }
365 
366 /*---------------------------------------------------------------
367  writeAsync
368  ---------------------------------------------------------------*/
370  const void* Buffer, const size_t Count, const int timeout_ms)
371 {
372  MRPT_START
373 
374  if (m_hSock == INVALID_SOCKET) return 0; // The socket is not connected!
375 
376  size_t remainToWrite, alreadyWritten = 0;
377  int writtenNow;
378  bool timeoutExpired = false;
379 
380  struct timeval timeoutSelect = {0, 0};
381  struct timeval* ptrTimeout;
382  fd_set sockArr;
383 
384  // Init fd_set structure & add our socket to it:
385  FD_ZERO(&sockArr);
386  FD_SET(m_hSock, &sockArr);
387 
388  // The timeout:
389  if (timeout_ms < 0)
390  {
391  ptrTimeout = nullptr;
392  }
393  else
394  {
395  timeoutSelect.tv_sec = timeout_ms / 1000;
396  timeoutSelect.tv_usec = 1000 * (timeout_ms % 1000);
397  ptrTimeout = &timeoutSelect;
398  }
399 
400  // Loop until timeout expires or the socket is closed.
401  while (alreadyWritten < Count && !timeoutExpired)
402  {
403  // Wait for received data
404  int selRet = ::select(
405  m_hSock + 1, // __nfds
406  nullptr, // Wait for read
407  &sockArr, // Wait for write
408  nullptr, // Wait for except.
409  ptrTimeout); // Timeout
410 
411  if (selRet == INVALID_SOCKET)
413  "Error writing to socket: %s", getLastErrorStr().c_str());
414 
415  if (selRet == 0)
416  {
417  // Timeout:
418  timeoutExpired = true;
419  }
420  else
421  {
422  // We have room to write data!
423 
424  // Compute remaining part:
425  remainToWrite = Count - alreadyWritten;
426 
427  // Receive bytes:
428  writtenNow = ::send(
429  m_hSock, ((char*)Buffer) + alreadyWritten, (int)remainToWrite,
430  0);
431 
432  if (writtenNow != INVALID_SOCKET)
433  {
434  // Accumulate the received length:
435  alreadyWritten += writtenNow;
436  }
437  }
438 
439  } // end while
440 
441  return alreadyWritten;
442 
443  MRPT_END
444 }
445 
446 /*---------------------------------------------------------------
447  getReadPendingBytes
448  ---------------------------------------------------------------*/
450 {
451  if (m_hSock == INVALID_SOCKET) return 0; // The socket is not connected!
452  unsigned long ret = 0;
453  if (
454 #ifdef _WIN32
455  ioctlsocket(m_hSock, FIONREAD, &ret)
456 #else
457  ioctl(m_hSock, FIONREAD, &ret)
458 #endif
459  )
460  {
461  THROW_EXCEPTION("Error invoking ioctlsocket(FIONREAD)");
462  }
463  else
464  return ret;
465 }
466 
467 /*---------------------------------------------------------------
468  setTCPNoDelay
469  ---------------------------------------------------------------*/
471 {
472  int length = sizeof(newValue);
473 
474  return setsockopt(
475  m_hSock, IPPROTO_TCP, TCP_NODELAY, (char*)&newValue, length);
476 }
477 
478 /*---------------------------------------------------------------
479  getTCPNoDelay
480  ---------------------------------------------------------------*/
482 {
483  int value;
484 #ifdef _WIN32
485  int length = sizeof(value);
486 #else
487  unsigned int length = sizeof(value);
488 #endif
489  int res =
490  getsockopt(m_hSock, IPPROTO_TCP, TCP_NODELAY, (char*)&value, &length);
491 
492  if (res == -1)
493  return -1;
494  else
495  return value;
496 }
497 
498 /*---------------------------------------------------------------
499  setSOSendBufffer
500  ---------------------------------------------------------------*/
502 {
503  const unsigned int length = sizeof(newValue);
504 
505  return setsockopt(m_hSock, SOL_SOCKET, SO_SNDBUF, (char*)&newValue, length);
506 }
507 
508 /*---------------------------------------------------------------
509  getSOSendBufffer
510  ---------------------------------------------------------------*/
512 {
513  int value;
514 #ifdef _WIN32
515  int length = sizeof(value);
516 #else
517  unsigned int length = sizeof(value);
518 #endif
519  getsockopt(m_hSock, SOL_SOCKET, SO_SNDBUF, (char*)&value, &length);
520 
521  return value;
522 }
523 
525 {
527 }
528 
530  [[maybe_unused]] int64_t off, [[maybe_unused]] CStream::TSeekOrigin org)
531 {
532  MRPT_START
533  THROW_EXCEPTION("This method has no effect in this class!");
534  MRPT_END
535 }
536 
538 {
539  MRPT_START
540  THROW_EXCEPTION("This method has no effect in this class!");
541  MRPT_END
542 }
543 
545 {
546  MRPT_START
547  THROW_EXCEPTION("This method has no effect in this class!");
548  MRPT_END
549 }
static unsigned int DNS_LOOKUP_TIMEOUT_MS
See description of CClientTCPSocket.
int setSOSendBufffer(int newValue)
Set the size of the SO send buffer.
#define MRPT_START
Definition: exceptions.h:241
void connect(const std::string &remotePartAddress, unsigned short remotePartTCPPort, unsigned int timeout_ms=0)
Establishes a connection with a remote part.
size_t Write(const void *Buffer, size_t Count) override
Introduces a virtual method responsible for writing to the stream.
#define THROW_EXCEPTION(msg)
Definition: exceptions.h:67
std::string std::string format(std::string_view fmt, ARGS &&... args)
Definition: format.h:26
bool isConnected()
Returns true if this objects represents a successfully connected socket.
uint64_t Seek(int64_t off, CStream::TSeekOrigin org=sFromBeginning) override
This virtual method has no effect in this implementation over a TCP socket, and its use raises an exc...
std::string getLastSocketErrorStr()
Returns a description of the last Sockets error.
Definition: net_utils.cpp:480
STL namespace.
int getSOSendBufffer()
Return the current size of the SO send buffer.
size_t Read(void *Buffer, size_t Count) override
Introduces a virtual method responsible for reading from the stream (This method BLOCKS) This method ...
uint64_t getTotalBytesCount() const override
This virtual method has no effect in this implementation over a TCP socket, and its use raises an exc...
~CClientTCPSocket() override
Destructor.
bool DNS_resolve_async(const std::string &server_name, std::string &out_ip, const unsigned int timeout_ms=3000)
Resolve a server address by its name, returning its IP address as a string - This method has a timeou...
Definition: net_utils.cpp:404
CClientTCPSocket()
Default constructor.
int getTCPNoDelay()
Return the value of the TCPNoDelay option.
std::string getLastErrorStr()
Returns a description of the last Sockets error.
size_t writeAsync(const void *Buffer, const size_t Count, const int timeout_ms=-1)
A method for writing to the socket with optional timeouts.
This is the global namespace for all Mobile Robot Programming Toolkit (MRPT) libraries.
int setTCPNoDelay(int newValue)
Set the TCP no delay option of the protocol (Nagle algorithm).
uint64_t getPosition() const override
This virtual method has no effect in this implementation over a TCP socket, and its use raises an exc...
#define MRPT_END
Definition: exceptions.h:245
void close()
Closes the connection.
std::string exception_to_str(const std::exception &e)
Builds a nice textual representation of a nested exception, which if generated using MRPT macros (THR...
Definition: exceptions.cpp:59
size_t getReadPendingBytes()
Return the number of bytes already in the receive queue (they can be read without waiting) ...
Serial and networking devices and utilities.
size_t readAsync(void *Buffer, const size_t Count, const int timeoutStart_ms=-1, const int timeoutBetween_ms=-1)
A method for reading from the socket with an optional timeout.
#define THROW_EXCEPTION_FMT(_FORMAT_STRING,...)
Definition: exceptions.h:69
void sendString(const std::string &str)
Writes a string to the socket.



Page generated by Doxygen 1.8.14 for MRPT 2.0.4 Git: 5711e29ae Wed May 27 14:29:47 2020 +0200 at miƩ may 27 14:30:10 CEST 2020