MRPT  1.9.9
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-2019, 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 
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  ---------------------------------------------------------------*/
470 int CClientTCPSocket::setTCPNoDelay(const int& newValue)
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  ---------------------------------------------------------------*/
501 int CClientTCPSocket::setSOSendBufffer(const int& newValue)
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 
529 uint64_t CClientTCPSocket::Seek(int64_t off, CStream::TSeekOrigin org)
530 {
531  MRPT_START
532  MRPT_UNUSED_PARAM(off);
533  MRPT_UNUSED_PARAM(org);
534  THROW_EXCEPTION("This method has no effect in this class!");
535  MRPT_END
536 }
537 
539 {
540  MRPT_START
541  THROW_EXCEPTION("This method has no effect in this class!");
542  MRPT_END
543 }
544 
546 {
547  MRPT_START
548  THROW_EXCEPTION("This method has no effect in this class!");
549  MRPT_END
550 }
static unsigned int DNS_LOOKUP_TIMEOUT_MS
See description of CClientTCPSocket.
#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 setSOSendBufffer(const int &newValue)
Set the size of the SO send buffer.
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.
GLsizei const GLchar ** string
Definition: glext.h:4116
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.
GLdouble GLdouble GLdouble r
Definition: glext.h:3711
unsigned __int64 uint64_t
Definition: glext.h:3457
uint64_t getPosition() const override
This virtual method has no effect in this implementation over a TCP socket, and its use raises an exc...
int setTCPNoDelay(const int &newValue)
Set the TCP no delay option of the protocol (Nagle algorithm).
#define MRPT_END
Definition: exceptions.h:245
GLuint GLsizei GLsizei * length
Definition: glext.h:4079
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
GLsizei const GLfloat * value
Definition: glext.h:4134
size_t getReadPendingBytes()
Return the number of bytes already in the receive queue (they can be read without waiting) ...
GLuint res
Definition: glext.h:7385
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.
__int64 int64_t
Definition: glext.h:3456
#define THROW_EXCEPTION_FMT(_FORMAT_STRING,...)
Definition: exceptions.h:69
void sendString(const std::string &str)
Writes a string to the socket.
#define MRPT_UNUSED_PARAM(a)
Determines whether this is an X86 or AMD64 platform.
Definition: common.h:186



Page generated by Doxygen 1.8.14 for MRPT 1.9.9 Git: 9b18308f3 Mon Nov 18 23:39:25 2019 +0100 at lun nov 18 23:45:12 CET 2019