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-2018, 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 
12 #include <mrpt/core/exceptions.h>
14 #include <mrpt/comms/net_utils.h>
15 #include <cstring>
16 
17 #ifdef _WIN32
18 // Windows
19 #define _WINSOCK_DEPRECATED_NO_WARNINGS
20 #include <winsock2.h>
21 #include <winerror.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 <sys/socket.h>
29 #include <unistd.h>
30 #include <fcntl.h>
31 #include <errno.h>
32 #include <sys/types.h>
33 #include <sys/ioctl.h>
34 #include <netdb.h>
35 #include <arpa/inet.h>
36 #include <netinet/in.h>
37 #include <netinet/tcp.h>
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  format(
150  "Error creating new client socket:\n%s",
151  getLastErrorStr().c_str()));
152 
153  struct sockaddr_in otherAddress;
154 
155  otherAddress.sin_family = AF_INET;
156  otherAddress.sin_port = htons(remotePartTCPPort);
157 
158  // Resolve the IP address of the given host name
159  std::string solved_IP;
161  remotePartAddress, solved_IP, DNS_LOOKUP_TIMEOUT_MS))
163  "DNS lookup failed for '%s'", remotePartAddress.c_str());
164 
165  // Fill out from IP address text:
166  otherAddress.sin_addr.s_addr = inet_addr(solved_IP.c_str());
167  if (INADDR_NONE == otherAddress.sin_addr.s_addr)
169  "Invalid IP address provided: %s", solved_IP.c_str());
170 
171 // Set to NON-BLOCKING:
172 #ifdef _WIN32
173  unsigned long non_block_mode = 1;
174  if (ioctlsocket(m_hSock, FIONBIO, &non_block_mode))
175  THROW_EXCEPTION("Error entering non-blocking mode with ioctlsocket();");
176 #else
177  int oldflags = fcntl(m_hSock, F_GETFL, 0);
178  if (oldflags == -1) THROW_EXCEPTION("Error retrieving fcntl();of socket.");
179  oldflags |= O_NONBLOCK; // Set NON-BLOCKING
180  if (-1 == fcntl(m_hSock, F_SETFL, oldflags))
181  THROW_EXCEPTION("Error entering non-blocking mode with fcntl();");
182 #endif
183 
184  // Try to connect:
185  int r = ::connect(
186  m_hSock, (struct sockaddr*)&otherAddress, sizeof(otherAddress));
187 #ifdef _WIN32
188  int er = WSAGetLastError();
189  if (r < 0 && er != WSAEINPROGRESS && er != WSAEWOULDBLOCK)
190 #else
191  int er = errno;
192  if (r < 0 && er != EINPROGRESS)
193 #endif
195  format(
196  "Error connecting to %s:%hu. Error: %s [%d]",
197  remotePartAddress.c_str(), remotePartTCPPort, strerror(er),
198  er));
199 
200  // Wait for connect:
201  fd_set socket_set;
202  timeval timer;
203 
204  FD_ZERO(&socket_set);
205  FD_SET(m_hSock, &socket_set);
206 
207  timer.tv_sec = timeout_ms / 1000;
208  timer.tv_usec = 1000 * (timeout_ms % 1000);
209 
210  int sel_ret = select(
211  m_hSock + 1,
212  nullptr, // For read
213  &socket_set, // For write or *connect done*
214  &socket_set, // For errors
215  timeout_ms == 0 ? nullptr : &timer);
216 
217  if (sel_ret == 0)
219  format(
220  "Timeout connecting to '%s:%hu':\n%s",
221  remotePartAddress.c_str(), remotePartTCPPort,
222  getLastErrorStr().c_str()));
223  if (sel_ret == -1)
225  format(
226  "Error connecting to '%s:%hu':\n%s", remotePartAddress.c_str(),
227  remotePartTCPPort, getLastErrorStr().c_str()));
228 
229  // Now, make sure it was not an error!
230  int valopt;
231 #ifdef _WIN32
232  int lon = sizeof(int);
233  getsockopt(m_hSock, SOL_SOCKET, SO_ERROR, (char*)(&valopt), &lon);
234 #else
235  socklen_t lon = sizeof(int);
236  getsockopt(m_hSock, SOL_SOCKET, SO_ERROR, (void*)(&valopt), &lon);
237 #endif
238 
239 #ifdef _WIN32
240  if (valopt)
242  format(
243  "Error connecting to %s:%hu. Error: %i.",
244  remotePartAddress.c_str(), remotePartTCPPort, valopt));
245 #else
246  if (valopt)
248  format(
249  "Error connecting to %s:%hu. Error: %s.",
250  remotePartAddress.c_str(), remotePartTCPPort,
251  strerror(valopt)));
252 #endif
253 // Connected!
254 
255 // If connected OK, remove the non-blocking flag:
256 #ifdef _WIN32
257  non_block_mode = 0;
258  if (ioctlsocket(m_hSock, FIONBIO, &non_block_mode))
259  THROW_EXCEPTION("Error entering blocking mode with ioctlsocket();");
260 #else
261  oldflags &= ~O_NONBLOCK; // Set BLOCKING
262  if (-1 == fcntl(m_hSock, F_SETFL, oldflags))
263  THROW_EXCEPTION("Error entering blocking mode with fcntl();");
264 #endif
265 
266  // Save the IP of the other part.
267  m_remotePartIP = remotePartAddress;
268 
269  MRPT_END
270 }
271 
272 /*---------------------------------------------------------------
273  isConnected
274  ---------------------------------------------------------------*/
275 bool CClientTCPSocket::isConnected() { return (m_hSock != INVALID_SOCKET); }
276 /*---------------------------------------------------------------
277  readAsync
278  ---------------------------------------------------------------*/
280  void* Buffer, const size_t Count, const int timeoutStart_ms,
281  const int timeoutBetween_ms)
282 {
283  MRPT_START
284 
285  if (m_hSock == INVALID_SOCKET) return 0; // The socket is not connected!
286 
287  size_t remainToRead, alreadyRead = 0;
288  int readNow;
289  bool timeoutExpired = false;
290 
291  struct timeval timeoutSelect;
292  struct timeval* ptrTimeout;
293  fd_set sockArr;
294 
295  // Init fd_set structure & add our socket to it:
296  FD_ZERO(&sockArr);
297  FD_SET(m_hSock, &sockArr);
298 
299  // Loop until timeout expires or the socket is closed.
300  while (alreadyRead < Count && !timeoutExpired)
301  {
302  // Use the "first" or "between" timeouts:
303  int curTimeout = alreadyRead == 0 ? timeoutStart_ms : timeoutBetween_ms;
304 
305  if (curTimeout < 0)
306  ptrTimeout = nullptr;
307  else
308  {
309  timeoutSelect.tv_sec = curTimeout / 1000;
310  timeoutSelect.tv_usec = 1000 * (curTimeout % 1000);
311  ptrTimeout = &timeoutSelect;
312  }
313 
314  // Wait for received data
315  int selRet = ::select(
316  m_hSock + 1, // __nfds
317  &sockArr, // Wait for read
318  nullptr, // Wait for write
319  nullptr, // Wait for except.
320  ptrTimeout); // Timeout
321 
322  if (selRet == INVALID_SOCKET)
324  "Error reading from socket: %s", getLastErrorStr().c_str());
325 
326  if (selRet == 0)
327  {
328  // Timeout:
329  timeoutExpired = true;
330  }
331  else
332  {
333  // Compute remaining part:
334  remainToRead = Count - alreadyRead;
335 
336  // Receive bytes:
337  readNow = ::recv(
338  m_hSock, ((char*)Buffer) + alreadyRead, (int)remainToRead, 0);
339 
340  if (readNow != INVALID_SOCKET)
341  {
342  // Accumulate the received length:
343  alreadyRead += readNow;
344  }
345  else
346  {
347  // Error: Socket closed?
348  this->close();
349  return alreadyRead;
350  }
351 
352  if (readNow == 0 && remainToRead != 0)
353  {
354  // We had an event of data available, so if we have now a zero,
355  // the socket has been gracefully closed:
356  timeoutExpired = true;
357  close();
358  }
359  }
360  } // end while
361 
362  return alreadyRead;
363 
364  MRPT_END
365 }
366 
367 /*---------------------------------------------------------------
368  writeAsync
369  ---------------------------------------------------------------*/
371  const void* Buffer, const size_t Count, const int timeout_ms)
372 {
373  MRPT_START
374 
375  if (m_hSock == INVALID_SOCKET) return 0; // The socket is not connected!
376 
377  size_t remainToWrite, alreadyWritten = 0;
378  int writtenNow;
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  // The timeout:
390  if (timeout_ms < 0)
391  {
392  ptrTimeout = nullptr;
393  }
394  else
395  {
396  timeoutSelect.tv_sec = timeout_ms / 1000;
397  timeoutSelect.tv_usec = 1000 * (timeout_ms % 1000);
398  ptrTimeout = &timeoutSelect;
399  }
400 
401  // Loop until timeout expires or the socket is closed.
402  while (alreadyWritten < Count && !timeoutExpired)
403  {
404  // Wait for received data
405  int selRet = ::select(
406  m_hSock + 1, // __nfds
407  nullptr, // Wait for read
408  &sockArr, // Wait for write
409  nullptr, // Wait for except.
410  ptrTimeout); // Timeout
411 
412  if (selRet == INVALID_SOCKET)
414  "Error writing to socket: %s", getLastErrorStr().c_str());
415 
416  if (selRet == 0)
417  {
418  // Timeout:
419  timeoutExpired = true;
420  }
421  else
422  {
423  // We have room to write data!
424 
425  // Compute remaining part:
426  remainToWrite = Count - alreadyWritten;
427 
428  // Receive bytes:
429  writtenNow = ::send(
430  m_hSock, ((char*)Buffer) + alreadyWritten, (int)remainToWrite,
431  0);
432 
433  if (writtenNow != INVALID_SOCKET)
434  {
435  // Accumulate the received length:
436  alreadyWritten += writtenNow;
437  }
438  }
439 
440  } // end while
441 
442  return alreadyWritten;
443 
444  MRPT_END
445 }
446 
447 /*---------------------------------------------------------------
448  getReadPendingBytes
449  ---------------------------------------------------------------*/
451 {
452  if (m_hSock == INVALID_SOCKET) return 0; // The socket is not connected!
453  unsigned long ret = 0;
454  if (
455 #ifdef _WIN32
456  ioctlsocket(m_hSock, FIONREAD, &ret)
457 #else
458  ioctl(m_hSock, FIONREAD, &ret)
459 #endif
460  )
461  {
462  THROW_EXCEPTION("Error invoking ioctlsocket(FIONREAD)");
463  }
464  else
465  return ret;
466 }
467 
468 /*---------------------------------------------------------------
469  setTCPNoDelay
470  ---------------------------------------------------------------*/
471 int CClientTCPSocket::setTCPNoDelay(const int& newValue)
472 {
473  int length = sizeof(newValue);
474 
475  return setsockopt(
476  m_hSock, IPPROTO_TCP, TCP_NODELAY, (char*)&newValue, length);
477 }
478 
479 /*---------------------------------------------------------------
480  getTCPNoDelay
481  ---------------------------------------------------------------*/
483 {
484  int value;
485 #ifdef _WIN32
486  int length = sizeof(value);
487 #else
488  unsigned int length = sizeof(value);
489 #endif
490  int res =
491  getsockopt(m_hSock, IPPROTO_TCP, TCP_NODELAY, (char*)&value, &length);
492 
493  if (res == -1)
494  return -1;
495  else
496  return value;
497 }
498 
499 /*---------------------------------------------------------------
500  setSOSendBufffer
501  ---------------------------------------------------------------*/
502 int CClientTCPSocket::setSOSendBufffer(const int& newValue)
503 {
504  const unsigned int length = sizeof(newValue);
505 
506  return setsockopt(m_hSock, SOL_SOCKET, SO_SNDBUF, (char*)&newValue, length);
507 }
508 
509 /*---------------------------------------------------------------
510  getSOSendBufffer
511  ---------------------------------------------------------------*/
513 {
514  int value;
515 #ifdef _WIN32
516  int length = sizeof(value);
517 #else
518  unsigned int length = sizeof(value);
519 #endif
520  getsockopt(m_hSock, SOL_SOCKET, SO_SNDBUF, (char*)&value, &length);
521 
522  return value;
523 }
524 
526 {
528 }
529 
530 uint64_t CClientTCPSocket::Seek(int64_t off, CStream::TSeekOrigin org)
531 {
532  MRPT_START
533  MRPT_UNUSED_PARAM(off);
534  MRPT_UNUSED_PARAM(org);
535  THROW_EXCEPTION("This method has no effect in this class!");
536  MRPT_END
537 }
538 
540 {
541  MRPT_START
542  THROW_EXCEPTION("This method has no effect in this class!");
543  MRPT_END
544 }
545 
547 {
548  MRPT_START
549  THROW_EXCEPTION("This method has no effect in this class!");
550  MRPT_END
551 }
static unsigned int DNS_LOOKUP_TIMEOUT_MS
See description of CClientTCPSocket.
#define MRPT_START
Definition: exceptions.h:262
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:41
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...
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.
__int64 int64_t
Definition: rptypes.h:49
int getTCPNoDelay()
Return the value of the TCPNoDelay option.
GLsizei const GLchar ** string
Definition: glext.h:4101
std::string getLastErrorStr()
Returns a description of the last Sockets error.
unsigned __int64 uint64_t
Definition: rptypes.h:50
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:3705
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:266
GLuint GLsizei GLsizei * length
Definition: glext.h:4064
void close()
Closes the connection.
GLsizei const GLfloat * value
Definition: glext.h:4117
size_t getReadPendingBytes()
Return the number of bytes already in the receive queue (they can be read without waiting) ...
GLuint res
Definition: glext.h:7268
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:43
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: 7d5e6d718 Fri Aug 24 01:51:28 2018 +0200 at lun nov 2 08:35:50 CET 2020