Visual Servoing Platform  version 3.2.0 under development (2019-01-22)
vpUDPServer.cpp
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 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 # define _WIN32_WINNT _WIN32_WINNT_VISTA // 0x0600
54 # endif
55 # include <Ws2tcpip.h>
56 #endif
57 
58 #include <visp3/core/vpUDPServer.h>
59 
66 vpUDPServer::vpUDPServer(const int port)
67  : m_clientAddress(), m_clientLength(0), m_serverAddress(), m_socketFileDescriptor(0)
68 #if defined(_WIN32)
69  ,
70  m_wsa()
71 #endif
72 {
73  init("", port);
74 }
75 
82 vpUDPServer::vpUDPServer(const std::string &hostname, const int port)
83  : m_clientAddress(), m_clientLength(0), m_serverAddress(), m_socketFileDescriptor(0)
84 #if defined(_WIN32)
85  ,
86  m_wsa()
87 #endif
88 {
89  init(hostname, port);
90 }
91 
93 {
94 #if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX
95  close(m_socketFileDescriptor);
96 #else
97  closesocket(m_socketFileDescriptor);
98  WSACleanup();
99 #endif
100 }
101 
102 void vpUDPServer::init(const std::string &hostname, const int port)
103 {
104 #if defined(_WIN32)
105  if (WSAStartup(MAKEWORD(2, 2), &m_wsa) != 0) {
106  std::stringstream ss;
107  ss << "Failed WSAStartup for the server, error code: " << WSAGetLastError();
108  throw vpException(vpException::fatalError, ss.str());
109  }
110 #endif
111 
112  /* socket: create the socket */
113  m_socketFileDescriptor = socket(AF_INET, SOCK_DGRAM, 0);
114 #if defined(_WIN32)
115  if (m_socketFileDescriptor == INVALID_SOCKET)
116 #else
117  if (m_socketFileDescriptor < 0)
118 #endif
119  throw vpException(vpException::fatalError, "Error opening UDP socket for the server!");
120 
121 /* setsockopt: Handy debugging trick that lets
122  * us rerun the server immediately after we kill it;
123  * otherwise we have to wait about 20 secs.
124  * Eliminates "ERROR on binding: Address already in use" error.
125  */
126 #if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX
127  int optval = 1;
128  setsockopt(m_socketFileDescriptor, SOL_SOCKET, SO_REUSEADDR, (const void *)&optval, sizeof(int));
129 #else
130  const char optval = 1;
131  setsockopt(m_socketFileDescriptor, SOL_SOCKET, SO_REUSEADDR, (const char *)&optval, sizeof(int));
132 #endif
133 
134  /* build the server's Internet address */
135  memset(&m_serverAddress, 0, sizeof(m_serverAddress));
136  if (hostname.empty()) {
137  m_serverAddress.sin_family = AF_INET;
138  m_serverAddress.sin_addr.s_addr = htonl(INADDR_ANY);
139  m_serverAddress.sin_port = htons((unsigned short)port);
140  } else {
141  std::stringstream ss;
142  ss << port;
143  struct addrinfo hints;
144  struct addrinfo *result = NULL;
145  struct addrinfo *ptr = NULL;
146 
147  memset(&hints, 0, sizeof(hints));
148  hints.ai_family = AF_INET;
149  hints.ai_socktype = SOCK_DGRAM;
150  hints.ai_protocol = IPPROTO_UDP;
151 
152  DWORD dwRetval = getaddrinfo(hostname.c_str(), ss.str().c_str(), &hints, &result);
153  if (dwRetval != 0) {
154  ss.str("");
155  ss << "getaddrinfo failed with error: " << dwRetval;
156  throw vpException(vpException::fatalError, ss.str());
157  }
158 
159  for (ptr = result; ptr != NULL; ptr = ptr->ai_next) {
160  if (ptr->ai_family == AF_INET && ptr->ai_socktype == SOCK_DGRAM) {
161  m_serverAddress = *(struct sockaddr_in *)ptr->ai_addr;
162  break;
163  }
164  }
165 
166  freeaddrinfo(result);
167  }
168 
169  /* bind: associate the parent socket with a port */
170  if (bind(m_socketFileDescriptor, (struct sockaddr *)&m_serverAddress, sizeof(m_serverAddress)) < 0)
171  throw vpException(vpException::fatalError, "Error on binding on the server!");
172 
173  m_clientLength = sizeof(m_clientAddress);
174 }
175 
187 int vpUDPServer::receive(std::string &msg, const int timeoutMs)
188 {
189  std::string hostInfo = "";
190  return receive(msg, hostInfo, timeoutMs);
191 }
192 
205 int vpUDPServer::receive(std::string &msg, std::string &hostInfo, const int timeoutMs)
206 {
207  fd_set s;
208  FD_ZERO(&s);
209  FD_SET(m_socketFileDescriptor, &s);
210  struct timeval timeout;
211  if (timeoutMs > 0) {
212  timeout.tv_sec = timeoutMs / 1000;
213  timeout.tv_usec = (timeoutMs % 1000) * 1000;
214  }
215  int retval = select((int)m_socketFileDescriptor + 1, &s, NULL, NULL, timeoutMs > 0 ? &timeout : NULL);
216 
217  if (retval == -1) {
218  std::cerr << "Error select!" << std::endl;
219  return -1;
220  }
221 
222  if (retval > 0) {
223 /* recvfrom: receive a UDP datagram from a client */
224 #if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX
225  int length = static_cast<int>(recvfrom(m_socketFileDescriptor, m_buf, sizeof(m_buf), 0, (struct sockaddr *)&m_clientAddress,
226  (socklen_t *)&m_clientLength));
227 #else
228  int length =
229  recvfrom(m_socketFileDescriptor, m_buf, sizeof(m_buf), 0, (struct sockaddr *)&m_clientAddress, &m_clientLength);
230 #endif
231  if (length <= 0) {
232  return length < 0 ? -1 : 0;
233  }
234 
235  msg = std::string(m_buf, length);
236 
237  /* getnameinfo: determine who sent the datagram */
238  char hostname[NI_MAXHOST];
239  char servInfo[NI_MAXSERV];
240  DWORD dwRetval = getnameinfo((struct sockaddr *)&m_clientAddress, sizeof(struct sockaddr), hostname, NI_MAXHOST,
241  servInfo, NI_MAXSERV, NI_NUMERICSERV);
242 
243  std::string hostName = "", hostIp = "", hostPort = "";
244  if (dwRetval != 0) {
245  std::cerr << "getnameinfo failed with error: " << WSAGetLastError() << std::endl;
246  } else {
247  hostName = hostname;
248  hostPort = servInfo;
249  }
250 
251  char result[INET_ADDRSTRLEN];
252  const char *ptr = inet_ntop(AF_INET, (void *)&m_clientAddress.sin_addr, result, sizeof(result));
253  if (ptr == NULL) {
254  std::cerr << "inet_ntop failed with error: " << WSAGetLastError() << std::endl;
255  } else {
256  hostIp = result;
257  }
258 
259  std::stringstream ss;
260  ss << hostName << " " << hostIp << " " << hostPort;
261  hostInfo = ss.str();
262 
263  return length;
264  }
265 
266  // Timeout
267  return 0;
268 }
269 
280 int vpUDPServer::send(const std::string &msg, const std::string &hostname, const int port)
281 {
282  if (msg.size() > VP_MAX_UDP_PAYLOAD) {
283  std::cerr << "Message is too long!" << std::endl;
284  return 0;
285  }
286 
287  // Create client address
288  memset(&m_clientAddress, 0, sizeof(m_clientAddress));
289  std::stringstream ss;
290  ss << port;
291  struct addrinfo hints;
292  struct addrinfo *result = NULL;
293  struct addrinfo *ptr = NULL;
294 
295  memset(&hints, 0, sizeof(hints));
296  hints.ai_family = AF_INET;
297  hints.ai_socktype = SOCK_DGRAM;
298  hints.ai_protocol = IPPROTO_UDP;
299 
300  DWORD dwRetval = getaddrinfo(hostname.c_str(), ss.str().c_str(), &hints, &result);
301  if (dwRetval != 0) {
302  ss.str("");
303  ss << "getaddrinfo failed with error: " << dwRetval;
304  throw vpException(vpException::fatalError, ss.str());
305  }
306 
307  for (ptr = result; ptr != NULL; ptr = ptr->ai_next) {
308  if (ptr->ai_family == AF_INET && ptr->ai_socktype == SOCK_DGRAM) {
309  m_clientAddress = *(struct sockaddr_in *)ptr->ai_addr;
310  break;
311  }
312  }
313 
314  freeaddrinfo(result);
315 
316 /* send the message to the client */
317 #if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX
318  return static_cast<int>(sendto(m_socketFileDescriptor, msg.c_str(), msg.size(), 0, (struct sockaddr *)&m_clientAddress,
319  m_clientLength));
320 #else
321  return sendto(m_socketFileDescriptor, msg.c_str(), (int)msg.size(), 0, (struct sockaddr *)&m_clientAddress,
322  m_clientLength);
323 #endif
324 }
325 
326 #elif !defined(VISP_BUILD_SHARED_LIBS)
327 // Work arround to avoid warning: libvisp_core.a(vpUDPServer.cpp.o) has no symbols
328 void dummy_vpUDPServer(){};
329 #endif
virtual ~vpUDPServer()
Definition: vpUDPServer.cpp:92
error that can be emited by ViSP classes.
Definition: vpException.h:71
vpUDPServer(const int port)
Definition: vpUDPServer.cpp:66
int send(const std::string &msg, const std::string &hostname, const int port)
int receive(std::string &msg, const int timeoutMs=0)