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  // Close socket:
73  close();
74 #ifdef _WIN32
75  WSACleanup();
76 #else
77 // Nothing else to do.
78 #endif
79 }
80 
82 {
84 
85 #ifdef _WIN32
86  // Delete socket:
87  if (m_hSock != INVALID_SOCKET)
88  {
89  shutdown(m_hSock, 2); // SD_BOTH );
90  closesocket(m_hSock);
91  m_hSock = INVALID_SOCKET;
92  }
93 #else
94  // Delete socket:
95  if (m_hSock != -1)
96  {
97  shutdown(m_hSock, SHUT_RDWR);
98  ::close(m_hSock);
99  m_hSock = -1;
100  }
101 #endif
102  MRPT_END
103 }
104 
105 /*---------------------------------------------------------------
106  Read
107  ---------------------------------------------------------------*/
108 size_t CClientTCPSocket::Read(void* Buffer, size_t Count)
109 {
110  MRPT_START
111 
112  return readAsync(Buffer, Count);
113 
114  MRPT_END
115 }
116 
117 /*---------------------------------------------------------------
118  Write
119  ---------------------------------------------------------------*/
120 size_t CClientTCPSocket::Write(const void* Buffer, size_t Count)
121 {
122  MRPT_START
123 
124  return writeAsync(Buffer, Count);
125 
126  MRPT_END
127 }
128 
130 {
131  Write(str.c_str(), str.size());
132 }
133 
134 /*---------------------------------------------------------------
135  connect
136  ---------------------------------------------------------------*/
138  const std::string& remotePartAddress, unsigned short remotePartTCPPort,
139  unsigned int timeout_ms)
140 {
141  MRPT_START
142 
143  // Close existing socket, if any.
144  if (m_hSock != INVALID_SOCKET) close();
145 
146  // Create the socket:
147  if (INVALID_SOCKET == (m_hSock = socket(AF_INET, SOCK_STREAM, 0)))
149  "Error creating new client socket:\n%s",
150  getLastErrorStr().c_str()));
151 
152  struct sockaddr_in otherAddress;
153 
154  otherAddress.sin_family = AF_INET;
155  otherAddress.sin_port = htons(remotePartTCPPort);
156 
157  // Resolve the IP address of the given host name
158  std::string solved_IP;
160  remotePartAddress, solved_IP, DNS_LOOKUP_TIMEOUT_MS))
162  "DNS lookup failed for '%s'", remotePartAddress.c_str());
163 
164  // Fill out from IP address text:
165  otherAddress.sin_addr.s_addr = inet_addr(solved_IP.c_str());
166  if (INADDR_NONE == otherAddress.sin_addr.s_addr)
168  "Invalid IP address provided: %s", solved_IP.c_str());
169 
170 // Set to NON-BLOCKING:
171 #ifdef _WIN32
172  unsigned long non_block_mode = 1;
173  if (ioctlsocket(m_hSock, FIONBIO, &non_block_mode))
174  THROW_EXCEPTION("Error entering non-blocking mode with ioctlsocket();");
175 #else
176  int oldflags = fcntl(m_hSock, F_GETFL, 0);
177  if (oldflags == -1) THROW_EXCEPTION("Error retrieving fcntl();of socket.");
178  oldflags |= O_NONBLOCK; // Set NON-BLOCKING
179  if (-1 == fcntl(m_hSock, F_SETFL, oldflags))
180  THROW_EXCEPTION("Error entering non-blocking mode with fcntl();");
181 #endif
182 
183  // Try to connect:
184  int r = ::connect(
185  m_hSock, (struct sockaddr*)&otherAddress, sizeof(otherAddress));
186 #ifdef _WIN32
187  int er = WSAGetLastError();
188  if (r < 0 && er != WSAEINPROGRESS && er != WSAEWOULDBLOCK)
189 #else
190  int er = errno;
191  if (r < 0 && er != EINPROGRESS)
192 #endif
194  "Error connecting to %s:%hu. Error: %s [%d]",
195  remotePartAddress.c_str(), remotePartTCPPort, strerror(er), er));
196 
197  // Wait for connect:
198  fd_set socket_set;
199  timeval timer;
200 
201  FD_ZERO(&socket_set);
202  FD_SET(m_hSock, &socket_set);
203 
204  timer.tv_sec = timeout_ms / 1000;
205  timer.tv_usec = 1000 * (timeout_ms % 1000);
206 
207  int sel_ret = select(
208  m_hSock + 1,
209  nullptr, // For read
210  &socket_set, // For write or *connect done*
211  &socket_set, // For errors
212  timeout_ms == 0 ? nullptr : &timer);
213 
214  if (sel_ret == 0)
216  "Timeout connecting to '%s:%hu':\n%s", remotePartAddress.c_str(),
217  remotePartTCPPort, getLastErrorStr().c_str()));
218  if (sel_ret == -1)
220  "Error connecting to '%s:%hu':\n%s", remotePartAddress.c_str(),
221  remotePartTCPPort, getLastErrorStr().c_str()));
222 
223  // Now, make sure it was not an error!
224  int valopt;
225 #ifdef _WIN32
226  int lon = sizeof(int);
227  getsockopt(m_hSock, SOL_SOCKET, SO_ERROR, (char*)(&valopt), &lon);
228 #else
229  socklen_t lon = sizeof(int);
230  getsockopt(m_hSock, SOL_SOCKET, SO_ERROR, (void*)(&valopt), &lon);
231 #endif
232 
233 #ifdef _WIN32
234  if (valopt)
236  "Error connecting to %s:%hu. Error: %i.", remotePartAddress.c_str(),
237  remotePartTCPPort, valopt));
238 #else
239  if (valopt)
241  "Error connecting to %s:%hu. Error: %s.", remotePartAddress.c_str(),
242  remotePartTCPPort, strerror(valopt)));
243 #endif
244 // Connected!
245 
246 // If connected OK, remove the non-blocking flag:
247 #ifdef _WIN32
248  non_block_mode = 0;
249  if (ioctlsocket(m_hSock, FIONBIO, &non_block_mode))
250  THROW_EXCEPTION("Error entering blocking mode with ioctlsocket();");
251 #else
252  oldflags &= ~O_NONBLOCK; // Set BLOCKING
253  if (-1 == fcntl(m_hSock, F_SETFL, oldflags))
254  THROW_EXCEPTION("Error entering blocking mode with fcntl();");
255 #endif
256 
257  // Save the IP of the other part.
258  m_remotePartIP = remotePartAddress;
259 
260  MRPT_END
261 }
262 
263 /*---------------------------------------------------------------
264  isConnected
265  ---------------------------------------------------------------*/
266 bool CClientTCPSocket::isConnected() { return (m_hSock != INVALID_SOCKET); }
267 /*---------------------------------------------------------------
268  readAsync
269  ---------------------------------------------------------------*/
271  void* Buffer, const size_t Count, const int timeoutStart_ms,
272  const int timeoutBetween_ms)
273 {
274  MRPT_START
275 
276  if (m_hSock == INVALID_SOCKET) return 0; // The socket is not connected!
277 
278  size_t remainToRead, alreadyRead = 0;
279  int readNow;
280  bool timeoutExpired = false;
281 
282  struct timeval timeoutSelect;
283  struct timeval* ptrTimeout;
284  fd_set sockArr;
285 
286  // Init fd_set structure & add our socket to it:
287  FD_ZERO(&sockArr);
288  FD_SET(m_hSock, &sockArr);
289 
290  // Loop until timeout expires or the socket is closed.
291  while (alreadyRead < Count && !timeoutExpired)
292  {
293  // Use the "first" or "between" timeouts:
294  int curTimeout = alreadyRead == 0 ? timeoutStart_ms : timeoutBetween_ms;
295 
296  if (curTimeout < 0)
297  ptrTimeout = nullptr;
298  else
299  {
300  timeoutSelect.tv_sec = curTimeout / 1000;
301  timeoutSelect.tv_usec = 1000 * (curTimeout % 1000);
302  ptrTimeout = &timeoutSelect;
303  }
304 
305  // Wait for received data
306  int selRet = ::select(
307  m_hSock + 1, // __nfds
308  &sockArr, // Wait for read
309  nullptr, // Wait for write
310  nullptr, // Wait for except.
311  ptrTimeout); // Timeout
312 
313  if (selRet == INVALID_SOCKET)
315  "Error reading from socket: %s", getLastErrorStr().c_str());
316 
317  if (selRet == 0)
318  {
319  // Timeout:
320  timeoutExpired = true;
321  }
322  else
323  {
324  // Compute remaining part:
325  remainToRead = Count - alreadyRead;
326 
327  // Receive bytes:
328  readNow = ::recv(
329  m_hSock, ((char*)Buffer) + alreadyRead, (int)remainToRead, 0);
330 
331  if (readNow != INVALID_SOCKET)
332  {
333  // Accumulate the received length:
334  alreadyRead += readNow;
335  }
336  else
337  {
338  // Error: Socket closed?
339  this->close();
340  return alreadyRead;
341  }
342 
343  if (readNow == 0 && remainToRead != 0)
344  {
345  // We had an event of data available, so if we have now a zero,
346  // the socket has been gracefully closed:
347  timeoutExpired = true;
348  close();
349  }
350  }
351  } // end while
352 
353  return alreadyRead;
354 
355  MRPT_END
356 }
357 
358 /*---------------------------------------------------------------
359  writeAsync
360  ---------------------------------------------------------------*/
362  const void* Buffer, const size_t Count, const int timeout_ms)
363 {
364  MRPT_START
365 
366  if (m_hSock == INVALID_SOCKET) return 0; // The socket is not connected!
367 
368  size_t remainToWrite, alreadyWritten = 0;
369  int writtenNow;
370  bool timeoutExpired = false;
371 
372  struct timeval timeoutSelect;
373  struct timeval* ptrTimeout;
374  fd_set sockArr;
375 
376  // Init fd_set structure & add our socket to it:
377  FD_ZERO(&sockArr);
378  FD_SET(m_hSock, &sockArr);
379 
380  // The timeout:
381  if (timeout_ms < 0)
382  {
383  ptrTimeout = nullptr;
384  }
385  else
386  {
387  timeoutSelect.tv_sec = timeout_ms / 1000;
388  timeoutSelect.tv_usec = 1000 * (timeout_ms % 1000);
389  ptrTimeout = &timeoutSelect;
390  }
391 
392  // Loop until timeout expires or the socket is closed.
393  while (alreadyWritten < Count && !timeoutExpired)
394  {
395  // Wait for received data
396  int selRet = ::select(
397  m_hSock + 1, // __nfds
398  nullptr, // Wait for read
399  &sockArr, // Wait for write
400  nullptr, // Wait for except.
401  ptrTimeout); // Timeout
402 
403  if (selRet == INVALID_SOCKET)
405  "Error writing to socket: %s", getLastErrorStr().c_str());
406 
407  if (selRet == 0)
408  {
409  // Timeout:
410  timeoutExpired = true;
411  }
412  else
413  {
414  // We have room to write data!
415 
416  // Compute remaining part:
417  remainToWrite = Count - alreadyWritten;
418 
419  // Receive bytes:
420  writtenNow = ::send(
421  m_hSock, ((char*)Buffer) + alreadyWritten, (int)remainToWrite,
422  0);
423 
424  if (writtenNow != INVALID_SOCKET)
425  {
426  // Accumulate the received length:
427  alreadyWritten += writtenNow;
428  }
429  }
430 
431  } // end while
432 
433  return alreadyWritten;
434 
435  MRPT_END
436 }
437 
438 /*---------------------------------------------------------------
439  getReadPendingBytes
440  ---------------------------------------------------------------*/
442 {
443  if (m_hSock == INVALID_SOCKET) return 0; // The socket is not connected!
444  unsigned long ret = 0;
445  if (
446 #ifdef _WIN32
447  ioctlsocket(m_hSock, FIONREAD, &ret)
448 #else
449  ioctl(m_hSock, FIONREAD, &ret)
450 #endif
451  )
452  {
453  THROW_EXCEPTION("Error invoking ioctlsocket(FIONREAD)");
454  }
455  else
456  return ret;
457 }
458 
459 /*---------------------------------------------------------------
460  setTCPNoDelay
461  ---------------------------------------------------------------*/
462 int CClientTCPSocket::setTCPNoDelay(const int& newValue)
463 {
464  int length = sizeof(newValue);
465 
466  return setsockopt(
467  m_hSock, IPPROTO_TCP, TCP_NODELAY, (char*)&newValue, length);
468 }
469 
470 /*---------------------------------------------------------------
471  getTCPNoDelay
472  ---------------------------------------------------------------*/
474 {
475  int value;
476 #ifdef _WIN32
477  int length = sizeof(value);
478 #else
479  unsigned int length = sizeof(value);
480 #endif
481  int res =
482  getsockopt(m_hSock, IPPROTO_TCP, TCP_NODELAY, (char*)&value, &length);
483 
484  if (res == -1)
485  return -1;
486  else
487  return value;
488 }
489 
490 /*---------------------------------------------------------------
491  setSOSendBufffer
492  ---------------------------------------------------------------*/
493 int CClientTCPSocket::setSOSendBufffer(const int& newValue)
494 {
495  const unsigned int length = sizeof(newValue);
496 
497  return setsockopt(m_hSock, SOL_SOCKET, SO_SNDBUF, (char*)&newValue, length);
498 }
499 
500 /*---------------------------------------------------------------
501  getSOSendBufffer
502  ---------------------------------------------------------------*/
504 {
505  int value;
506 #ifdef _WIN32
507  int length = sizeof(value);
508 #else
509  unsigned int length = sizeof(value);
510 #endif
511  getsockopt(m_hSock, SOL_SOCKET, SO_SNDBUF, (char*)&value, &length);
512 
513  return value;
514 }
515 
517 {
519 }
520 
521 uint64_t CClientTCPSocket::Seek(int64_t off, CStream::TSeekOrigin org)
522 {
523  MRPT_START
524  MRPT_UNUSED_PARAM(off);
525  MRPT_UNUSED_PARAM(org);
526  THROW_EXCEPTION("This method has no effect in this class!");
527  MRPT_END
528 }
529 
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 }
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
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:479
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:403
CClientTCPSocket()
Default constructor.
__int64 int64_t
Definition: rptypes.h:52
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.
unsigned __int64 uint64_t
Definition: rptypes.h:53
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
uint64_t getPosition() const override
This virtual method has no effect in this implementation over a TCP socket, and its use raises an exc...
std::string format(const char *fmt,...) MRPT_printf_format_check(1
A std::string version of C sprintf.
Definition: format.cpp:16
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.
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.
#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: 8fe78517f Sun Jul 14 19:43:28 2019 +0200 at lun oct 28 02:10:00 CET 2019