Main MRPT website > C++ reference for MRPT 1.5.7
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 "base-precomp.h" // Precompiled headers
11 
13 #include <mrpt/utils/CMessage.h>
14 #include <mrpt/utils/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::system;
43 using namespace mrpt;
44 using namespace std;
45 
46 
47 unsigned int CClientTCPSocket::DNS_LOOKUP_TIMEOUT_MS = 3000;
48 
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 
73 {
74  // Close socket:
75  close();
76 #ifdef MRPT_OS_WINDOWS
77  WSACleanup();
78 #else
79  // Nothing else to do.
80 #endif
81 }
82 
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 
109 /*---------------------------------------------------------------
110  Read
111  ---------------------------------------------------------------*/
112 size_t CClientTCPSocket::Read(void *Buffer, size_t Count)
113 {
114  MRPT_START
115 
116  return readAsync(Buffer,Count);
117 
118  MRPT_END
119 }
120 
121 /*---------------------------------------------------------------
122  Write
123  ---------------------------------------------------------------*/
124 size_t CClientTCPSocket::Write(const void *Buffer, size_t Count)
125 {
126  MRPT_START
127 
128  return writeAsync(Buffer,Count);
129 
130  MRPT_END
131 }
132 
133 /*---------------------------------------------------------------
134  sendString
135  ---------------------------------------------------------------*/
137 {
138  Write( str.c_str(), str.size() );
139 }
140 
141 /*---------------------------------------------------------------
142  sendMessage
143  ---------------------------------------------------------------*/
145  const CMessage& outMsg,
146  const int timeout_ms
147  )
148 {
149  uint32_t contentLen, toWrite,written;
150 
151  // --------------------------------
152  // (1) Send a "magic word":
153  // --------------------------------
154  const char *magic= "MRPTMessage";
155  toWrite = strlen(magic);
156 
157  written = writeAsync( magic, toWrite, timeout_ms );
158  if (written!=toWrite) return false; // Error!
159 
160  // --------------------------------
161  // (2) Send the message type:
162  // --------------------------------
163  toWrite = sizeof( outMsg.type );
164 
165  written = writeAsync( &outMsg.type, toWrite, timeout_ms );
166  if (written!=toWrite) return false; // Error!
167 
168  // ---------------------------------------
169  // (3) Send the message's content length:
170  // ---------------------------------------
171  contentLen = outMsg.content.size();
172  toWrite = sizeof( contentLen );
173 
174  written = writeAsync( &contentLen, toWrite, timeout_ms );
175  if (written!=toWrite) return false; // Error!
176 
177  // ---------------------------------------
178  // (4) Send the message's contents:
179  // ---------------------------------------
180  toWrite = contentLen;
181 
182  written = writeAsync( &outMsg.content[0], toWrite, timeout_ms );
183  if (written!=toWrite) return false; // Error!
184 
185  return true;
186 }
187 
188 /*---------------------------------------------------------------
189  receiveMessage
190  ---------------------------------------------------------------*/
192  CMessage& inMsg,
193  unsigned int timeoutStart_ms,
194  unsigned int timeoutBetween_ms
195  )
196 {
197  uint32_t contentLen, toRead,actRead;
198 
199  // --------------------------------
200  // (1) Read the "magic word":
201  // --------------------------------
202  char magic[20]; // ;
203  toRead = 11;
204 
205  actRead = readAsync( magic,toRead,timeoutStart_ms,timeoutBetween_ms );
206  if (actRead!=toRead) return false; // Error!
207  magic[actRead] = 0; // Null-term string
208 
209  // Check magic:
210  if ( 0 != os::_strcmpi( "MRPTMessage",magic ) )
211  return false;
212 
213  // --------------------------------
214  // (2) Read the message type:
215  // --------------------------------
216  toRead = sizeof( inMsg.type );
217 
218  actRead = readAsync( &inMsg.type,toRead,timeoutBetween_ms,timeoutBetween_ms );
219  if (actRead!=toRead) return false; // Error!
220 
221  // ---------------------------------------
222  // (3) Read the message's content length:
223  // ---------------------------------------
224  toRead = sizeof( contentLen );
225 
226  actRead = readAsync( &contentLen,toRead,timeoutBetween_ms,timeoutBetween_ms );
227  if (actRead!=toRead) return false; // Error!
228 
229  // Reserve memory:
230  inMsg.content.resize( contentLen );
231 
232  // ---------------------------------------
233  // (4) Read the message's contents:
234  // ---------------------------------------
235  toRead = contentLen;
236 
237  actRead = readAsync( &inMsg.content[0],toRead,timeoutBetween_ms,timeoutBetween_ms);
238  if (actRead!=toRead) return false; // Error!
239 
240  return true;
241 }
242 
243 
244 
245 /*---------------------------------------------------------------
246  connect
247  ---------------------------------------------------------------*/
249  const std::string &remotePartAddress,
250  unsigned short remotePartTCPPort,
251  unsigned int timeout_ms )
252 {
253  MRPT_START
254 
255  // Close existing socket, if any.
256  if (m_hSock != INVALID_SOCKET)
257  close();
258 
259  // Create the socket:
260  if ( INVALID_SOCKET == (m_hSock = socket(AF_INET, SOCK_STREAM, 0)) )
261  THROW_EXCEPTION( format("Error creating new client socket:\n%s",getLastErrorStr().c_str() ));
262 
263  struct sockaddr_in otherAddress;
264 
265  otherAddress.sin_family = AF_INET;
266  otherAddress.sin_port = htons(remotePartTCPPort);
267 
268  // Resolve the IP address of the given host name
269  std::string solved_IP;
270  if (!net::DNS_resolve_async(remotePartAddress,solved_IP,DNS_LOOKUP_TIMEOUT_MS))
271  THROW_EXCEPTION_FMT("DNS lookup failed for '%s'",remotePartAddress.c_str());
272 
273 
274  // Fill out from IP address text:
275  otherAddress.sin_addr.s_addr = inet_addr(solved_IP.c_str());
276  if (INADDR_NONE==otherAddress.sin_addr.s_addr)
277  THROW_EXCEPTION_FMT("Invalid IP address provided: %s",solved_IP.c_str());
278 
279  // Set to NON-BLOCKING:
280 #ifdef MRPT_OS_WINDOWS
281  unsigned long non_block_mode = 1;
282  if (ioctlsocket(m_hSock, FIONBIO, &non_block_mode) ) THROW_EXCEPTION( "Error entering non-blocking mode with ioctlsocket()." );
283 #else
284  int oldflags=fcntl(m_hSock, F_GETFL, 0);
285  if (oldflags == -1) THROW_EXCEPTION( "Error retrieving fcntl() of socket." );
286  oldflags |= O_NONBLOCK; // Set NON-BLOCKING
287  if (-1==fcntl(m_hSock, F_SETFL, oldflags)) THROW_EXCEPTION( "Error entering non-blocking mode with fcntl()." );
288 #endif
289 
290  // Try to connect:
291  int r = ::connect( m_hSock , (struct sockaddr *)&otherAddress,sizeof(otherAddress));
292 #ifdef MRPT_OS_WINDOWS
293  int er = WSAGetLastError();
294  if (r < 0 && er != WSAEINPROGRESS && er != WSAEWOULDBLOCK)
295 #else
296  int er = errno;
297  if (r < 0 && er != EINPROGRESS)
298 #endif
299  THROW_EXCEPTION( format("Error connecting to %s:%hu. Error: %s [%d]", remotePartAddress.c_str(), remotePartTCPPort, strerror(er),er));
300 
301  // Wait for connect:
302  fd_set socket_set;
303  timeval timer;
304 
305  FD_ZERO(&socket_set);
306  FD_SET(m_hSock,&socket_set);
307 
308  timer.tv_sec = timeout_ms/1000;
309  timer.tv_usec = 1000*(timeout_ms%1000);
310 
311  int sel_ret = select(
312  m_hSock+1,
313  NULL, // For read
314  &socket_set, // For write or *connect done*
315  &socket_set, // For errors
316  timeout_ms==0 ? NULL : &timer
317  );
318 
319  if (sel_ret == 0) THROW_EXCEPTION( format("Timeout connecting to '%s:%hu':\n%s",remotePartAddress.c_str(),remotePartTCPPort, getLastErrorStr().c_str() ));
320  if (sel_ret ==-1) THROW_EXCEPTION( format("Error connecting to '%s:%hu':\n%s",remotePartAddress.c_str(),remotePartTCPPort, getLastErrorStr().c_str() ));
321 
322  // Now, make sure it was not an error!
323  int valopt;
324  #ifdef MRPT_OS_WINDOWS
325  int lon = sizeof(int);
326  getsockopt(m_hSock, SOL_SOCKET, SO_ERROR, (char*)(&valopt), &lon);
327  #else
328  socklen_t lon = sizeof(int);
329  getsockopt(m_hSock, SOL_SOCKET, SO_ERROR, (void*)(&valopt), &lon);
330  #endif
331 
332  #ifdef MRPT_OS_WINDOWS
333  if (valopt) THROW_EXCEPTION( format("Error connecting to %s:%hu. Error: %i.", remotePartAddress.c_str(), remotePartTCPPort, valopt));
334  #else
335  if (valopt) THROW_EXCEPTION( format("Error connecting to %s:%hu. Error: %s.", remotePartAddress.c_str(), remotePartTCPPort, strerror(valopt)));
336 #endif
337  // Connected!
338 
339  // If connected OK, remove the non-blocking flag:
340 #ifdef MRPT_OS_WINDOWS
341  non_block_mode = 0;
342  if (ioctlsocket(m_hSock, FIONBIO, &non_block_mode) ) THROW_EXCEPTION( "Error entering blocking mode with ioctlsocket()." );
343 #else
344  oldflags &= ~O_NONBLOCK; // Set BLOCKING
345  if (-1==fcntl(m_hSock, F_SETFL, oldflags)) THROW_EXCEPTION( "Error entering blocking mode with fcntl()." );
346 #endif
347 
348  // Save the IP of the other part.
349  m_remotePartIP = remotePartAddress;
350 
351  MRPT_END
352 }
353 
354 /*---------------------------------------------------------------
355  isConnected
356  ---------------------------------------------------------------*/
358 {
359  return (m_hSock != INVALID_SOCKET);
360 }
361 
362 
363 
364 /*---------------------------------------------------------------
365  readAsync
366  ---------------------------------------------------------------*/
368  void *Buffer,
369  const size_t Count,
370  const int timeoutStart_ms,
371  const int timeoutBetween_ms )
372 {
373  MRPT_START
374 
375  if (m_hSock == INVALID_SOCKET) return 0; // The socket is not connected!
376 
377  size_t remainToRead, alreadyRead = 0;
378  int readNow;
379  bool timeoutExpired = false;
380 
381  struct timeval timeoutSelect;
382  struct timeval *ptrTimeout;
383  fd_set sockArr;
384 
385  // Init fd_set structure & add our socket to it:
386  FD_ZERO(&sockArr);
387  FD_SET(m_hSock, &sockArr);
388 
389  // Loop until timeout expires or the socket is closed.
390  while ( alreadyRead<Count && !timeoutExpired )
391  {
392  // Use the "first" or "between" timeouts:
393  int curTimeout = alreadyRead==0 ? timeoutStart_ms : timeoutBetween_ms;
394 
395  if (curTimeout<0)
396  ptrTimeout = NULL;
397  else
398  {
399  timeoutSelect.tv_sec = curTimeout / 1000;
400  timeoutSelect.tv_usec = 1000 * (curTimeout % 1000);
401  ptrTimeout = &timeoutSelect;
402  }
403 
404  // Wait for received data
405  int selRet = ::select(
406  m_hSock+1, // __nfds
407  &sockArr, // Wait for read
408  NULL, // Wait for write
409  NULL, // Wait for except.
410  ptrTimeout); // Timeout
411 
412  if( selRet==INVALID_SOCKET )
413  THROW_EXCEPTION_FMT( "Error reading from socket: %s", getLastErrorStr().c_str() );
414 
415  if (selRet==0)
416  {
417  // Timeout:
418  timeoutExpired = true;
419  }
420  else
421  {
422  // Compute remaining part:
423  remainToRead = Count - alreadyRead;
424 
425  // Receive bytes:
426  readNow = ::recv( m_hSock, ((char*)Buffer) + alreadyRead, (int)remainToRead, 0);
427 
428  if (readNow != INVALID_SOCKET)
429  {
430  // Accumulate the received length:
431  alreadyRead += readNow;
432  }
433  else
434  {
435  // Error: Socket closed?
436  this->close();
437  return alreadyRead;
438  }
439 
440  if (readNow==0 && remainToRead!=0)
441  {
442  // We had an event of data available, so if we have now a zero,
443  // the socket has been gracefully closed:
444  timeoutExpired = true;
445  close();
446  }
447  }
448  } // end while
449 
450  return alreadyRead;
451 
452  MRPT_END
453 }
454 
455 /*---------------------------------------------------------------
456  writeAsync
457  ---------------------------------------------------------------*/
459  const void *Buffer,
460  const size_t Count,
461  const int timeout_ms )
462 {
463  MRPT_START
464 
465  if (m_hSock == INVALID_SOCKET) return 0; // The socket is not connected!
466 
467  size_t remainToWrite, alreadyWritten = 0;
468  int writtenNow;
469  bool timeoutExpired = false;
470 
471  struct timeval timeoutSelect;
472  struct timeval *ptrTimeout;
473  fd_set sockArr;
474 
475  // Init fd_set structure & add our socket to it:
476  FD_ZERO(&sockArr);
477  FD_SET(m_hSock, &sockArr);
478 
479  // The timeout:
480  if (timeout_ms<0)
481  {
482  ptrTimeout = NULL;
483  }
484  else
485  {
486  timeoutSelect.tv_sec = timeout_ms / 1000;
487  timeoutSelect.tv_usec = 1000 * (timeout_ms % 1000);
488  ptrTimeout = &timeoutSelect;
489  }
490 
491  // Loop until timeout expires or the socket is closed.
492  while ( alreadyWritten<Count && !timeoutExpired )
493  {
494  // Wait for received data
495  int selRet = ::select(
496  m_hSock+1, // __nfds
497  NULL, // Wait for read
498  &sockArr, // Wait for write
499  NULL, // Wait for except.
500  ptrTimeout); // Timeout
501 
502  if( selRet==INVALID_SOCKET )
503  THROW_EXCEPTION_FMT( "Error writing to socket: %s", getLastErrorStr().c_str() );
504 
505  if (selRet==0)
506  {
507  // Timeout:
508  timeoutExpired = true;
509  }
510  else
511  {
512  // We have room to write data!
513 
514  // Compute remaining part:
515  remainToWrite = Count - alreadyWritten;
516 
517  // Receive bytes:
518  writtenNow = ::send( m_hSock, ((char*)Buffer) + alreadyWritten, (int)remainToWrite, 0);
519 
520  if (writtenNow != INVALID_SOCKET)
521  {
522  // Accumulate the received length:
523  alreadyWritten += writtenNow;
524  }
525  }
526 
527  } // end while
528 
529  return alreadyWritten;
530 
531  MRPT_END
532 }
533 
534 
535 
536 /*---------------------------------------------------------------
537  getReadPendingBytes
538  ---------------------------------------------------------------*/
540 {
541  if (m_hSock == INVALID_SOCKET) return 0; // The socket is not connected!
542  unsigned long ret=0;
543  if (
544 #ifdef MRPT_OS_WINDOWS
545  ioctlsocket(m_hSock,FIONREAD,&ret)
546 #else
547  ioctl(m_hSock, FIONREAD, &ret)
548 #endif
549  )
550  {
551  THROW_EXCEPTION( "Error invoking ioctlsocket(FIONREAD)" )
552  }
553  else return ret;
554 }
555 
556 
557 /*---------------------------------------------------------------
558  setTCPNoDelay
559  ---------------------------------------------------------------*/
560 int CClientTCPSocket::setTCPNoDelay( const int &newValue )
561 {
562  int length = sizeof( newValue );
563 
564  return setsockopt(m_hSock,IPPROTO_TCP,TCP_NODELAY,(char*)&newValue,length);
565 }
566 
567 
568 /*---------------------------------------------------------------
569  getTCPNoDelay
570  ---------------------------------------------------------------*/
572 {
573  int value;
574 #ifdef MRPT_OS_WINDOWS
575  int length = sizeof ( value );
576 #else
577  unsigned int length = sizeof ( value );
578 #endif
579  int res = getsockopt(m_hSock, IPPROTO_TCP, TCP_NODELAY, (char*)&value, &length );
580 
581  if (res==-1)
582  return -1;
583  else return value;
584 }
585 
586 
587 /*---------------------------------------------------------------
588  setSOSendBufffer
589  ---------------------------------------------------------------*/
590 int CClientTCPSocket::setSOSendBufffer( const int &newValue )
591 {
592  const unsigned int length = sizeof( newValue );
593 
594  return setsockopt(m_hSock, SOL_SOCKET, SO_SNDBUF, (char*)&newValue, length );
595 }
596 
597 
598 /*---------------------------------------------------------------
599  getSOSendBufffer
600  ---------------------------------------------------------------*/
602 {
603  int value;
604 #ifdef MRPT_OS_WINDOWS
605  int length = sizeof ( value );
606 #else
607  unsigned int length = sizeof ( value );
608 #endif
609  getsockopt(m_hSock, SOL_SOCKET, SO_SNDBUF, (char*)&value, &length );
610 
611  return value;
612 }
#define INVALID_SOCKET
Classes for serialization, sockets, ini-file manipulation, streams, list of properties-values, timewatch, extensions to STL.
Definition: zip.h:16
This namespace provides a OS-independent interface to many useful functions: filenames manipulation...
Definition: math_frwds.h:29
#define THROW_EXCEPTION(msg)
#define THROW_EXCEPTION_FMT(_FORMAT_STRING,...)
STL namespace.
void sendString(const std::string &str)
Writes a string to the socket.
size_t Read(void *Buffer, size_t Count) MRPT_OVERRIDE
Introduces a virtual method responsible for reading from the stream (This method BLOCKS) This method ...
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.
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.
void close()
Closes the connection.
bool sendMessage(const CMessage &outMsg, const int timeout_ms=-1)
Send a message through the TCP stream.
#define MRPT_END
bool isConnected()
Returns true if this objects represents a successfully connected socket.
std::string BASE_IMPEXP format(const char *fmt,...) MRPT_printf_format_check(1
A std::string version of C sprintf.
Definition: format.cpp:21
static unsigned int DNS_LOOKUP_TIMEOUT_MS
See description of CClientTCPSocket.
int setSOSendBufffer(const int &newValue)
Set the size of the SO send buffer.
GLsizei const GLchar ** string
Definition: glext.h:3919
size_t Write(const void *Buffer, size_t Count) MRPT_OVERRIDE
Introduces a virtual method responsible for writing to the stream.
int getSOSendBufffer()
Return the current size of the SO send buffer.
#define MRPT_START
This is the global namespace for all Mobile Robot Programming Toolkit (MRPT) libraries.
void connect(const std::string &remotePartAddress, unsigned short remotePartTCPPort, unsigned int timeout_ms=0)
Establishes a connection with a remote part.
GLdouble GLdouble GLdouble r
Definition: glext.h:3618
CClientTCPSocket()
Default constructor.
size_t getReadPendingBytes()
Return the number of bytes already in the receive queue (they can be read without waiting) ...
int BASE_IMPEXP _strcmpi(const char *str1, const char *str2) MRPT_NO_THROWS
An OS-independent version of strcmpi.
Definition: os.cpp:320
int getTCPNoDelay()
Return the value of the TCPNoDelay option.
GLuint GLsizei GLsizei * length
Definition: glext.h:3900
bool BASE_IMPEXP 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:430
std::vector< uint8_t > content
The contents of the message (memory is automatically handled by the std::vector object) ...
Definition: CMessage.h:33
GLsizei const GLfloat * value
Definition: glext.h:3929
GLuint res
Definition: glext.h:6298
bool receiveMessage(CMessage &inMsg, const unsigned int timeoutStart_ms=100, const unsigned int timeoutBetween_ms=1000)
Waits for an incoming message through the TCP stream.
unsigned __int32 uint32_t
Definition: rptypes.h:49
A class that contain generic messages, that can be sent and received from a "CClientTCPSocket" object...
Definition: CMessage.h:29
uint32_t type
An identifier of the message type (only the least-sig byte is typically sent)
Definition: CMessage.h:32
int setTCPNoDelay(const int &newValue)
Set the TCP no delay option of the protocol (Nagle algorithm).



Page generated by Doxygen 1.8.14 for MRPT 1.5.7 Git: 5902e14cc Wed Apr 24 15:04:01 2019 +0200 at lun oct 28 01:39:17 CET 2019