Visual Servoing Platform  version 3.1.0
vpUDPServer.cpp
1 /****************************************************************************
2  *
3  * This file is part of the ViSP software.
4  * Copyright (C) 2005 - 2017 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 #if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX
40 #include <arpa/inet.h>
41 #include <errno.h>
42 #include <netdb.h>
43 #include <unistd.h>
44 #define DWORD int
45 #define WSAGetLastError() strerror(errno)
46 #else
47 #if defined(__MINGW32__)
48 #define _WIN32_WINNT _WIN32_WINNT_VISTA // 0x0600
49 #endif
50 #include <Ws2tcpip.h>
51 #endif
52 
53 #include <visp3/core/vpUDPServer.h>
54 
61 vpUDPServer::vpUDPServer(const int port)
62  : m_clientAddress(), m_clientLength(0), m_serverAddress(), m_socketFileDescriptor(0)
63 #if defined(_WIN32)
64  ,
65  m_wsa()
66 #endif
67 {
68  init("", port);
69 }
70 
77 vpUDPServer::vpUDPServer(const std::string &hostname, const int port)
78  : m_clientAddress(), m_clientLength(0), m_serverAddress(), m_socketFileDescriptor(0)
79 #if defined(_WIN32)
80  ,
81  m_wsa()
82 #endif
83 {
84  init(hostname, port);
85 }
86 
88 {
89 #if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX
90  close(m_socketFileDescriptor);
91 #else
92  closesocket(m_socketFileDescriptor);
93  WSACleanup();
94 #endif
95 }
96 
97 void vpUDPServer::init(const std::string &hostname, const int port)
98 {
99 #if defined(_WIN32)
100  if (WSAStartup(MAKEWORD(2, 2), &m_wsa) != 0) {
101  std::stringstream ss;
102  ss << "Failed WSAStartup for the server, error code: " << WSAGetLastError();
103  throw vpException(vpException::fatalError, ss.str());
104  }
105 #endif
106 
107  /* socket: create the socket */
108  m_socketFileDescriptor = socket(AF_INET, SOCK_DGRAM, 0);
109 #if defined(_WIN32)
110  if (m_socketFileDescriptor == INVALID_SOCKET)
111 #else
112  if (m_socketFileDescriptor < 0)
113 #endif
114  throw vpException(vpException::fatalError, "Error opening UDP socket for the server!");
115 
116 /* setsockopt: Handy debugging trick that lets
117  * us rerun the server immediately after we kill it;
118  * otherwise we have to wait about 20 secs.
119  * Eliminates "ERROR on binding: Address already in use" error.
120  */
121 #if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX
122  int optval = 1;
123  setsockopt(m_socketFileDescriptor, SOL_SOCKET, SO_REUSEADDR, (const void *)&optval, sizeof(int));
124 #else
125  const char optval = 1;
126  setsockopt(m_socketFileDescriptor, SOL_SOCKET, SO_REUSEADDR, (const char *)&optval, sizeof(int));
127 #endif
128 
129  /* build the server's Internet address */
130  memset(&m_serverAddress, 0, sizeof(m_serverAddress));
131  if (hostname.empty()) {
132  m_serverAddress.sin_family = AF_INET;
133  m_serverAddress.sin_addr.s_addr = htonl(INADDR_ANY);
134  m_serverAddress.sin_port = htons((unsigned short)port);
135  } else {
136  std::stringstream ss;
137  ss << port;
138  struct addrinfo hints;
139  struct addrinfo *result = NULL;
140  struct addrinfo *ptr = NULL;
141 
142  memset(&hints, 0, sizeof(hints));
143  hints.ai_family = AF_INET;
144  hints.ai_socktype = SOCK_DGRAM;
145  hints.ai_protocol = IPPROTO_UDP;
146 
147  DWORD dwRetval = getaddrinfo(hostname.c_str(), ss.str().c_str(), &hints, &result);
148  if (dwRetval != 0) {
149  ss.str("");
150  ss << "getaddrinfo failed with error: " << dwRetval;
151  throw vpException(vpException::fatalError, ss.str());
152  }
153 
154  for (ptr = result; ptr != NULL; ptr = ptr->ai_next) {
155  if (ptr->ai_family == AF_INET && ptr->ai_socktype == SOCK_DGRAM) {
156  m_serverAddress = *(struct sockaddr_in *)ptr->ai_addr;
157  break;
158  }
159  }
160 
161  freeaddrinfo(result);
162  }
163 
164  /* bind: associate the parent socket with a port */
165  if (bind(m_socketFileDescriptor, (struct sockaddr *)&m_serverAddress, sizeof(m_serverAddress)) < 0)
166  throw vpException(vpException::fatalError, "Error on binding on the server!");
167 
168  m_clientLength = sizeof(m_clientAddress);
169 }
170 
182 int vpUDPServer::receive(std::string &msg, const int timeoutMs)
183 {
184  std::string hostInfo = "";
185  return receive(msg, hostInfo, timeoutMs);
186 }
187 
200 int vpUDPServer::receive(std::string &msg, std::string &hostInfo, const int timeoutMs)
201 {
202  fd_set s;
203  FD_ZERO(&s);
204  FD_SET(m_socketFileDescriptor, &s);
205  struct timeval timeout;
206  if (timeoutMs > 0) {
207  timeout.tv_sec = timeoutMs / 1000;
208  timeout.tv_usec = (timeoutMs % 1000) * 1000;
209  }
210  int retval = select((int)m_socketFileDescriptor + 1, &s, NULL, NULL, timeoutMs > 0 ? &timeout : NULL);
211 
212  if (retval == -1) {
213  std::cerr << "Error select!" << std::endl;
214  return -1;
215  }
216 
217  if (retval > 0) {
218 /* recvfrom: receive a UDP datagram from a client */
219 #if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX
220  int length = recvfrom(m_socketFileDescriptor, m_buf, sizeof(m_buf), 0, (struct sockaddr *)&m_clientAddress,
221  (socklen_t *)&m_clientLength);
222 #else
223  int length =
224  recvfrom(m_socketFileDescriptor, m_buf, sizeof(m_buf), 0, (struct sockaddr *)&m_clientAddress, &m_clientLength);
225 #endif
226  if (length <= 0) {
227  return length < 0 ? -1 : 0;
228  }
229 
230  msg = std::string(m_buf, length);
231 
232  /* getnameinfo: determine who sent the datagram */
233  char hostname[NI_MAXHOST];
234  char servInfo[NI_MAXSERV];
235  DWORD dwRetval = getnameinfo((struct sockaddr *)&m_clientAddress, sizeof(struct sockaddr), hostname, NI_MAXHOST,
236  servInfo, NI_MAXSERV, NI_NUMERICSERV);
237 
238  std::string hostName = "", hostIp = "", hostPort = "";
239  if (dwRetval != 0) {
240  std::cerr << "getnameinfo failed with error: " << WSAGetLastError() << std::endl;
241  } else {
242  hostName = hostname;
243  hostPort = servInfo;
244  }
245 
246  char result[INET_ADDRSTRLEN];
247  const char *ptr = inet_ntop(AF_INET, (void *)&m_clientAddress.sin_addr, result, sizeof(result));
248  if (ptr == NULL) {
249  std::cerr << "inet_ntop failed with error: " << WSAGetLastError() << std::endl;
250  } else {
251  hostIp = result;
252  }
253 
254  std::stringstream ss;
255  ss << hostName << " " << hostIp << " " << hostPort;
256  hostInfo = ss.str();
257 
258  return length;
259  }
260 
261  // Timeout
262  return 0;
263 }
264 
275 int vpUDPServer::send(const std::string &msg, const std::string &hostname, const int port)
276 {
277  if (msg.size() > VP_MAX_UDP_PAYLOAD) {
278  std::cerr << "Message is too long!" << std::endl;
279  return 0;
280  }
281 
282  // Create client address
283  memset(&m_clientAddress, 0, sizeof(m_clientAddress));
284  std::stringstream ss;
285  ss << port;
286  struct addrinfo hints;
287  struct addrinfo *result = NULL;
288  struct addrinfo *ptr = NULL;
289 
290  memset(&hints, 0, sizeof(hints));
291  hints.ai_family = AF_INET;
292  hints.ai_socktype = SOCK_DGRAM;
293  hints.ai_protocol = IPPROTO_UDP;
294 
295  DWORD dwRetval = getaddrinfo(hostname.c_str(), ss.str().c_str(), &hints, &result);
296  if (dwRetval != 0) {
297  ss.str("");
298  ss << "getaddrinfo failed with error: " << dwRetval;
299  throw vpException(vpException::fatalError, ss.str());
300  }
301 
302  for (ptr = result; ptr != NULL; ptr = ptr->ai_next) {
303  if (ptr->ai_family == AF_INET && ptr->ai_socktype == SOCK_DGRAM) {
304  m_clientAddress = *(struct sockaddr_in *)ptr->ai_addr;
305  break;
306  }
307  }
308 
309  freeaddrinfo(result);
310 
311 /* send the message to the client */
312 #if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX
313  return sendto(m_socketFileDescriptor, msg.c_str(), msg.size(), 0, (struct sockaddr *)&m_clientAddress,
314  m_clientLength);
315 #else
316  return sendto(m_socketFileDescriptor, msg.c_str(), (int)msg.size(), 0, (struct sockaddr *)&m_clientAddress,
317  m_clientLength);
318 #endif
319 }
error that can be emited by ViSP classes.
Definition: vpException.h:71
vpUDPServer(const int port)
Definition: vpUDPServer.cpp:61
int send(const std::string &msg, const std::string &hostname, const int port)
int receive(std::string &msg, const int timeoutMs=0)