1 /****************************************************************************
2  *
3  * ViSP, open source Visual Servoing Platform software.
4  * Copyright (C) 2005 - 2019 by Inria. All rights reserved.
5  *
6  * This software is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  * See the file LICENSE.txt at the root directory of this source
11  * distribution for additional information about the GNU GPL.
12  *
13  * For using ViSP with software that can not be combined with the GNU
14  * GPL, please contact Inria about acquiring a ViSP Professional
15  * Edition License.
16  *
17  * See http://visp.inria.fr for more information.
18  *
19  * This software was developed at:
20  * Inria Rennes - Bretagne Atlantique
21  * Campus Universitaire de Beaulieu
22  * 35042 Rennes Cedex
23  * France
24  *
25  * If you have questions regarding the use of this file, please contact
26  * Inria at visp@inria.fr
27  *
28  * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
29  * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
30  *
31  * Description:
32  * UDP Client
33  *
34  *****************************************************************************/
35 
36 #include <cstring>
37 #include <sstream>
38 
39 #include <visp3/core/vpConfig.h>
40 
41 // inet_ntop() not supported on win XP
42 #ifdef VISP_HAVE_FUNC_INET_NTOP
43 
44 #if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX
45 #include <arpa/inet.h>
46 #include <errno.h>
47 #include <netdb.h>
48 #include <unistd.h>
49 #define DWORD int
50 #else
51 #if defined(__MINGW32__)
52 #  ifndef _WIN32_WINNT
53 #    define _WIN32_WINNT _WIN32_WINNT_VISTA // 0x0600
54 #  endif
55 #endif
56 #include <Ws2tcpip.h>
57 #endif
58 
59 #include <visp3/core/vpUDPClient.h>
60 
61 /*!
62   Default constructor.
63 
64   Use connect() to establish the connexion with the server.
65 */
vpUDPClient()66 vpUDPClient::vpUDPClient()
67   : m_is_init(false), m_serverAddress(), m_serverLength(0), m_socketFileDescriptor()
68 #if defined(_WIN32)
69     , m_wsa()
70 #endif
71 {
72 }
73 
74 /*!
75   Create a (IPv4) UDP client.
76 
77   \param hostname : Server hostname or IP address.
78   \param port : Server port number.
79 */
vpUDPClient(const std::string & hostname,int port)80 vpUDPClient::vpUDPClient(const std::string &hostname, int port)
81   : m_is_init(false), m_serverAddress(), m_serverLength(0), m_socketFileDescriptor()
82 #if defined(_WIN32)
83     , m_wsa()
84 #endif
85 {
86   init(hostname, port);
87 }
88 
89 /*!
90    Destructor.
91  */
~vpUDPClient()92 vpUDPClient::~vpUDPClient() { close(); }
93 
94 /*!
95    Close UDP connexion.
96  */
close()97 void vpUDPClient::close()
98 {
99   if (m_is_init) {
100 #if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX
101     ::close(m_socketFileDescriptor);
102 #else
103     closesocket(m_socketFileDescriptor);
104     WSACleanup();
105 #endif
106     m_is_init = false;
107   }
108 }
109 
110 /*!
111   Initialize a (IPv4) UDP client.
112 
113   \param hostname : Server hostname or IP address.
114   \param port : Server port number.
115 */
init(const std::string & hostname,int port)116 void vpUDPClient::init(const std::string &hostname, int port)
117 {
118   // Close connexion if already initialized
119   close();
120 
121 #if defined(_WIN32)
122   if (WSAStartup(MAKEWORD(2, 2), &m_wsa) != 0) {
123     std::stringstream ss;
124     ss << "Failed WSAStartup for the server, error code: " << WSAGetLastError();
125     throw vpException(vpException::fatalError, ss.str());
126   }
127 #endif
128 
129   /* socket: create the socket */
130   m_socketFileDescriptor = socket(AF_INET, SOCK_DGRAM, 0);
131 #if defined(_WIN32)
132   if (m_socketFileDescriptor == INVALID_SOCKET)
133 #else
134   if (m_socketFileDescriptor < 0)
135 #endif
136     throw vpException(vpException::fatalError, "Error opening UDP socket for the client!");
137 
138   /* build the server's Internet address */
139   memset(&m_serverAddress, 0, sizeof(m_serverAddress));
140   std::stringstream ss;
141   ss << port;
142   struct addrinfo hints;
143   struct addrinfo *result = NULL;
144   struct addrinfo *ptr = NULL;
145 
146   memset(&hints, 0, sizeof(hints));
147   hints.ai_family = AF_INET;
148   hints.ai_socktype = SOCK_DGRAM;
149   hints.ai_protocol = IPPROTO_UDP;
150 
151   DWORD dwRetval = getaddrinfo(hostname.c_str(), ss.str().c_str(), &hints, &result);
152   if (dwRetval != 0) {
153     ss.str("");
154     ss << "getaddrinfo failed with error: " << dwRetval;
155     throw vpException(vpException::fatalError, ss.str());
156   }
157 
158   for (ptr = result; ptr != NULL; ptr = ptr->ai_next) {
159     if (ptr->ai_family == AF_INET && ptr->ai_socktype == SOCK_DGRAM) {
160       m_serverAddress = *(struct sockaddr_in *)ptr->ai_addr;
161       break;
162     }
163   }
164 
165   freeaddrinfo(result);
166 
167   m_serverLength = sizeof(m_serverAddress);
168   m_is_init = true;
169 }
170 
171 /*!
172   Receive data sent by the server.
173 
174   \param msg : ASCII message or byte data.
175   \param timeoutMs : Timeout in millisecond (if zero, the call is blocking).
176 
177   \return The message length / size of the byte array sent received, or -1 if
178   there is an error, or 0 if there is a timeout.
179 
180   \note
181   To transform the ASCII representation of an integer:
182   \code
183   int val = atoi(msg.c_str());
184   //or
185   std::istringstream ss(msg);
186   ss >> val;
187   \endcode
188   To convert from a byte array to an integer:
189   \code
190   int val = *reinterpret_cast<const int *>(msg.c_str());
191   \endcode
192 */
receive(std::string & msg,int timeoutMs)193 int vpUDPClient::receive(std::string &msg, int timeoutMs)
194 {
195   if (!m_is_init) {
196     throw(vpException(vpException::notInitialized, "UDP client is not initialized"));
197   }
198   fd_set s;
199   FD_ZERO(&s);
200   FD_SET(m_socketFileDescriptor, &s);
201   struct timeval timeout;
202   if (timeoutMs > 0) {
203     timeout.tv_sec = timeoutMs / 1000;
204     timeout.tv_usec = (timeoutMs % 1000) * 1000;
205   }
206   int retval = select((int)m_socketFileDescriptor + 1, &s, NULL, NULL, timeoutMs > 0 ? &timeout : NULL);
207 
208   if (retval == -1) {
209     std::cerr << "Error select!" << std::endl;
210     return -1;
211   }
212 
213   if (retval > 0) {
214     /* recvfrom: receive a UDP datagram from the server */
215     int length = static_cast<int>(recvfrom(m_socketFileDescriptor, m_buf, sizeof(m_buf), 0, (struct sockaddr *)&m_serverAddress,
216                                            (socklen_t *)&m_serverLength));
217     if (length <= 0) {
218       return length < 0 ? -1 : 0;
219     }
220 
221     msg = std::string(m_buf, length);
222     return length;
223   }
224 
225   // Timeout
226   return 0;
227 }
228 
229 /*!
230   Receive data sent by the server.
231 
232   \param msg : A message to send over the network.
233   \param len : Message length.
234   \param timeoutMs : Timeout in millisecond (if zero, the call is blocking).
235 
236   \return The message length / size of the byte array sent received, or -1 if
237   there is an error, or 0 if there is a timeout.
238 */
receive(void * msg,size_t len,int timeoutMs)239 int vpUDPClient::receive(void *msg, size_t len, int timeoutMs)
240 {
241   if (!m_is_init) {
242     throw(vpException(vpException::notInitialized, "UDP client is not initialized"));
243   }
244   fd_set s;
245   FD_ZERO(&s);
246   FD_SET(m_socketFileDescriptor, &s);
247   struct timeval timeout;
248   if (timeoutMs > 0) {
249     timeout.tv_sec = timeoutMs / 1000;
250     timeout.tv_usec = (timeoutMs % 1000) * 1000;
251   }
252   int retval = select((int)m_socketFileDescriptor + 1, &s, NULL, NULL, timeoutMs > 0 ? &timeout : NULL);
253 
254   if (retval == -1) {
255     std::cerr << "Error select!" << std::endl;
256     return -1;
257   }
258 
259   if (retval > 0) {
260     /* recvfrom: receive a UDP datagram from the server */
261     int length = static_cast<int>(recvfrom(m_socketFileDescriptor, (char *)msg, (int)len, 0, (struct sockaddr *)&m_serverAddress,
262                                            (socklen_t *)&m_serverLength));
263     if (length <= 0) {
264       return length < 0 ? -1 : 0;
265     }
266 
267     return length;
268   }
269 
270   // Timeout
271   return 0;
272 }
273 
274 
275 /*!
276   Send data to the server.
277 
278   \param msg : ASCII message or byte data.
279 
280   \return The message length / size of the byte array sent.
281 
282   \note
283   To send the ASCII representation of an integer:
284   \code
285   int val = 1024;
286   std::ostringstream os;
287   os << val;
288   server.send(os.str(), hostname, port);
289   \endcode
290   To send directly the byte data (assuming the same integer representation on
291   the server and the client):
292   \code
293   int val = 1024;
294   char data[sizeof(val)];
295   memcpy(data, &val, sizeof(val));
296   std::string msg(data, sizeof(val)); //required to avoid the string being splitted with the first \0 character
297   server.send(msg, hostname, port);
298   \endcode
299 */
send(const std::string & msg)300 int vpUDPClient::send(const std::string &msg)
301 {
302   if (!m_is_init) {
303     throw(vpException(vpException::notInitialized, "UDP client is not initialized"));
304   }
305   if (msg.size() > VP_MAX_UDP_PAYLOAD) {
306     std::cerr << "Message is too long!" << std::endl;
307     return 0;
308   }
309 
310 /* send the message to the server */
311 #if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX
312   return static_cast<int>(sendto(m_socketFileDescriptor, msg.c_str(), msg.size(), 0, (struct sockaddr *)&m_serverAddress,
313                                  m_serverLength));
314 #else
315   return sendto(m_socketFileDescriptor, msg.c_str(), (int)msg.size(), 0, (struct sockaddr *)&m_serverAddress,
316                 m_serverLength);
317 #endif
318 }
319 
320 /*!
321   Send data to the server.
322 
323   \param msg : Message to send.
324   \param len : Message length.
325 
326   \return The message length / size of the byte array sent.
327 
328 */
send(const void * msg,size_t len)329 int vpUDPClient::send(const void *msg, size_t len)
330 {
331   if (!m_is_init) {
332     throw(vpException(vpException::notInitialized, "UDP client is not initialized"));
333   }
334   if (len > VP_MAX_UDP_PAYLOAD) {
335     std::cerr << "Message is too long!" << std::endl;
336     return 0;
337   }
338 
339 /* send the message to the server */
340 #if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX
341   return static_cast<int>(sendto(m_socketFileDescriptor, msg, len, 0, (struct sockaddr *)&m_serverAddress,
342                                  m_serverLength));
343 #else
344   return sendto(m_socketFileDescriptor, (char *)msg, (int)len, 0, (struct sockaddr *)&m_serverAddress,
345                 m_serverLength);
346 #endif
347 }
348 
349 #elif !defined(VISP_BUILD_SHARED_LIBS)
350 // Work arround to avoid warning: libvisp_core.a(vpUDPClient.cpp.o) has no symbols
dummy_vpUDPClient()351 void dummy_vpUDPClient(){};
352 #endif
353