Visual Servoing Platform  version 3.6.1 under development (2024-03-04)
vpUDPServer.cpp
1 /****************************************************************************
2  *
3  * ViSP, open source Visual Servoing Platform software.
4  * Copyright (C) 2005 - 2023 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 https://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 Server
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 #define WSAGetLastError() strerror(errno)
51 #else
52 #if defined(__MINGW32__)
53 #ifdef _WIN32_WINNT
54 // Undef _WIN32_WINNT to avoid a warning (_WIN32_WINNT redefinition) with mingw
55 #undef _WIN32_WINNT
56 #define _WIN32_WINNT _WIN32_WINNT_VISTA // 0x0600
57 // Without re-defining _WIN32_WINNT = _WIN32_WINNT_VISTA there is a build issue with mingw:
58 // vpUDPServer.cpp:254:23: error: 'inet_ntop' was not declared in this scope
59 // const char *ptr = inet_ntop(AF_INET, (void *)&m_clientAddress.sin_addr, result, sizeof(result));
60 // vpUDPServer.cpp:254:23: note: suggested alternative: 'inet_ntoa'
61 #endif
62 #endif
63 #include <Ws2tcpip.h>
64 #endif
65 
66 #include <visp3/core/vpUDPServer.h>
67 
75  : m_clientAddress(), m_clientLength(0), m_serverAddress(), m_socketFileDescriptor(0)
76 #if defined(_WIN32)
77  ,
78  m_wsa()
79 #endif
80 {
81  init("", port);
82 }
83 
90 vpUDPServer::vpUDPServer(const std::string &hostname, int port)
91  : m_clientAddress(), m_clientLength(0), m_serverAddress(), m_socketFileDescriptor(0)
92 #if defined(_WIN32)
93  ,
94  m_wsa()
95 #endif
96 {
97  init(hostname, port);
98 }
99 
101 {
102 #if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX
103  close(m_socketFileDescriptor);
104 #else
105  closesocket(m_socketFileDescriptor);
106  WSACleanup();
107 #endif
108 }
109 
110 void vpUDPServer::init(const std::string &hostname, int port)
111 {
112 #if defined(_WIN32)
113  if (WSAStartup(MAKEWORD(2, 2), &m_wsa) != 0) {
114  std::stringstream ss;
115  ss << "Failed WSAStartup for the server, error code: " << WSAGetLastError();
116  throw vpException(vpException::fatalError, ss.str());
117  }
118 #endif
119 
120  /* socket: create the socket */
121  m_socketFileDescriptor = socket(AF_INET, SOCK_DGRAM, 0);
122 #if defined(_WIN32)
123  if (m_socketFileDescriptor == INVALID_SOCKET)
124 #else
125  if (m_socketFileDescriptor < 0)
126 #endif
127  throw vpException(vpException::fatalError, "Error opening UDP socket for the server!");
128 
129 /* setsockopt: Handy debugging trick that lets
130  * us rerun the server immediately after we kill it;
131  * otherwise we have to wait about 20 secs.
132  * Eliminates "ERROR on binding: Address already in use" error.
133  */
134 #if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX
135  int optval = 1;
136  setsockopt(m_socketFileDescriptor, SOL_SOCKET, SO_REUSEADDR, (const void *)&optval, sizeof(int));
137 #else
138  const char optval = 1;
139  setsockopt(m_socketFileDescriptor, SOL_SOCKET, SO_REUSEADDR, (const char *)&optval, sizeof(int));
140 #endif
141 
142  /* build the server's Internet address */
143  memset(&m_serverAddress, 0, sizeof(m_serverAddress));
144  if (hostname.empty()) {
145  m_serverAddress.sin_family = AF_INET;
146  m_serverAddress.sin_addr.s_addr = htonl(INADDR_ANY);
147  m_serverAddress.sin_port = htons((unsigned short)port);
148  } else {
149  std::stringstream ss;
150  ss << port;
151  struct addrinfo hints;
152  struct addrinfo *result = nullptr;
153  struct addrinfo *ptr = nullptr;
154 
155  memset(&hints, 0, sizeof(hints));
156  hints.ai_family = AF_INET;
157  hints.ai_socktype = SOCK_DGRAM;
158  hints.ai_protocol = IPPROTO_UDP;
159 
160  DWORD dwRetval = getaddrinfo(hostname.c_str(), ss.str().c_str(), &hints, &result);
161  if (dwRetval != 0) {
162  ss.str("");
163  ss << "getaddrinfo failed with error: " << dwRetval;
164  throw vpException(vpException::fatalError, ss.str());
165  }
166 
167  for (ptr = result; ptr != nullptr; ptr = ptr->ai_next) {
168  if (ptr->ai_family == AF_INET && ptr->ai_socktype == SOCK_DGRAM) {
169  m_serverAddress = *(struct sockaddr_in *)ptr->ai_addr;
170  break;
171  }
172  }
173 
174  freeaddrinfo(result);
175  }
176 
177  /* bind: associate the parent socket with a port */
178  if (bind(m_socketFileDescriptor, (struct sockaddr *)&m_serverAddress, sizeof(m_serverAddress)) < 0)
179  throw vpException(vpException::fatalError, "Error on binding on the server!");
180 
181  m_clientLength = sizeof(m_clientAddress);
182 }
183 
195 int vpUDPServer::receive(std::string &msg, int timeoutMs)
196 {
197  std::string hostInfo = "";
198  return receive(msg, hostInfo, timeoutMs);
199 }
200 
213 int vpUDPServer::receive(std::string &msg, std::string &hostInfo, int timeoutMs)
214 {
215  fd_set s;
216  FD_ZERO(&s);
217  FD_SET(m_socketFileDescriptor, &s);
218  struct timeval timeout;
219  if (timeoutMs > 0) {
220  timeout.tv_sec = timeoutMs / 1000;
221  timeout.tv_usec = (timeoutMs % 1000) * 1000;
222  }
223  int retval = select((int)m_socketFileDescriptor + 1, &s, nullptr, nullptr, timeoutMs > 0 ? &timeout : nullptr);
224 
225  if (retval == -1) {
226  std::cerr << "Error select!" << std::endl;
227  return -1;
228  }
229 
230  if (retval > 0) {
231 /* recvfrom: receive a UDP datagram from a client */
232 #if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX
233  int length = static_cast<int>(recvfrom(m_socketFileDescriptor, m_buf, sizeof(m_buf), 0,
234  (struct sockaddr *)&m_clientAddress, (socklen_t *)&m_clientLength));
235 #else
236  int length =
237  recvfrom(m_socketFileDescriptor, m_buf, sizeof(m_buf), 0, (struct sockaddr *)&m_clientAddress, &m_clientLength);
238 #endif
239  if (length <= 0) {
240  return length < 0 ? -1 : 0;
241  }
242 
243  msg = std::string(m_buf, length);
244 
245  /* getnameinfo: determine who sent the datagram */
246  char hostname[NI_MAXHOST];
247  char servInfo[NI_MAXSERV];
248  DWORD dwRetval = getnameinfo((struct sockaddr *)&m_clientAddress, sizeof(struct sockaddr), hostname, NI_MAXHOST,
249  servInfo, NI_MAXSERV, NI_NUMERICSERV);
250 
251  std::string hostName = "", hostIp = "", hostPort = "";
252  if (dwRetval != 0) {
253  std::cerr << "getnameinfo failed with error: " << WSAGetLastError() << std::endl;
254  } else {
255  hostName = hostname;
256  hostPort = servInfo;
257  }
258 
259  char result[INET_ADDRSTRLEN];
260  const char *ptr = inet_ntop(AF_INET, (void *)&m_clientAddress.sin_addr, result, sizeof(result));
261  if (ptr == nullptr) {
262  std::cerr << "inet_ntop failed with error: " << WSAGetLastError() << std::endl;
263  } else {
264  hostIp = result;
265  }
266 
267  std::stringstream ss;
268  ss << hostName << " " << hostIp << " " << hostPort;
269  hostInfo = ss.str();
270 
271  return length;
272  }
273 
274  // Timeout
275  return 0;
276 }
277 
288 int vpUDPServer::send(const std::string &msg, const std::string &hostname, int port)
289 {
290  if (msg.size() > VP_MAX_UDP_PAYLOAD) {
291  std::cerr << "Message is too long!" << std::endl;
292  return 0;
293  }
294 
295  // Create client address
296  memset(&m_clientAddress, 0, sizeof(m_clientAddress));
297  std::stringstream ss;
298  ss << port;
299  struct addrinfo hints;
300  struct addrinfo *result = nullptr;
301  struct addrinfo *ptr = nullptr;
302 
303  memset(&hints, 0, sizeof(hints));
304  hints.ai_family = AF_INET;
305  hints.ai_socktype = SOCK_DGRAM;
306  hints.ai_protocol = IPPROTO_UDP;
307 
308  DWORD dwRetval = getaddrinfo(hostname.c_str(), ss.str().c_str(), &hints, &result);
309  if (dwRetval != 0) {
310  ss.str("");
311  ss << "getaddrinfo failed with error: " << dwRetval;
312  throw vpException(vpException::fatalError, ss.str());
313  }
314 
315  for (ptr = result; ptr != nullptr; ptr = ptr->ai_next) {
316  if (ptr->ai_family == AF_INET && ptr->ai_socktype == SOCK_DGRAM) {
317  m_clientAddress = *(struct sockaddr_in *)ptr->ai_addr;
318  break;
319  }
320  }
321 
322  freeaddrinfo(result);
323 
324 /* send the message to the client */
325 #if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX
326  return static_cast<int>(
327  sendto(m_socketFileDescriptor, msg.c_str(), msg.size(), 0, (struct sockaddr *)&m_clientAddress, m_clientLength));
328 #else
329  return sendto(m_socketFileDescriptor, msg.c_str(), (int)msg.size(), 0, (struct sockaddr *)&m_clientAddress,
330  m_clientLength);
331 #endif
332 }
333 
334 #elif !defined(VISP_BUILD_SHARED_LIBS)
335 // Work around to avoid warning: libvisp_core.a(vpUDPServer.cpp.o) has no symbols
336 void dummy_vpUDPServer(){};
337 #endif
error that can be emitted by ViSP classes.
Definition: vpException.h:59
@ fatalError
Fatal error.
Definition: vpException.h:84
virtual ~vpUDPServer()
int send(const std::string &msg, const std::string &hostname, int port)
int receive(std::string &msg, int timeoutMs=0)
vpUDPServer(int port)
Definition: vpUDPServer.cpp:74