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
size_t getReadPendingBytes()
Return the number of bytes already in the receive queue (they can be read without waiting)
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.
static unsigned int DNS_LOOKUP_TIMEOUT_MS
See description of CClientTCPSocket.
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 ...
void sendString(const std::string &str)
Writes a string to the socket.
bool isConnected()
Returns true if this objects represents a successfully connected socket.
int setSOSendBufffer(const int &newValue)
Set the size of the SO send buffer.
size_t Write(const void *Buffer, size_t Count) MRPT_OVERRIDE
Introduces a virtual method responsible for writing to the stream.
void connect(const std::string &remotePartAddress, unsigned short remotePartTCPPort, unsigned int timeout_ms=0)
Establishes a connection with a remote part.
int getSOSendBufffer()
Return the current size of the SO send buffer.
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.
int setTCPNoDelay(const int &newValue)
Set the TCP no delay option of the protocol (Nagle algorithm).
CClientTCPSocket()
Default constructor.
int getTCPNoDelay()
Return the value of the TCPNoDelay option.
void close()
Closes the connection.
bool sendMessage(const CMessage &outMsg, const int timeout_ms=-1)
Send a message through the TCP stream.
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.
A class that contain generic messages, that can be sent and received from a "CClientTCPSocket" object...
Definition: CMessage.h:30
std::vector< uint8_t > content
The contents of the message (memory is automatically handled by the std::vector object)
Definition: CMessage.h:33
uint32_t type
An identifier of the message type (only the least-sig byte is typically sent)
Definition: CMessage.h:32
GLuint res
Definition: glext.h:6298
GLuint GLsizei GLsizei * length
Definition: glext.h:3900
GLdouble GLdouble GLdouble r
Definition: glext.h:3618
GLsizei const GLfloat * value
Definition: glext.h:3929
GLsizei const GLchar ** string
Definition: glext.h:3919
int BASE_IMPEXP _strcmpi(const char *str1, const char *str2) MRPT_NO_THROWS
An OS-independent version of strcmpi.
Definition: os.cpp:320
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
#define MRPT_START
Definition: mrpt_macros.h:366
#define MRPT_END
Definition: mrpt_macros.h:370
#define THROW_EXCEPTION(msg)
Definition: mrpt_macros.h:154
#define THROW_EXCEPTION_FMT(_FORMAT_STRING,...)
Definition: mrpt_macros.h:163
This namespace provides a OS-independent interface to many useful functions: filenames manipulation,...
Definition: math_frwds.h:30
Classes for serialization, sockets, ini-file manipulation, streams, list of properties-values,...
Definition: zip.h:16
This is the global namespace for all Mobile Robot Programming Toolkit (MRPT) libraries.
std::string BASE_IMPEXP format(const char *fmt,...) MRPT_printf_format_check(1
A std::string version of C sprintf.
Definition: format.cpp:21
unsigned __int32 uint32_t
Definition: rptypes.h:49



Page generated by Doxygen 1.9.1 for MRPT 1.5.7 Git: 5902e14cc Wed Apr 24 15:04:01 2019 +0200 at mar 26 may 2026 13:12:03 CEST