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