DTrackSDK  v2.9.0
DTrackNet.cpp
1 /* DTrackNet: C++ source file
2  *
3  * DTrackSDK: functions for receiving and sending UDP/TCP packets.
4  *
5  * Copyright 2007-2021, Advanced Realtime Tracking GmbH & Co. KG
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  * notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  * notice, this list of conditions and the following disclaimer in the
14  * documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of copyright holder nor the names of its contributors
16  * may be used to endorse or promote products derived from this software
17  * without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  *
30  * Version v2.7.0
31  *
32  */
33 
34 #include "DTrackNet.hpp"
35 
36 #include <cstdlib>
37 #include <cstdio>
38 #include <cstring>
39 
40 // usually the following should work; otherwise define OS_* manually:
41 #if defined(_WIN32) || defined(WIN32) || defined(_WIN64)
42  #define OS_WIN // for MS Windows (2000, XP, Vista, 7, 8, 10)
43 #else
44  #define OS_UNIX // for Unix (Linux, Irix)
45 #endif
46 
47 #ifdef OS_UNIX
48  #include <unistd.h>
49  #include <netdb.h>
50  #include <sys/socket.h>
51  #include <sys/time.h>
52  #include <netinet/in.h>
53  #include <arpa/inet.h>
54 #endif
55 #ifdef OS_WIN
56  #include <ws2tcpip.h>
57  #include <winsock2.h>
58  #include <windows.h>
59 #endif
60 
61 namespace DTrackNet {
62 
67 #ifdef OS_UNIX
68  int ossock; // Unix socket
69 #endif
70 #ifdef OS_WIN
71  SOCKET ossock; // Windows socket
72 #endif
73 };
74 
75 
76 /*
77  * Initialize network ressources.
78  */
79 void net_init(void)
80 {
81 #ifdef OS_WIN
82  // initialize socket dll (only Windows):
83  WORD vreq;
84  WSADATA wsa;// internal socket type
85  vreq = MAKEWORD(2, 0);
86  if (WSAStartup(vreq, &wsa) != 0)
87  {
88  return;
89  }
90 #endif
91 }
92 
93 
94 /*
95  * Free network ressources.
96  */
97 void net_exit(void)
98 {
99 #ifdef OS_WIN
100  WSACleanup();
101 #endif
102 }
103 
104 
105 /*
106  * Convert string to IP address (IPv4 only).
107  */
108 unsigned int ip_name2ip( const char* name )
109 {
110  int err;
111  struct addrinfo hints, *res;
112 
113  memset( &hints, 0, sizeof( hints ) );
114  hints.ai_family = AF_INET; // only IPv4 supported
115  hints.ai_socktype = SOCK_STREAM;
116 
117  res = NULL;
118  err = getaddrinfo( name, NULL, &hints, &res );
119  if ( err != 0 || res == NULL )
120  return 0;
121 
122  unsigned int ip;
123  struct sockaddr_in sin;
124  memcpy( &sin, res->ai_addr, sizeof( sin ) ); // casting is causing warnings (-Wcast-align) by some compilers
125  ip = ntohl( ( (struct in_addr )( sin.sin_addr ) ).s_addr );
126 
127  freeaddrinfo( res );
128  return ip;
129 }
130 
131 
132 // ---------------------------------------------------------------------------------------------------
133 // Handling UDP data:
134 // ---------------------------------------------------------------------------------------------------
135 
136 /*
137  * Initialize UDP socket.
138  */
139 UDP::UDP( unsigned short port, unsigned int multicastIp )
140  : m_isValid( false ), m_socket( NULL ), m_port( port ), m_multicastIp( 0 ), m_remoteIp( 0 )
141 {
142  struct _ip_socket_struct* s;
143  struct sockaddr_in addr;
144 #ifdef OS_UNIX
145  socklen_t addrlen;
146 #endif
147 #ifdef OS_WIN
148  int addrlen;
149 #endif
150 
151  s = new struct _ip_socket_struct();
152 
153  // create socket:
154 #ifdef OS_UNIX
155  s->ossock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
156  if (s->ossock < 0)
157  {
158  delete s;
159  return;
160  }
161 #endif
162 #ifdef OS_WIN
163  s->ossock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
164  if (s->ossock == INVALID_SOCKET)
165  {
166  delete s;
167  return;
168  }
169 #endif
170  m_socket = s;
171 
172  if ( multicastIp != 0 )
173  {
174  // set reuse port to on to allow multiple binds per host
175  int flag_on = 1;
176  if ( setsockopt( m_socket->ossock, SOL_SOCKET, SO_REUSEADDR, (char* )&flag_on, sizeof( flag_on ) ) < 0 )
177  {
178  perror("setsockopt() failed1");
179  return;
180  }
181  }
182 
183  // name socket:
184  addr.sin_family = AF_INET;
185  addr.sin_port = htons( m_port );
186  addr.sin_addr.s_addr = htonl(INADDR_ANY);
187  addrlen = sizeof(addr);
188  if ( bind( m_socket->ossock, (struct sockaddr *)&addr, addrlen ) < 0 )
189  return;
190 
191  if ( m_port == 0 )
192  {
193  // port number was chosen by the OS
194  if ( getsockname( m_socket->ossock, (struct sockaddr *)&addr, &addrlen ) )
195  return;
196 
197  m_port = ntohs( addr.sin_port );
198  }
199 
200  if ( multicastIp != 0 )
201  {
202  // construct an IGMP join request structure
203  struct ip_mreq ipmreq;
204  ipmreq.imr_multiaddr.s_addr = htonl( multicastIp );
205  ipmreq.imr_interface.s_addr = htonl(INADDR_ANY);
206  // send an ADD MEMBERSHIP message via setsockopt
207  if ( setsockopt( m_socket->ossock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char* )&ipmreq, sizeof( ipmreq ) ) < 0 )
208  {
209  perror("setsockopt() failed2");
210  return;
211  }
212  m_multicastIp = multicastIp;
213  }
214 
215  m_isValid = true;
216 }
217 
218 
219 /*
220  * Deinitialize UDP socket.
221  */
223 {
224  if ( m_socket == NULL ) return;
225 
226  if ( m_multicastIp != 0 )
227  {
228  struct ip_mreq ipmreq;
229  ipmreq.imr_multiaddr.s_addr = htonl( m_multicastIp );
230  ipmreq.imr_interface.s_addr = htonl(INADDR_ANY);
231  // send a DROP MEMBERSHIP message via setsockopt
232  if ( setsockopt( m_socket->ossock, IPPROTO_IP, IP_DROP_MEMBERSHIP, (char* )&ipmreq, sizeof( ipmreq ) ) < 0 )
233  {
234  perror("setsockopt() failed3");
235  }
236  }
237 
238 #ifdef OS_UNIX
239  close( m_socket->ossock );
240 #endif
241 #ifdef OS_WIN
242  closesocket( m_socket->ossock );
243 #endif
244  delete m_socket;
245 }
246 
247 
248 /*
249  * Returns if UDP socket is open to receive data.
250  */
252 {
253  return m_isValid;
254 }
255 
256 
257 /*
258  * Get UDP data port where data is received.
259  */
260 unsigned short UDP::getPort()
261 {
262  return m_port;
263 }
264 
265 
266 /*
267  * Get IP address of the sender of the latest received data.
268  */
269 unsigned int UDP::getRemoteIp()
270 {
271  return m_remoteIp;
272 }
273 
274 
275 /*
276  * Receive UDP data.
277  */
278 int UDP::receive( void *buffer, int maxLen, int toutUs )
279 {
280  int err;
281  fd_set set;
282  struct timeval tout;
283 
284  // waiting for data:
285  FD_ZERO(&set);
286  FD_SET( m_socket->ossock, &set );
287  tout.tv_sec = toutUs / 1000000;
288  tout.tv_usec = toutUs % 1000000;
289 
290  err = select( FD_SETSIZE, &set, NULL, NULL, &tout );
291  switch ( err )
292  {
293  case 1:
294  break; // data available
295  case 0:
296  return -1; // timeout
297  default:
298  return -2; // error
299  }
300 
301  // receiving packet:
302  while ( true )
303  {
304  struct sockaddr_in addr;
305 #ifdef OS_UNIX
306  socklen_t addrlen;
307 #endif
308 #ifdef OS_WIN
309  int addrlen;
310 #endif
311  addrlen = sizeof( struct sockaddr_in );
312 
313  int nbytes = static_cast< int >( recvfrom( m_socket->ossock, ( char* )buffer, maxLen, 0,
314  ( struct sockaddr* )&addr, &addrlen ) ); // receive one packet
315  if (nbytes < 0)
316  { // receive error
317  return -3;
318  }
319 
320  if ( addr.sin_family == AF_INET ) // only IPv4 supported
321  {
322  m_remoteIp = ntohl( addr.sin_addr.s_addr );
323  }
324 
325  // check, if more data available: if so, receive another packet
326  FD_ZERO(&set);
327  FD_SET( m_socket->ossock, &set );
328 
329  tout.tv_sec = 0; // no timeout
330  tout.tv_usec = 0;
331  if (select(FD_SETSIZE, &set, NULL, NULL, &tout) != 1)
332  {
333  // no more data available: check length of received packet and return
334  if ( nbytes >= maxLen )
335  { // buffer overflow
336  return -4;
337  }
338  return nbytes;
339  }
340  }
341 }
342 
343 
344 /*
345  * Send UDP data.
346  */
347 int UDP::send( const void* buffer, int len, unsigned int ip, unsigned short port, int toutUs )
348 {
349  fd_set set;
350  struct timeval tout;
351  int err;
352  struct sockaddr_in addr;
353 
354  // building address:
355  addr.sin_family = AF_INET;
356  addr.sin_addr.s_addr = htonl( ip );
357  addr.sin_port = htons(port);
358 
359  // waiting to send data:
360  FD_ZERO(&set);
361  FD_SET( m_socket->ossock, &set );
362  tout.tv_sec = toutUs / 1000000;
363  tout.tv_usec = toutUs % 1000000;
364 
365  err = select( FD_SETSIZE, NULL, &set, NULL, &tout );
366  switch ( err )
367  {
368  case 1:
369  break;
370  case 0:
371  return -1; // timeout
372  default:
373  return -2; // error
374  }
375 
376  // sending data:
377  int nbytes = static_cast< int >( sendto( m_socket->ossock, (const char* )buffer, len, 0, (struct sockaddr* )&addr,
378  (size_t )sizeof( struct sockaddr_in ) ) );
379  if(nbytes < len)
380  { // send error
381  return -3;
382  }
383  return 0;
384 }
385 
386 
387 // ---------------------------------------------------------------------------------------------------
388 // Handling TCP data:
389 // ---------------------------------------------------------------------------------------------------
390 
391 /*
392  * Initialize client TCP socket.
393  */
394 TCP::TCP( unsigned int ip, unsigned short port )
395  : m_isValid( false ), m_socket( NULL )
396 {
397  struct _ip_socket_struct* s;
398  struct sockaddr_in addr;
399 
400  s = new struct _ip_socket_struct();
401 
402  // create socket:
403 #ifdef OS_UNIX
404  s->ossock = socket(PF_INET, SOCK_STREAM, 0);
405  if (s->ossock < 0)
406  {
407  delete s;
408  return;
409  }
410 #endif
411 #ifdef OS_WIN
412  s->ossock = socket(PF_INET, SOCK_STREAM, 0);
413  if (s->ossock == INVALID_SOCKET)
414  {
415  delete s;
416  return;
417  }
418 #endif
419  m_socket = s;
420 
421  // connect with server:
422  addr.sin_family = AF_INET;
423  addr.sin_addr.s_addr = htonl(ip);
424  addr.sin_port = htons(port);
425  if ( connect( m_socket->ossock, (struct sockaddr *)&addr, (size_t )sizeof( addr ) ) != 0 )
426  return;
427 
428  m_isValid = true;
429 }
430 
431 
432 /*
433  * Deinitialize TCP socket.
434  */
436 {
437  if ( m_socket == NULL ) return;
438 
439 #ifdef OS_UNIX
440  close( m_socket->ossock );
441 #endif
442 #ifdef OS_WIN
443  closesocket( m_socket->ossock );
444 #endif
445  delete m_socket;
446 }
447 
448 
449 /*
450  * Returns if TCP connection is active.
451  */
453 {
454  return m_isValid;
455 }
456 
457 
458 /*
459  * Receive TCP data.
460  */
461 int TCP::receive( void *buffer, int maxLen, int toutUs )
462 {
463  int err;
464  fd_set set;
465  struct timeval tout;
466 
467  // waiting for data:
468  FD_ZERO(&set);
469  FD_SET( m_socket->ossock, &set );
470  tout.tv_sec = toutUs / 1000000;
471  tout.tv_usec = toutUs % 1000000;
472 
473  err = select( FD_SETSIZE, &set, NULL, NULL, &tout );
474  switch ( err )
475  {
476  case 1:
477  break; // data available
478  case 0:
479  return -1; // timeout
480  default:
481  return -2; // error
482  }
483 
484  // receiving packet:
485  int nbytes = static_cast< int >( recv( m_socket->ossock, (char *)buffer, maxLen, 0 ) );
486  if (nbytes == 0)
487  { // broken connection
488  return -9;
489  }
490  if (nbytes < 0)
491  { // receive error
492  return -3;
493  }
494  if (nbytes >= maxLen)
495  { // buffer overflow
496  return -4;
497  }
498  return nbytes;
499 }
500 
501 
502 /*
503  * Send TCP data.
504  */
505 int TCP::send( const void* buffer, int len, int toutUs )
506 {
507  fd_set set;
508  struct timeval tout;
509  int err;
510 
511  // waiting to send data:
512  FD_ZERO(&set);
513  FD_SET( m_socket->ossock, &set );
514  tout.tv_sec = toutUs / 1000000;
515  tout.tv_usec = toutUs % 1000000;
516 
517  err = select( FD_SETSIZE, NULL, &set, NULL, &tout );
518  switch ( err )
519  {
520  case 1:
521  break;
522  case 0:
523  return -1; // timeout
524  default:
525  return -2; // error
526  }
527 
528  // sending data:
529  int nbytes = static_cast< int >( sendto( m_socket->ossock, (const char* )buffer, len, 0, NULL, 0 ) );
530  if (nbytes < len)
531  { // send error
532  return -3;
533  }
534  return 0;
535 }
536 
537 
538 } // namespace DTrackNet
539 
int send(const void *buffer, int len, unsigned int ip, unsigned short port, int toutUs)
Send UDP data.
Definition: DTrackNet.cpp:347
unsigned short getPort()
Get UDP data port where data is received.
Definition: DTrackNet.cpp:260
~TCP()
Deinitialize TCP socket.
Definition: DTrackNet.cpp:435
int receive(void *buffer, int maxLen, int toutUs)
Receive TCP data.
Definition: DTrackNet.cpp:461
int receive(void *buffer, int maxLen, int toutUs)
Receive UDP data.
Definition: DTrackNet.cpp:278
unsigned int getRemoteIp()
Get IP address of sender of latest received data.
Definition: DTrackNet.cpp:269
Internal socket type.
Definition: DTrackNet.cpp:66
TCP(unsigned int ip, unsigned short port)
Initialize client TCP socket.
Definition: DTrackNet.cpp:394
bool isValid()
Returns if UDP socket is open to receive data.
Definition: DTrackNet.cpp:251
int send(const void *buffer, int len, int toutUs)
Send TCP data.
Definition: DTrackNet.cpp:505
bool isValid()
Returns if TCP connection is active.
Definition: DTrackNet.cpp:452
UDP(unsigned short port, unsigned int multicastIp=0)
Initialize UDP socket.
Definition: DTrackNet.cpp:139
~UDP()
Deinitialize UDP socket.
Definition: DTrackNet.cpp:222