Visual Servoing Platform version 3.5.0
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# 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
90vpUDPServer::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
110void 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 = NULL;
153 struct addrinfo *ptr = NULL;
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 != NULL; 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
195int vpUDPServer::receive(std::string &msg, int timeoutMs)
196{
197 std::string hostInfo = "";
198 return receive(msg, hostInfo, timeoutMs);
199}
200
213int 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, NULL, NULL, timeoutMs > 0 ? &timeout : NULL);
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, (struct sockaddr *)&m_clientAddress,
234 (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 == NULL) {
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
288int 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 = NULL;
301 struct addrinfo *ptr = NULL;
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 != NULL; 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>(sendto(m_socketFileDescriptor, msg.c_str(), msg.size(), 0, (struct sockaddr *)&m_clientAddress,
327 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 arround to avoid warning: libvisp_core.a(vpUDPServer.cpp.o) has no symbols
336void dummy_vpUDPServer(){};
337#endif
error that can be emited by ViSP classes.
Definition: vpException.h:72
@ fatalError
Fatal error.
Definition: vpException.h:96
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