Visual Servoing Platform version 3.7.0
Loading...
Searching...
No Matches
vpUDPServer.cpp
1/*
2 * ViSP, open source Visual Servoing Platform software.
3 * Copyright (C) 2005 - 2025 by Inria. All rights reserved.
4 *
5 * This software is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 * See the file LICENSE.txt at the root directory of this source
10 * distribution for additional information about the GNU GPL.
11 *
12 * For using ViSP with software that can not be combined with the GNU
13 * GPL, please contact Inria about acquiring a ViSP Professional
14 * Edition License.
15 *
16 * See https://visp.inria.fr for more information.
17 *
18 * This software was developed at:
19 * Inria Rennes - Bretagne Atlantique
20 * Campus Universitaire de Beaulieu
21 * 35042 Rennes Cedex
22 * France
23 *
24 * If you have questions regarding the use of this file, please contact
25 * Inria at visp@inria.fr
26 *
27 * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
28 * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
29 *
30 * Description:
31 * UDP Server
32 */
33
34#include <cstring>
35#include <sstream>
36
37#include <visp3/core/vpConfig.h>
38
39// inet_ntop() not supported on win XP
40#ifdef VISP_HAVE_FUNC_INET_NTOP
41
42#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX
43#include <arpa/inet.h>
44#include <errno.h>
45#include <netdb.h>
46#include <unistd.h>
47#define DWORD int
48#define WSAGetLastError() strerror(errno)
49#else
50#if defined(__MINGW32__)
51#ifdef _WIN32_WINNT
52// Undef _WIN32_WINNT to avoid a warning (_WIN32_WINNT redefinition) with mingw
53#undef _WIN32_WINNT
54#define _WIN32_WINNT _WIN32_WINNT_VISTA // 0x0600
55// Without re-defining _WIN32_WINNT = _WIN32_WINNT_VISTA there is a build issue with mingw:
56// vpUDPServer.cpp:254:23: error: 'inet_ntop' was not declared in this scope
57// const char *ptr = inet_ntop(AF_INET, (void *)&m_clientAddress.sin_addr, result, sizeof(result));
58// vpUDPServer.cpp:254:23: note: suggested alternative: 'inet_ntoa'
59#endif
60#endif
61
62#if defined(__clang__)
63// Mute warning : non-portable path to file '<WS2tcpip.h>'; specified path differs in case from file name on disk [-Wnonportable-system-include-path]
64# pragma clang diagnostic push
65# pragma clang diagnostic ignored "-Wnonportable-system-include-path"
66#endif
67
68#include <Ws2tcpip.h>
69
70#if defined(__clang__)
71# pragma clang diagnostic pop
72#endif
73
74#endif
75
76#include <visp3/core/vpUDPServer.h>
77
86 : m_clientAddress(), m_clientLength(0), m_serverAddress(), m_socketFileDescriptor(0)
87#if defined(_WIN32)
88 ,
89 m_wsa()
90#endif
91{
92 init("", port);
93}
94
101vpUDPServer::vpUDPServer(const std::string &hostname, int port)
102 : m_clientAddress(), m_clientLength(0), m_serverAddress(), m_socketFileDescriptor(0)
103#if defined(_WIN32)
104 ,
105 m_wsa()
106#endif
107{
108 init(hostname, port);
109}
110
112{
113#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX
114 close(m_socketFileDescriptor);
115#else
116 closesocket(m_socketFileDescriptor);
117 WSACleanup();
118#endif
119}
120
121void vpUDPServer::init(const std::string &hostname, int port)
122{
123#if defined(_WIN32)
124 if (WSAStartup(MAKEWORD(2, 2), &m_wsa) != 0) {
125 std::stringstream ss;
126 ss << "Failed WSAStartup for the server, error code: " << WSAGetLastError();
127 throw vpException(vpException::fatalError, ss.str());
128 }
129#endif
130
131 /* socket: create the socket */
132 m_socketFileDescriptor = socket(AF_INET, SOCK_DGRAM, 0);
133#if defined(_WIN32)
134 if (m_socketFileDescriptor == INVALID_SOCKET)
135#else
136 if (m_socketFileDescriptor < 0)
137#endif
138 throw vpException(vpException::fatalError, "Error opening UDP socket for the server!");
139
140/* setsockopt: Handy debugging trick that lets
141 * us rerun the server immediately after we kill it;
142 * otherwise we have to wait about 20 secs.
143 * Eliminates "ERROR on binding: Address already in use" error.
144 */
145#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX
146 int optval = 1;
147 setsockopt(m_socketFileDescriptor, SOL_SOCKET, SO_REUSEADDR, (const void *)&optval, sizeof(int));
148#else
149 const char optval = 1;
150 setsockopt(m_socketFileDescriptor, SOL_SOCKET, SO_REUSEADDR, (const char *)&optval, sizeof(int));
151#endif
152
153 /* build the server's Internet address */
154 memset(&m_serverAddress, 0, sizeof(m_serverAddress));
155 if (hostname.empty()) {
156 m_serverAddress.sin_family = AF_INET;
157 m_serverAddress.sin_addr.s_addr = htonl(INADDR_ANY);
158 m_serverAddress.sin_port = htons(static_cast<unsigned short>(port));
159 }
160 else {
161 std::stringstream ss;
162 ss << port;
163 struct addrinfo hints;
164 struct addrinfo *result = nullptr;
165 struct addrinfo *ptr = nullptr;
166
167 memset(&hints, 0, sizeof(hints));
168 hints.ai_family = AF_INET;
169 hints.ai_socktype = SOCK_DGRAM;
170 hints.ai_protocol = IPPROTO_UDP;
171
172 DWORD dwRetval = getaddrinfo(hostname.c_str(), ss.str().c_str(), &hints, &result);
173 if (dwRetval != 0) {
174 ss.str("");
175 ss << "getaddrinfo failed with error: " << dwRetval;
176 throw vpException(vpException::fatalError, ss.str());
177 }
178
179 for (ptr = result; ptr != nullptr; ptr = ptr->ai_next) {
180 if (ptr->ai_family == AF_INET && ptr->ai_socktype == SOCK_DGRAM) {
181 m_serverAddress = *(struct sockaddr_in *)ptr->ai_addr;
182 break;
183 }
184 }
185
186 freeaddrinfo(result);
187 }
188
189 /* bind: associate the parent socket with a port */
190 if (bind(m_socketFileDescriptor, (struct sockaddr *)&m_serverAddress, sizeof(m_serverAddress)) < 0)
191 throw vpException(vpException::fatalError, "Error on binding on the server!");
192
193 m_clientLength = sizeof(m_clientAddress);
194}
195
207int vpUDPServer::receive(std::string &msg, int timeoutMs)
208{
209 std::string hostInfo = "";
210 return receive(msg, hostInfo, timeoutMs);
211}
212
225int vpUDPServer::receive(std::string &msg, std::string &hostInfo, int timeoutMs)
226{
227 fd_set s;
228 FD_ZERO(&s);
229 FD_SET(m_socketFileDescriptor, &s);
230 struct timeval timeout;
231 if (timeoutMs > 0) {
232 timeout.tv_sec = timeoutMs / 1000;
233 timeout.tv_usec = (timeoutMs % 1000) * 1000;
234 }
235 int retval = select(static_cast<int>(m_socketFileDescriptor) + 1, &s, nullptr, nullptr, timeoutMs > 0 ? &timeout : nullptr);
236
237 if (retval == -1) {
238 std::cerr << "Error select!" << std::endl;
239 return -1;
240 }
241
242 if (retval > 0) {
243/* recvfrom: receive a UDP datagram from a client */
244#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX
245 int length = static_cast<int>(recvfrom(m_socketFileDescriptor, m_buf, sizeof(m_buf), 0,
246 (struct sockaddr *)&m_clientAddress, (socklen_t *)&m_clientLength));
247#else
248 int length =
249 recvfrom(m_socketFileDescriptor, m_buf, sizeof(m_buf), 0, (struct sockaddr *)&m_clientAddress, &m_clientLength);
250#endif
251 if (length <= 0) {
252 return length < 0 ? -1 : 0;
253 }
254
255 msg = std::string(m_buf, length);
256
257 /* getnameinfo: determine who sent the datagram */
258 char hostname[NI_MAXHOST];
259 char servInfo[NI_MAXSERV];
260 DWORD dwRetval = getnameinfo((struct sockaddr *)&m_clientAddress, sizeof(struct sockaddr), hostname, NI_MAXHOST,
261 servInfo, NI_MAXSERV, NI_NUMERICSERV);
262
263 std::string hostName = "", hostIp = "", hostPort = "";
264 if (dwRetval != 0) {
265 std::cerr << "getnameinfo failed with error: " << WSAGetLastError() << std::endl;
266 }
267 else {
268 hostName = hostname;
269 hostPort = servInfo;
270 }
271
272 char result[INET_ADDRSTRLEN];
273 const char *ptr = inet_ntop(AF_INET, (void *)&m_clientAddress.sin_addr, result, sizeof(result));
274 if (ptr == nullptr) {
275 std::cerr << "inet_ntop failed with error: " << WSAGetLastError() << std::endl;
276 }
277 else {
278 hostIp = result;
279 }
280
281 std::stringstream ss;
282 ss << hostName << " " << hostIp << " " << hostPort;
283 hostInfo = ss.str();
284
285 return length;
286 }
287
288 // Timeout
289 return 0;
290}
291
302int vpUDPServer::send(const std::string &msg, const std::string &hostname, int port)
303{
304 if (msg.size() > VP_MAX_UDP_PAYLOAD) {
305 std::cerr << "Message is too long!" << std::endl;
306 return 0;
307 }
308
309 // Create client address
310 memset(&m_clientAddress, 0, sizeof(m_clientAddress));
311 std::stringstream ss;
312 ss << port;
313 struct addrinfo hints;
314 struct addrinfo *result = nullptr;
315 struct addrinfo *ptr = nullptr;
316
317 memset(&hints, 0, sizeof(hints));
318 hints.ai_family = AF_INET;
319 hints.ai_socktype = SOCK_DGRAM;
320 hints.ai_protocol = IPPROTO_UDP;
321
322 DWORD dwRetval = getaddrinfo(hostname.c_str(), ss.str().c_str(), &hints, &result);
323 if (dwRetval != 0) {
324 ss.str("");
325 ss << "getaddrinfo failed with error: " << dwRetval;
326 throw vpException(vpException::fatalError, ss.str());
327 }
328
329 for (ptr = result; ptr != nullptr; ptr = ptr->ai_next) {
330 if (ptr->ai_family == AF_INET && ptr->ai_socktype == SOCK_DGRAM) {
331 m_clientAddress = *(struct sockaddr_in *)ptr->ai_addr;
332 break;
333 }
334 }
335
336 freeaddrinfo(result);
337
338/* send the message to the client */
339#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX
340 return static_cast<int>(
341 sendto(m_socketFileDescriptor, msg.c_str(), msg.size(), 0, (struct sockaddr *)&m_clientAddress, m_clientLength));
342#else
343 return sendto(m_socketFileDescriptor, msg.c_str(), static_cast<int>(msg.size()), 0, (struct sockaddr *)&m_clientAddress,
344 m_clientLength);
345#endif
346}
347END_VISP_NAMESPACE
348#elif !defined(VISP_BUILD_SHARED_LIBS)
349// Work around to avoid warning: libvisp_core.a(vpUDPServer.cpp.o) has no symbols
350void dummy_vpUDPServer() { }
351#endif
error that can be emitted by ViSP classes.
Definition vpException.h:60
@ fatalError
Fatal error.
Definition vpException.h:72
vpUDPServer(int port)
virtual ~vpUDPServer()
int send(const std::string &msg, const std::string &hostname, int port)
int receive(std::string &msg, int timeoutMs=0)