Main MRPT website > C++ reference for MRPT 1.9.9
CClientTCPSocket.cpp
Go to the documentation of this file.
1 /* +------------------------------------------------------------------------+
2  | Mobile Robot Programming Toolkit (MRPT) |
3  | http://www.mrpt.org/ |
4  | |
5  | Copyright (c) 2005-2017, Individual contributors, see AUTHORS file |
6  | See: http://www.mrpt.org/Authors - All rights reserved. |
7  | Released under BSD License. See details in http://www.mrpt.org/License |
8  +------------------------------------------------------------------------+ */
9 
10 #include "comms-precomp.h" // Precompiled headers
11 
13 #include <mrpt/utils/CMessage.h>
14 #include <mrpt/comms/net_utils.h>
15 #include <mrpt/system/os.h>
16 #include <cstring>
17 
18 #ifdef MRPT_OS_WINDOWS
19 // Windows
20 #define _WINSOCK_DEPRECATED_NO_WARNINGS
21 #include <winsock2.h>
22 #include <winerror.h>
23 #if defined(__BORLANDC__) || defined(_MSC_VER)
24 #pragma comment(lib, "WS2_32.LIB")
25 #endif
26 #else
27 // Linux, Apple
28 #define INVALID_SOCKET (-1)
29 #include <sys/socket.h>
30 #include <unistd.h>
31 #include <fcntl.h>
32 #include <errno.h>
33 #include <sys/types.h>
34 #include <sys/ioctl.h>
35 #include <netdb.h>
36 #include <arpa/inet.h>
37 #include <netinet/in.h>
38 #include <netinet/tcp.h>
39 #endif
40 
41 using namespace mrpt::utils;
42 using namespace mrpt::comms;
43 using namespace mrpt::system;
44 using namespace mrpt;
45 using namespace std;
46 
47 unsigned int CClientTCPSocket::DNS_LOOKUP_TIMEOUT_MS = 3000;
48 
49 CClientTCPSocket::CClientTCPSocket()
50 {
52 
53 #ifdef MRPT_OS_WINDOWS
54  // Init the WinSock Library:
55  // ----------------------------
56  WORD wVersionRequested;
57  WSADATA wsaData;
58 
59  wVersionRequested = MAKEWORD(2, 0);
60 
61  if (WSAStartup(wVersionRequested, &wsaData))
62  THROW_EXCEPTION("Error calling WSAStartup");
63 
64  m_hSock = INVALID_SOCKET;
65 #else
66  // Linux, Apple
67  m_hSock = -1;
68 #endif
69  MRPT_END
70 }
71 
72 CClientTCPSocket::~CClientTCPSocket()
73 {
74  // Close socket:
75  close();
76 #ifdef MRPT_OS_WINDOWS
77  WSACleanup();
78 #else
79 // Nothing else to do.
80 #endif
81 }
82 
83 void CClientTCPSocket::close()
84 {
86 
87 #ifdef MRPT_OS_WINDOWS
88  // Delete socket:
89  if (m_hSock != INVALID_SOCKET)
90  {
91  shutdown(m_hSock, 2); // SD_BOTH );
92  closesocket(m_hSock);
93  m_hSock = INVALID_SOCKET;
94  }
95 #else
96  // Delete socket:
97  if (m_hSock != -1)
98  {
99  shutdown(m_hSock, SHUT_RDWR);
100  ::close(m_hSock);
101  m_hSock = -1;
102  }
103 #endif
104  MRPT_END
105 }
106 
107 /*---------------------------------------------------------------
108  Read
109  ---------------------------------------------------------------*/
110 size_t CClientTCPSocket::Read(void* Buffer, size_t Count)
111 {
112  MRPT_START
113 
114  return readAsync(Buffer, Count);
115 
116  MRPT_END
117 }
118 
119 /*---------------------------------------------------------------
120  Write
121  ---------------------------------------------------------------*/
122 size_t CClientTCPSocket::Write(const void* Buffer, size_t Count)
123 {
124  MRPT_START
125 
126  return writeAsync(Buffer, Count);
127 
128  MRPT_END
129 }
130 
131 /*---------------------------------------------------------------
132  sendString
133  ---------------------------------------------------------------*/
134 void CClientTCPSocket::sendString(const std::string& str)
135 {
136  Write(str.c_str(), str.size());
137 }
138 
139 /*---------------------------------------------------------------
140  sendMessage
141  ---------------------------------------------------------------*/
142 bool CClientTCPSocket::sendMessage(const CMessage& outMsg, const int timeout_ms)
143 {
144  uint32_t contentLen, toWrite, written;
145 
146  // --------------------------------
147  // (1) Send a "magic word":
148  // --------------------------------
149  const char* magic = "MRPTMessage";
150  toWrite = strlen(magic);
151 
152  written = writeAsync(magic, toWrite, timeout_ms);
153  if (written != toWrite) return false; // Error!
154 
155  // --------------------------------
156  // (2) Send the message type:
157  // --------------------------------
158  toWrite = sizeof(outMsg.type);
159 
160  written = writeAsync(&outMsg.type, toWrite, timeout_ms);
161  if (written != toWrite) return false; // Error!
162 
163  // ---------------------------------------
164  // (3) Send the message's content length:
165  // ---------------------------------------
166  contentLen = outMsg.content.size();
167  toWrite = sizeof(contentLen);
168 
169  written = writeAsync(&contentLen, toWrite, timeout_ms);
170  if (written != toWrite) return false; // Error!
171 
172  // ---------------------------------------
173  // (4) Send the message's contents:
174  // ---------------------------------------
175  toWrite = contentLen;
176 
177  written = writeAsync(&outMsg.content[0], toWrite, timeout_ms);
178  if (written != toWrite) return false; // Error!
179 
180  return true;
181 }
182 
183 /*---------------------------------------------------------------
184  receiveMessage
185  ---------------------------------------------------------------*/
186 bool CClientTCPSocket::receiveMessage(
187  CMessage& inMsg, unsigned int timeoutStart_ms,
188  unsigned int timeoutBetween_ms)
189 {
190  uint32_t contentLen, toRead, actRead;
191 
192  // --------------------------------
193  // (1) Read the "magic word":
194  // --------------------------------
195  char magic[20]; // ;
196  toRead = 11;
197 
198  actRead = readAsync(magic, toRead, timeoutStart_ms, timeoutBetween_ms);
199  if (actRead != toRead) return false; // Error!
200  magic[actRead] = 0; // Null-term string
201 
202  // Check magic:
203  if (0 != os::_strcmpi("MRPTMessage", magic)) return false;
204 
205  // --------------------------------
206  // (2) Read the message type:
207  // --------------------------------
208  toRead = sizeof(inMsg.type);
209 
210  actRead =
211  readAsync(&inMsg.type, toRead, timeoutBetween_ms, timeoutBetween_ms);
212  if (actRead != toRead) return false; // Error!
213 
214  // ---------------------------------------
215  // (3) Read the message's content length:
216  // ---------------------------------------
217  toRead = sizeof(contentLen);
218 
219  actRead =
220  readAsync(&contentLen, toRead, timeoutBetween_ms, timeoutBetween_ms);
221  if (actRead != toRead) return false; // Error!
222 
223  // Reserve memory:
224  inMsg.content.resize(contentLen);
225 
226  // ---------------------------------------
227  // (4) Read the message's contents:
228  // ---------------------------------------
229  toRead = contentLen;
230 
231  actRead = readAsync(
232  &inMsg.content[0], toRead, timeoutBetween_ms, timeoutBetween_ms);
233  if (actRead != toRead) return false; // Error!
234 
235  return true;
236 }
237 
238 /*---------------------------------------------------------------
239  connect
240  ---------------------------------------------------------------*/
241 void CClientTCPSocket::connect(
242  const std::string& remotePartAddress, unsigned short remotePartTCPPort,
243  unsigned int timeout_ms)
244 {
245  MRPT_START
246 
247  // Close existing socket, if any.
248  if (m_hSock != INVALID_SOCKET) close();
249 
250  // Create the socket:
251  if (INVALID_SOCKET == (m_hSock = socket(AF_INET, SOCK_STREAM, 0)))
253  format(
254  "Error creating new client socket:\n%s",
255  getLastErrorStr().c_str()));
256 
257  struct sockaddr_in otherAddress;
258 
259  otherAddress.sin_family = AF_INET;
260  otherAddress.sin_port = htons(remotePartTCPPort);
261 
262  // Resolve the IP address of the given host name
263  std::string solved_IP;
265  remotePartAddress, solved_IP, DNS_LOOKUP_TIMEOUT_MS))
267  "DNS lookup failed for '%s'", remotePartAddress.c_str());
268 
269  // Fill out from IP address text:
270  otherAddress.sin_addr.s_addr = inet_addr(solved_IP.c_str());
271  if (INADDR_NONE == otherAddress.sin_addr.s_addr)
273  "Invalid IP address provided: %s", solved_IP.c_str());
274 
275 // Set to NON-BLOCKING:
276 #ifdef MRPT_OS_WINDOWS
277  unsigned long non_block_mode = 1;
278  if (ioctlsocket(m_hSock, FIONBIO, &non_block_mode))
279  THROW_EXCEPTION("Error entering non-blocking mode with ioctlsocket().");
280 #else
281  int oldflags = fcntl(m_hSock, F_GETFL, 0);
282  if (oldflags == -1) THROW_EXCEPTION("Error retrieving fcntl() of socket.");
283  oldflags |= O_NONBLOCK; // Set NON-BLOCKING
284  if (-1 == fcntl(m_hSock, F_SETFL, oldflags))
285  THROW_EXCEPTION("Error entering non-blocking mode with fcntl().");
286 #endif
287 
288  // Try to connect:
289  int r = ::connect(
290  m_hSock, (struct sockaddr*)&otherAddress, sizeof(otherAddress));
291 #ifdef MRPT_OS_WINDOWS
292  int er = WSAGetLastError();
293  if (r < 0 && er != WSAEINPROGRESS && er != WSAEWOULDBLOCK)
294 #else
295  int er = errno;
296  if (r < 0 && er != EINPROGRESS)
297 #endif
299  format(
300  "Error connecting to %s:%hu. Error: %s [%d]",
301  remotePartAddress.c_str(), remotePartTCPPort, strerror(er),
302  er));
303 
304  // Wait for connect:
305  fd_set socket_set;
306  timeval timer;
307 
308  FD_ZERO(&socket_set);
309  FD_SET(m_hSock, &socket_set);
310 
311  timer.tv_sec = timeout_ms / 1000;
312  timer.tv_usec = 1000 * (timeout_ms % 1000);
313 
314  int sel_ret = select(
315  m_hSock + 1,
316  nullptr, // For read
317  &socket_set, // For write or *connect done*
318  &socket_set, // For errors
319  timeout_ms == 0 ? nullptr : &timer);
320 
321  if (sel_ret == 0)
323  format(
324  "Timeout connecting to '%s:%hu':\n%s",
325  remotePartAddress.c_str(), remotePartTCPPort,
326  getLastErrorStr().c_str()));
327  if (sel_ret == -1)
329  format(
330  "Error connecting to '%s:%hu':\n%s", remotePartAddress.c_str(),
331  remotePartTCPPort, getLastErrorStr().c_str()));
332 
333  // Now, make sure it was not an error!
334  int valopt;
335 #ifdef MRPT_OS_WINDOWS
336  int lon = sizeof(int);
337  getsockopt(m_hSock, SOL_SOCKET, SO_ERROR, (char*)(&valopt), &lon);
338 #else
339  socklen_t lon = sizeof(int);
340  getsockopt(m_hSock, SOL_SOCKET, SO_ERROR, (void*)(&valopt), &lon);
341 #endif
342 
343 #ifdef MRPT_OS_WINDOWS
344  if (valopt)
346  format(
347  "Error connecting to %s:%hu. Error: %i.",
348  remotePartAddress.c_str(), remotePartTCPPort, valopt));
349 #else
350  if (valopt)
352  format(
353  "Error connecting to %s:%hu. Error: %s.",
354  remotePartAddress.c_str(), remotePartTCPPort,
355  strerror(valopt)));
356 #endif
357 // Connected!
358 
359 // If connected OK, remove the non-blocking flag:
360 #ifdef MRPT_OS_WINDOWS
361  non_block_mode = 0;
362  if (ioctlsocket(m_hSock, FIONBIO, &non_block_mode))
363  THROW_EXCEPTION("Error entering blocking mode with ioctlsocket().");
364 #else
365  oldflags &= ~O_NONBLOCK; // Set BLOCKING
366  if (-1 == fcntl(m_hSock, F_SETFL, oldflags))
367  THROW_EXCEPTION("Error entering blocking mode with fcntl().");
368 #endif
369 
370  // Save the IP of the other part.
371  m_remotePartIP = remotePartAddress;
372 
373  MRPT_END
374 }
375 
376 /*---------------------------------------------------------------
377  isConnected
378  ---------------------------------------------------------------*/
379 bool CClientTCPSocket::isConnected() { return (m_hSock != INVALID_SOCKET); }
380 /*---------------------------------------------------------------
381  readAsync
382  ---------------------------------------------------------------*/
383 size_t CClientTCPSocket::readAsync(
384  void* Buffer, const size_t Count, const int timeoutStart_ms,
385  const int timeoutBetween_ms)
386 {
387  MRPT_START
388 
389  if (m_hSock == INVALID_SOCKET) return 0; // The socket is not connected!
390 
391  size_t remainToRead, alreadyRead = 0;
392  int readNow;
393  bool timeoutExpired = false;
394 
395  struct timeval timeoutSelect;
396  struct timeval* ptrTimeout;
397  fd_set sockArr;
398 
399  // Init fd_set structure & add our socket to it:
400  FD_ZERO(&sockArr);
401  FD_SET(m_hSock, &sockArr);
402 
403  // Loop until timeout expires or the socket is closed.
404  while (alreadyRead < Count && !timeoutExpired)
405  {
406  // Use the "first" or "between" timeouts:
407  int curTimeout = alreadyRead == 0 ? timeoutStart_ms : timeoutBetween_ms;
408 
409  if (curTimeout < 0)
410  ptrTimeout = nullptr;
411  else
412  {
413  timeoutSelect.tv_sec = curTimeout / 1000;
414  timeoutSelect.tv_usec = 1000 * (curTimeout % 1000);
415  ptrTimeout = &timeoutSelect;
416  }
417 
418  // Wait for received data
419  int selRet = ::select(
420  m_hSock + 1, // __nfds
421  &sockArr, // Wait for read
422  nullptr, // Wait for write
423  nullptr, // Wait for except.
424  ptrTimeout); // Timeout
425 
426  if (selRet == INVALID_SOCKET)
428  "Error reading from socket: %s", getLastErrorStr().c_str());
429 
430  if (selRet == 0)
431  {
432  // Timeout:
433  timeoutExpired = true;
434  }
435  else
436  {
437  // Compute remaining part:
438  remainToRead = Count - alreadyRead;
439 
440  // Receive bytes:
441  readNow = ::recv(
442  m_hSock, ((char*)Buffer) + alreadyRead, (int)remainToRead, 0);
443 
444  if (readNow != INVALID_SOCKET)
445  {
446  // Accumulate the received length:
447  alreadyRead += readNow;
448  }
449  else
450  {
451  // Error: Socket closed?
452  this->close();
453  return alreadyRead;
454  }
455 
456  if (readNow == 0 && remainToRead != 0)
457  {
458  // We had an event of data available, so if we have now a zero,
459  // the socket has been gracefully closed:
460  timeoutExpired = true;
461  close();
462  }
463  }
464  } // end while
465 
466  return alreadyRead;
467 
468  MRPT_END
469 }
470 
471 /*---------------------------------------------------------------
472  writeAsync
473  ---------------------------------------------------------------*/
474 size_t CClientTCPSocket::writeAsync(
475  const void* Buffer, const size_t Count, const int timeout_ms)
476 {
477  MRPT_START
478 
479  if (m_hSock == INVALID_SOCKET) return 0; // The socket is not connected!
480 
481  size_t remainToWrite, alreadyWritten = 0;
482  int writtenNow;
483  bool timeoutExpired = false;
484 
485  struct timeval timeoutSelect;
486  struct timeval* ptrTimeout;
487  fd_set sockArr;
488 
489  // Init fd_set structure & add our socket to it:
490  FD_ZERO(&sockArr);
491  FD_SET(m_hSock, &sockArr);
492 
493  // The timeout:
494  if (timeout_ms < 0)
495  {
496  ptrTimeout = nullptr;
497  }
498  else
499  {
500  timeoutSelect.tv_sec = timeout_ms / 1000;
501  timeoutSelect.tv_usec = 1000 * (timeout_ms % 1000);
502  ptrTimeout = &timeoutSelect;
503  }
504 
505  // Loop until timeout expires or the socket is closed.
506  while (alreadyWritten < Count && !timeoutExpired)
507  {
508  // Wait for received data
509  int selRet = ::select(
510  m_hSock + 1, // __nfds
511  nullptr, // Wait for read
512  &sockArr, // Wait for write
513  nullptr, // Wait for except.
514  ptrTimeout); // Timeout
515 
516  if (selRet == INVALID_SOCKET)
518  "Error writing to socket: %s", getLastErrorStr().c_str());
519 
520  if (selRet == 0)
521  {
522  // Timeout:
523  timeoutExpired = true;
524  }
525  else
526  {
527  // We have room to write data!
528 
529  // Compute remaining part:
530  remainToWrite = Count - alreadyWritten;
531 
532  // Receive bytes:
533  writtenNow = ::send(
534  m_hSock, ((char*)Buffer) + alreadyWritten, (int)remainToWrite,
535  0);
536 
537  if (writtenNow != INVALID_SOCKET)
538  {
539  // Accumulate the received length:
540  alreadyWritten += writtenNow;
541  }
542  }
543 
544  } // end while
545 
546  return alreadyWritten;
547 
548  MRPT_END
549 }
550 
551 /*---------------------------------------------------------------
552  getReadPendingBytes
553  ---------------------------------------------------------------*/
554 size_t CClientTCPSocket::getReadPendingBytes()
555 {
556  if (m_hSock == INVALID_SOCKET) return 0; // The socket is not connected!
557  unsigned long ret = 0;
558  if (
559 #ifdef MRPT_OS_WINDOWS
560  ioctlsocket(m_hSock, FIONREAD, &ret)
561 #else
562  ioctl(m_hSock, FIONREAD, &ret)
563 #endif
564  )
565  {
566  THROW_EXCEPTION("Error invoking ioctlsocket(FIONREAD)")
567  }
568  else
569  return ret;
570 }
571 
572 /*---------------------------------------------------------------
573  setTCPNoDelay
574  ---------------------------------------------------------------*/
575 int CClientTCPSocket::setTCPNoDelay(const int& newValue)
576 {
577  int length = sizeof(newValue);
578 
579  return setsockopt(
580  m_hSock, IPPROTO_TCP, TCP_NODELAY, (char*)&newValue, length);
581 }
582 
583 /*---------------------------------------------------------------
584  getTCPNoDelay
585  ---------------------------------------------------------------*/
586 int CClientTCPSocket::getTCPNoDelay()
587 {
588  int value;
589 #ifdef MRPT_OS_WINDOWS
590  int length = sizeof(value);
591 #else
592  unsigned int length = sizeof(value);
593 #endif
594  int res =
595  getsockopt(m_hSock, IPPROTO_TCP, TCP_NODELAY, (char*)&value, &length);
596 
597  if (res == -1)
598  return -1;
599  else
600  return value;
601 }
602 
603 /*---------------------------------------------------------------
604  setSOSendBufffer
605  ---------------------------------------------------------------*/
606 int CClientTCPSocket::setSOSendBufffer(const int& newValue)
607 {
608  const unsigned int length = sizeof(newValue);
609 
610  return setsockopt(m_hSock, SOL_SOCKET, SO_SNDBUF, (char*)&newValue, length);
611 }
612 
613 /*---------------------------------------------------------------
614  getSOSendBufffer
615  ---------------------------------------------------------------*/
616 int CClientTCPSocket::getSOSendBufffer()
617 {
618  int value;
619 #ifdef MRPT_OS_WINDOWS
620  int length = sizeof(value);
621 #else
622  unsigned int length = sizeof(value);
623 #endif
624  getsockopt(m_hSock, SOL_SOCKET, SO_SNDBUF, (char*)&value, &length);
625 
626  return value;
627 }
628 
629 std::string CClientTCPSocket::getLastErrorStr()
630 {
632 }
#define INVALID_SOCKET
Classes for serialization, sockets, ini-file manipulation, streams, list of properties-values, timewatch, extensions to STL.
This namespace provides a OS-independent interface to many useful functions: filenames manipulation...
Definition: math_frwds.h:30
#define THROW_EXCEPTION(msg)
#define THROW_EXCEPTION_FMT(_FORMAT_STRING,...)
std::string getLastSocketErrorStr()
Returns a description of the last Sockets error.
Definition: net_utils.cpp:475
STL namespace.
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:400
#define MRPT_END
std::string format(const char *fmt,...) MRPT_printf_format_check(1
A std::string version of C sprintf.
Definition: format.cpp:19
GLsizei const GLchar ** string
Definition: glext.h:4101
#define MRPT_START
This is the global namespace for all Mobile Robot Programming Toolkit (MRPT) libraries.
GLdouble GLdouble GLdouble r
Definition: glext.h:3705
GLuint GLsizei GLsizei * length
Definition: glext.h:4064
std::vector< uint8_t > content
The contents of the message (memory is automatically handled by the std::vector object) ...
Definition: CMessage.h:39
GLsizei const GLfloat * value
Definition: glext.h:4117
GLuint res
Definition: glext.h:7268
Serial and networking devices and utilities.
unsigned __int32 uint32_t
Definition: rptypes.h:47
A class that contain generic messages, that can be sent and received from a "CClientTCPSocket" object...
Definition: CMessage.h:31
uint32_t type
An identifier of the message type (only the least-sig byte is typically sent)
Definition: CMessage.h:36
int _strcmpi(const char *str1, const char *str2) noexcept
An OS-independent version of strcmpi.
Definition: os.cpp:319



Page generated by Doxygen 1.8.14 for MRPT 1.9.9 Git: ae4571287 Thu Nov 23 00:06:53 2017 +0100 at dom oct 27 23:51:55 CET 2019