|
From: <gb...@us...> - 2008-10-14 02:53:59
|
Revision: 319
http://gearbox.svn.sourceforge.net/gearbox/?rev=319&view=rev
Author: gbiggs
Date: 2008-10-14 02:53:40 +0000 (Tue, 14 Oct 2008)
Log Message:
-----------
Added first version of UDP support to flexiport.
Modified Paths:
--------------
gearbox/trunk/src/flexiport/CMakeLists.txt
gearbox/trunk/src/flexiport/flexiport.cpp
gearbox/trunk/src/flexiport/flexiport_config.h.in
gearbox/trunk/src/flexiport/tcpport.cpp
gearbox/trunk/src/flexiport/tcpport.h
gearbox/trunk/src/flexiport/test/CMakeLists.txt
gearbox/trunk/src/flexiport/test/example.cmake.in
gearbox/trunk/src/flexiport/test/example.readme
Added Paths:
-----------
gearbox/trunk/src/flexiport/test/udp_example.cpp
gearbox/trunk/src/flexiport/udpport.cpp
gearbox/trunk/src/flexiport/udpport.h
Modified: gearbox/trunk/src/flexiport/CMakeLists.txt
===================================================================
--- gearbox/trunk/src/flexiport/CMakeLists.txt 2008-10-12 01:39:24 UTC (rev 318)
+++ gearbox/trunk/src/flexiport/CMakeLists.txt 2008-10-14 02:53:40 UTC (rev 319)
@@ -9,8 +9,9 @@
OPTION (FLEXIPORT_INCLUDE_SERIAL "Include the serial port in FlexiPort" ON)
OPTION (FLEXIPORT_INCLUDE_TCP "Include the TCP network port in FlexiPort" ON)
+ OPTION (FLEXIPORT_INCLUDE_UDP "Include the UDP network port in FlexiPort" ON)
OPTION (FLEXIPORT_INCLUDE_LOGGING "Include the log reader/writer ports in FlexiPort" ON)
- MARK_AS_ADVANCED (FLEXIPORT_INCLUDE_SERIAL FLEXIPORT_INCLUDE_TCP)
+ MARK_AS_ADVANCED (FLEXIPORT_INCLUDE_SERIAL FLEXIPORT_INCLUDE_TCP FLEXIPORT_INCLUDE_UDP)
IF (GBX_OS_QNX)
SET (CMAKE_REQUIRED_LIBRARIES socket)
@@ -33,6 +34,10 @@
SET (hdrs ${hdrs} tcpport.h)
SET (srcs ${srcs} tcpport.cpp)
ENDIF (FLEXIPORT_INCLUDE_TCP)
+ IF (FLEXIPORT_INCLUDE_UDP)
+ SET (hdrs ${hdrs} udpport.h)
+ SET (srcs ${srcs} udpport.cpp)
+ ENDIF (FLEXIPORT_INCLUDE_UDP)
IF (FLEXIPORT_INCLUDE_LOGGING)
SET (hdrs ${hdrs} logwriterport.h logreaderport.h)
SET (srcs ${srcs} logwriterport.cpp logreaderport.cpp logfile.cpp)
Modified: gearbox/trunk/src/flexiport/flexiport.cpp
===================================================================
--- gearbox/trunk/src/flexiport/flexiport.cpp 2008-10-12 01:39:24 UTC (rev 318)
+++ gearbox/trunk/src/flexiport/flexiport.cpp 2008-10-14 02:53:40 UTC (rev 319)
@@ -29,6 +29,7 @@
#include "port.h"
#include "serialport.h"
#include "tcpport.h"
+#include "udpport.h"
#include "logwriterport.h"
#include "logreaderport.h"
#include "flexiport_config.h"
@@ -109,6 +110,10 @@
if (type == "tcp")
return new TCPPort (options);
#endif // FLEXIPORT_INCLUDE_TCP
+#ifdef FLEXIPORT_INCLUDE_UDP
+ if (type == "udp")
+ return new UDPPort (options);
+#endif // FLEXIPORT_INCLUDE_UDP
#ifdef FLEXIPORT_INCLUDE_LOGGING
if (type == "logreader")
Modified: gearbox/trunk/src/flexiport/flexiport_config.h.in
===================================================================
--- gearbox/trunk/src/flexiport/flexiport_config.h.in 2008-10-12 01:39:24 UTC (rev 318)
+++ gearbox/trunk/src/flexiport/flexiport_config.h.in 2008-10-14 02:53:40 UTC (rev 319)
@@ -2,5 +2,6 @@
#cmakedefine FLEXIPORT_INCLUDE_SERIAL 1
#cmakedefine FLEXIPORT_INCLUDE_TCP 1
+#cmakedefine FLEXIPORT_INCLUDE_UDP 1
#cmakedefine FLEXIPORT_INCLUDE_LOGGING 1
#cmakedefine FLEXIPORT_HAVE_GETADDRINFO 1
\ No newline at end of file
Modified: gearbox/trunk/src/flexiport/tcpport.cpp
===================================================================
--- gearbox/trunk/src/flexiport/tcpport.cpp 2008-10-12 01:39:24 UTC (rev 318)
+++ gearbox/trunk/src/flexiport/tcpport.cpp 2008-10-14 02:53:40 UTC (rev 319)
@@ -4,17 +4,17 @@
* Copyright (c) 2008 Geoffrey Biggs
*
* flexiport flexible hardware data communications library.
- *
- * This distribution is licensed to you under the terms described in the LICENSE file included in
+ *
+ * This distribution is licensed to you under the terms described in the LICENSE file included in
* this distribution.
*
* This work is a product of the National Institute of Advanced Industrial Science and Technology,
* Japan. Registration number: H20PRO-881
- *
+ *
* This file is part of flexiport.
*
* flexiport is free software: you can redistribute it and/or modify it under the terms of the GNU
- * Lesser General Public License as published by the Free Software Foundation, either version 3 of
+ * Lesser General Public License as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* flexiport is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
@@ -156,7 +156,7 @@
// Connect to the given address
if (_debug >= 1)
cerr << "TCPPort::" << __func__ << "() Connecting" << endl;
-
+
Connect ();
}
SetPortBlockingFlag ();
@@ -230,11 +230,11 @@
{
if (ErrNo () == ERRNO_EAGAIN)
return -1; // Timed out
- else
+ else
{
// General error
stringstream ss;
- ss << "TCPPort::" << __func__ << "() recv() error: (" << ErrNo () << ") " <<
+ ss << "TCPPort::" << __func__ << "() recv() error: (" << ErrNo () << ") " <<
StrError (ErrNo ());
throw PortException (ss.str ());
}
@@ -266,7 +266,7 @@
if (_debug >= 2)
{
- cerr << "TCPPort::" << __func__ << "() Going to read until have " <<
+ cerr << "TCPPort::" << __func__ << "() Going to read until have " <<
count << " bytes" << endl;
}
@@ -289,11 +289,11 @@
throw PortException (string ("TCPPort::") + __func__ +
string ("() recv() timed out, probably shouldn't happen."));
}
- else
+ else
{
// General error
stringstream ss;
- ss << "TCPPort::" << __func__ << "() recv() error: (" << ErrNo () << ") " <<
+ ss << "TCPPort::" << __func__ << "() recv() error: (" << ErrNo () << ") " <<
StrError (ErrNo ());
throw PortException (ss.str ());
}
@@ -351,7 +351,7 @@
if (_debug >= 2)
{
- cerr << "TCPPort::" << __func__ << "() Found " << bytesAvailable <<
+ cerr << "TCPPort::" << __func__ << "() Found " << bytesAvailable <<
" bytes available" << endl;
}
return bytesAvailable;
@@ -386,14 +386,14 @@
#endif
{
stringstream ss;
- ss << "TCPPort::" << __func__ << "() ioctl() error: (" << ErrNo () << ") " <<
+ ss << "TCPPort::" << __func__ << "() ioctl() error: (" << ErrNo () << ") " <<
StrError (ErrNo ());
throw PortException (ss.str ());
}
if (_debug >= 2)
{
- cerr << "TCPPort::" << __func__ << "() Found " << bytesAvailable <<
+ cerr << "TCPPort::" << __func__ << "() Found " << bytesAvailable <<
" bytes available after waiting" << endl;
}
return bytesAvailable;
@@ -436,7 +436,7 @@
{
// General error
stringstream ss;
- ss << "TCPPort::" << __func__ << "() send() error: (" << ErrNo () << ") " <<
+ ss << "TCPPort::" << __func__ << "() send() error: (" << ErrNo () << ") " <<
StrError (ErrNo ());
throw PortException (ss.str ());
}
@@ -470,7 +470,7 @@
if (numRead < 0 && ErrNo () != ERRNO_EAGAIN)
{
stringstream ss;
- ss << "TCPPort::" << __func__ << "() recv() error: (" << ErrNo () << ") " <<
+ ss << "TCPPort::" << __func__ << "() recv() error: (" << ErrNo () << ") " <<
StrError (ErrNo ());
throw PortException (ss.str ());
}
@@ -556,44 +556,74 @@
{
Close (); // To make sure
- _sock = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
+ // If getaddrinfo() is available, much less stuff needs to be hard-coded or copied around.
+#if defined (FLEXIPORT_HAVE_GETADDRINFO)
+ struct addrinfo *res = NULL, hints;
+
+ memset (&hints, 0, sizeof (hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = IPPROTO_TCP;
+
+ int errorCode;
+ ostringstream portSS;
+ portSS << _port;
+ if ((errorCode = getaddrinfo (_ip.c_str (), portSS.str ().c_str (), &hints, &res)) != 0)
+ {
+ Close ();
+ stringstream ss;
#if defined (WIN32)
+ ss << "TCPPort::" << __func__ << "() getaddrinfo() error: (" << ErrNo () << ") " <<
+ StrError (ErrNo ());
+#else
+ ss << "TCPPort::" << __func__ << "() getaddrinfo() error: (" << errorCode << ") " <<
+ gai_strerror (errorCode);
+#endif
+ throw PortException (ss.str ());
+ }
+
+ _sock = socket (res->ai_family, res->ai_socktype, res->ai_protocol);
+#if defined (WIN32)
if (_sock == INVALID_SOCKET)
#else
if (_sock < 0)
#endif
{
stringstream ss;
- ss << "TCPPort::" << __func__ << "() socket() error: (" << ErrNo () << ") " <<
+ ss << "TCPPort::" << __func__ << "() socket() error: (" << ErrNo () << ") " <<
StrError (ErrNo ());
throw PortException (ss.str ());
}
- sockaddr_in sockAddr;
- memset (&sockAddr, 0, sizeof (sockAddr));
-#if defined (FLEXIPORT_HAVE_GETADDRINFO)
- struct addrinfo *res = NULL, hints;
- memset (&hints, 0, sizeof (hints));
- hints.ai_family = AF_INET;
- hints.ai_socktype = SOCK_STREAM;
- hints.ai_protocol = IPPROTO_TCP;
- int errorCode;
- if ((errorCode = getaddrinfo (_ip.c_str (), NULL, &hints, &res)) != 0)
+ if (_debug >= 1)
+ cerr << "TCPPort::" << __func__ << "() Connecting to " << _ip << ":" << _port << "." << endl;
+ if (connect (_sock, res->ai_addr, res->ai_addrlen) < 0)
{
Close ();
stringstream ss;
+ ss << "Failed to connect to " << _ip << ": (" << ErrNo () << ") " << StrError (ErrNo ());
+ throw PortException (ss.str ());
+ }
+
+ freeaddrinfo (res);
+#else // defined (FLEXIPORT_HAVE_GETADDRINFO)
+ // Do it the ugly old way.
+ sockaddr_in sockAddr;
+ memset (&sockAddr, 0, sizeof (sockAddr));
+
+ _sock = socket (PF_INET, SOCK_STREAM, 0);
#if defined (WIN32)
- ss << "TCPPort::" << __func__ << "() getaddrinfo() error: (" << ErrNo () << ") " <<
- StrError (ErrNo ());
+ if (_sock == INVALID_SOCKET)
#else
- ss << "TCPPort::" << __func__ << "() getaddrinfo() error: (" << errorCode << ") " <<
- gai_strerror (errorCode);
+ if (_sock < 0)
#endif
+ {
+ stringstream ss;
+ ss << "TCPPort::" << __func__ << "() socket() error: (" << ErrNo () << ") " <<
+ StrError (ErrNo ());
throw PortException (ss.str ());
}
- memcpy (&sockAddr, res[0].ai_addr, res[0].ai_addrlen);
- freeaddrinfo (res);
-#else // defined (FLEXIPORT_HAVE_GETADDRINFO)
+
struct hostent *hp = NULL;
if ((hp = gethostbyname (_ip.c_str ())) == NULL)
{
@@ -602,7 +632,6 @@
}
memcpy (&sockAddr.sin_addr, hp->h_addr, hp->h_length);
sockAddr.sin_family = hp->h_addrtype;
-#endif // defined (FLEXIPORT_HAVE_GETADDRINFO)
sockAddr.sin_port = htons (_port);
if (_debug >= 1)
@@ -614,16 +643,84 @@
ss << "Failed to connect to " << _ip << ": (" << ErrNo () << ") " << StrError (ErrNo ());
throw PortException (ss.str ());
}
+#endif // defined (FLEXIPORT_HAVE_GETADDRINFO)
}
// Wait for a connection: used in listen mode
void TCPPort::WaitForConnection (void)
{
+ Close (); // To make sure
+
+#if defined (FLEXIPORT_HAVE_GETADDRINFO)
+ struct addrinfo *res = NULL, hints;
+ memset (&hints, 0, sizeof (hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_PASSIVE;
+
+ ostringstream portSS;
+ portSS << _port;
+ int errorCode;
+ if (_ip == "*")
+ {
+ // Listen on all interfaces
+ if ((errorCode = getaddrinfo (NULL, portSS.str ().c_str (), &hints, &res)) != 0)
+ {
+ stringstream ss;
+#if defined (WIN32)
+ ss << "TCPPort::" << __func__ << "() getaddrinfo() error: (" << ErrNo () << ") " <<
+ StrError (ErrNo ());
+#else
+ ss << "TCPPort::" << __func__ << "() getaddrinfo() error: (" << errorCode << ") " <<
+ gai_strerror (errorCode);
+#endif
+ throw PortException (ss.str ());
+ }
+ }
+ else
+ {
+ // Listen on the specified interface only
+ if ((errorCode = getaddrinfo (_ip.c_str (), portSS.str ().c_str (), &hints, &res)) != 0)
+ {
+ stringstream ss;
+#if defined (WIN32)
+ ss << "TCPPort::" << __func__ << "() getaddrinfo() error: (" << ErrNo () << ") " <<
+ StrError (ErrNo ());
+#else
+ ss << "TCPPort::" << __func__ << "() getaddrinfo() error: (" << errorCode << ") " <<
+ gai_strerror (errorCode);
+#endif
+ throw PortException (ss.str ());
+ }
+ }
+
+ _listenSock = socket (res->ai_family, res->ai_socktype, res->ai_protocol);
+#if defined (WIN32)
+ if (_listenSock == INVALID_SOCKET)
+#else
+ if (_listenSock < 0)
+#endif
+ {
+ stringstream ss;
+ ss << "TCPPort::" << __func__ << "() socket() error: (" << ErrNo () << ") " <<
+ StrError (ErrNo ());
+ throw PortException (ss.str ());
+ }
+
+ if (bind (_listenSock, res->ai_addr, res->ai_addrlen) < 0)
+ {
+ Close ();
+ stringstream ss;
+ ss << "TCPPort::" << __func__ << "() bind() error: (" << ErrNo () << ") " <<
+ StrError (ErrNo ());
+ throw PortException (ss.str ());
+ }
+
+ freeaddrinfo (res);
+#else // defined (FLEXIPORT_HAVE_GETADDRINFO)
char hostName[HOST_NAME_MAX + 1] = {'\0'};
- struct hostent *hp = NULL;
+ struct hostent *hp = NULL;
- Close (); // To make sure
-
_listenSock = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
#if defined (WIN32)
if (_listenSock == INVALID_SOCKET)
@@ -646,14 +743,14 @@
{
{
stringstream ss;
- ss << "TCPPort::" << __func__ << "() gethostname() error: (" << ErrNo () << ") " <<
+ ss << "TCPPort::" << __func__ << "() gethostname() error: (" << ErrNo () << ") " <<
StrError (ErrNo ());
throw PortException (ss.str ());
}
}
if ((hp = gethostbyname (hostName)) == NULL)
{
- throw PortException (string ("TCPPort::") + __func__ +
+ throw PortException (string ("TCPPort::") + __func__ +
string (" gethostbyname() error."));
}
sockAddr.sin_family = hp->h_addrtype;
@@ -662,36 +759,12 @@
else
{
// Listen on the specified interface only
-#if defined (FLEXIPORT_HAVE_GETADDRINFO)
- struct addrinfo *res = NULL, hints;
- memset (&hints, 0, sizeof (hints));
- hints.ai_family = AF_INET;
- hints.ai_socktype = SOCK_STREAM;
- hints.ai_protocol = IPPROTO_TCP;
- int errorCode;
- if ((errorCode = getaddrinfo (_ip.c_str (), NULL, &hints, &res)) != 0)
- {
- Close ();
- stringstream ss;
-#if defined (WIN32)
- ss << "TCPPort::" << __func__ << "() getaddrinfo() error: (" << ErrNo () << ") " <<
- StrError (ErrNo ());
-#else
- ss << "TCPPort::" << __func__ << "() getaddrinfo() error: (" << errorCode << ") " <<
- gai_strerror (errorCode);
-#endif
- throw PortException (ss.str ());
- }
- memcpy (&sockAddr, res[0].ai_addr, res[0].ai_addrlen);
- freeaddrinfo (res);
-#else // defined (FLEXIPORT_HAVE_GETADDRINFO)
if ((hp = gethostbyname (_ip.c_str ())) == NULL)
{
throw PortException (string ("TCPPort::") + __func__ +
string (" gethostbyname() error."));
}
sockAddr.sin_family = hp->h_addrtype;
-#endif // defined (FLEXIPORT_HAVE_GETADDRINFO)
sockAddr.sin_port = htons (_port);
}
@@ -703,8 +776,11 @@
StrError (ErrNo ());
throw PortException (ss.str ());
}
+#endif // defined (FLEXIPORT_HAVE_GETADDRINFO)
+
+ // All the same from this point on
listen (_listenSock, 1);
-
+
if (_debug >= 1)
cerr << "TCPPort::" << __func__ << "() Waiting for a connection." << endl;
_sock = accept (_listenSock, NULL, NULL);
@@ -720,6 +796,15 @@
StrError (ErrNo ());
throw PortException (ss.str ());
}
+
+ // Done with the listening socket so close it
+ if (close (_listenSock) < 0)
+ {
+ stringstream ss;
+ ss << "TCPPort::" << __func__ << "() close(_listenSock) error: (" << ErrNo () << ") " <<
+ StrError (ErrNo ());
+ throw PortException (ss.str ());
+ }
}
// Checks if data is available, waiting for the timeout if none is available immediatly
@@ -854,7 +939,7 @@
if ((flags = fcntl (_sock, F_GETFD)) < 0)
{
stringstream ss;
- ss << "TCPPort::" << __func__ << "() fcntl(F_GETFD) error: (" << ErrNo () << ") " <<
+ ss << "TCPPort::" << __func__ << "() fcntl(F_GETFD) error: (" << ErrNo () << ") " <<
StrError (ErrNo ());
throw PortException (ss.str ());
}
@@ -862,7 +947,7 @@
if (fcntl (_sock, F_SETFD, flags) < 0)
{
stringstream ss;
- ss << "TCPPort::" << __func__ << "() fcntl(F_SETFD) error: (" << ErrNo () << ") " <<
+ ss << "TCPPort::" << __func__ << "() fcntl(F_SETFD) error: (" << ErrNo () << ") " <<
StrError (ErrNo ());
throw PortException (ss.str ());
}
@@ -885,7 +970,7 @@
if ((flags = fcntl (_sock, F_GETFD)) < 0)
{
stringstream ss;
- ss << "TCPPort::" << __func__ << "() fcntl(F_GETFD) error: (" << ErrNo () << ") " <<
+ ss << "TCPPort::" << __func__ << "() fcntl(F_GETFD) error: (" << ErrNo () << ") " <<
StrError (ErrNo ());
throw PortException (ss.str ());
}
@@ -893,7 +978,7 @@
if (fcntl (_sock, F_SETFD, flags) < 0)
{
stringstream ss;
- ss << "TCPPort::" << __func__ << "() fcntl(F_SETFD) error: (" << ErrNo () << ") " <<
+ ss << "TCPPort::" << __func__ << "() fcntl(F_SETFD) error: (" << ErrNo () << ") " <<
StrError (ErrNo ());
throw PortException (ss.str ());
}
Modified: gearbox/trunk/src/flexiport/tcpport.h
===================================================================
--- gearbox/trunk/src/flexiport/tcpport.h 2008-10-12 01:39:24 UTC (rev 318)
+++ gearbox/trunk/src/flexiport/tcpport.h 2008-10-14 02:53:40 UTC (rev 319)
@@ -4,17 +4,17 @@
* Copyright (c) 2008 Geoffrey Biggs
*
* flexiport flexible hardware data communications library.
- *
- * This distribution is licensed to you under the terms described in the LICENSE file included in
+ *
+ * This distribution is licensed to you under the terms described in the LICENSE file included in
* this distribution.
*
* This work is a product of the National Institute of Advanced Industrial Science and Technology,
* Japan. Registration number: H20PRO-881
- *
+ *
* This file is part of flexiport.
*
* flexiport is free software: you can redistribute it and/or modify it under the terms of the GNU
- * Lesser General Public License as published by the Free Software Foundation, either version 3 of
+ * Lesser General Public License as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* flexiport is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
@@ -46,7 +46,7 @@
@par Options
- ip <string>
- - TCP IP address to connect to.
+ - IP address to connect to. If listen is true, set to "*" to listen on any interface.
- Default: 127.0.0.1
- port <integer>
- TCP port to connect to/listen on.
@@ -62,7 +62,7 @@
~TCPPort (void);
/** @brief Open the port.
-
+
For a listening port, this will call accept() and therefore cause the calling process to
block until an incoming connection. */
void Open (void);
@@ -103,7 +103,7 @@
bool _open;
void CheckPort (bool read);
-
+
bool ProcessOption (const std::string &option, const std::string &value);
void Connect (void);
Modified: gearbox/trunk/src/flexiport/test/CMakeLists.txt
===================================================================
--- gearbox/trunk/src/flexiport/test/CMakeLists.txt 2008-10-12 01:39:24 UTC (rev 318)
+++ gearbox/trunk/src/flexiport/test/CMakeLists.txt 2008-10-14 02:53:40 UTC (rev 319)
@@ -1,10 +1,13 @@
INCLUDE (${GBX_CMAKE_DIR}/UseBasicRules.cmake)
+GBX_ADD_EXECUTABLE (serial_example serial_example.cpp)
+TARGET_LINK_LIBRARIES (serial_example flexiport)
+
GBX_ADD_EXECUTABLE (tcp_example tcp_example.cpp)
TARGET_LINK_LIBRARIES (tcp_example flexiport)
-GBX_ADD_EXECUTABLE (serial_example serial_example.cpp)
-TARGET_LINK_LIBRARIES (serial_example flexiport)
+GBX_ADD_EXECUTABLE (udp_example udp_example.cpp)
+TARGET_LINK_LIBRARIES (udp_example flexiport)
GBX_ADD_EXAMPLE (flexiport/example example.cmake.in example.cmake
- tcp_example.cpp serial_example.cpp example.readme example.logr example.logw)
+ serial_example.cpp tcp_example.cpp udp_example.cpp example.readme example.logr example.logw)
Modified: gearbox/trunk/src/flexiport/test/example.cmake.in
===================================================================
--- gearbox/trunk/src/flexiport/test/example.cmake.in 2008-10-12 01:39:24 UTC (rev 318)
+++ gearbox/trunk/src/flexiport/test/example.cmake.in 2008-10-14 02:53:40 UTC (rev 319)
@@ -4,6 +4,13 @@
INCLUDE_DIRECTORIES (@CMAKE_INSTALL_PREFIX@)
+ADD_EXECUTABLE (flexiport_serial_example serial_example.cpp)
+TARGET_LINK_LIBRARIES (flexiport_serial_example flexiport)
+SET_TARGET_PROPERTIES (flexiport_serial_example PROPERTIES
+ LINK_FLAGS "-L@CMAKE_INSTALL_PREFIX@/lib/gearbox"
+ INSTALL_RPATH "${INSTALL_RPATH};@CMAKE_INSTALL_PREFIX@/lib/gearbox"
+ BUILD_WITH_INSTALL_RPATH TRUE)
+
ADD_EXECUTABLE (flexiport_tcp_example tcp_example.cpp)
TARGET_LINK_LIBRARIES (flexiport_tcp_example flexiport)
SET_TARGET_PROPERTIES (flexiport_tcp_example PROPERTIES
@@ -11,9 +18,9 @@
INSTALL_RPATH "${INSTALL_RPATH};@CMAKE_INSTALL_PREFIX@/lib/gearbox"
BUILD_WITH_INSTALL_RPATH TRUE)
-ADD_EXECUTABLE (flexiport_serial_example serial_example.cpp)
-TARGET_LINK_LIBRARIES (flexiport_serial_example flexiport)
-SET_TARGET_PROPERTIES (flexiport_serial_example PROPERTIES
+ADD_EXECUTABLE (flexiport_udp_example udp_example.cpp)
+TARGET_LINK_LIBRARIES (flexiport_udp_example flexiport)
+SET_TARGET_PROPERTIES (flexiport_udp_example PROPERTIES
LINK_FLAGS "-L@CMAKE_INSTALL_PREFIX@/lib/gearbox"
INSTALL_RPATH "${INSTALL_RPATH};@CMAKE_INSTALL_PREFIX@/lib/gearbox"
BUILD_WITH_INSTALL_RPATH TRUE)
Modified: gearbox/trunk/src/flexiport/test/example.readme
===================================================================
--- gearbox/trunk/src/flexiport/test/example.readme 2008-10-12 01:39:24 UTC (rev 318)
+++ gearbox/trunk/src/flexiport/test/example.readme 2008-10-14 02:53:40 UTC (rev 319)
@@ -46,6 +46,17 @@
tcp_example -o timeout=1,debug=3
+Running udp_example
+----------------------
+
+The udp_example is similar to the tcp_example, replacing the TCPPort object
+with a UDPPort object.
+
+You may specify additional options for the UDPPort objects using -o, such as
+a timeout. e.g.:
+
+udp_example -o timeout=1,debug=3
+
Note for Windows users
----------------------
Added: gearbox/trunk/src/flexiport/test/udp_example.cpp
===================================================================
--- gearbox/trunk/src/flexiport/test/udp_example.cpp (rev 0)
+++ gearbox/trunk/src/flexiport/test/udp_example.cpp 2008-10-14 02:53:40 UTC (rev 319)
@@ -0,0 +1,232 @@
+/*
+ * GearBox Project: Peer-Reviewed Open-Source Libraries for Robotics
+ * http://gearbox.sf.net/
+ * Copyright (c) 2008 Geoffrey Biggs
+ *
+ * flexiport flexible hardware data communications library.
+ *
+ * This distribution is licensed to you under the terms described in the LICENSE file included in
+ * this distribution.
+ *
+ * This work is a product of the National Institute of Advanced Industrial Science and Technology,
+ * Japan. Registration number: H20PRO-881
+ *
+ * This file is part of flexiport.
+ *
+ * flexiport is free software: you can redistribute it and/or modify it under the terms of the GNU
+ * Lesser General Public License as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * flexiport is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
+ * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with flexiport.
+ * If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef WIN32
+ #include <unistd.h>
+#endif
+
+#include <string.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <string>
+#include <iostream>
+using namespace std;
+
+#include <flexiport/flexiport.h>
+#include <flexiport/port.h>
+
+#if defined (WIN32)
+ #include <Windows.h>
+#endif
+
+inline void SLEEP (int length)
+{
+#if defined (WIN32)
+ Sleep (length * 1000);
+#else
+ sleep (length);
+#endif
+}
+
+#if defined (WIN32)
+DWORD WINAPI DoChild (LPVOID param)
+{
+ string portOptions = *(reinterpret_cast<string*> (param));
+#else
+int DoChild (string portOptions)
+{
+#endif
+ cout << "Waiting a few seconds..." << endl;
+ SLEEP (5);
+ cout << "Creating client port" << endl;
+ flexiport::Port *port;
+
+ port = flexiport::CreatePort ("type=udp,dest_port=20000,recv_port=20001," + portOptions);
+ cout << port->GetStatus ();
+
+ cout << "Client opening port." << endl;
+ port->Open ();
+ cout << "Client port open. Testing sending data to server." << endl;
+ cout << "Client sending 'Message #1'" << endl;
+ string stringMessage = "Message #1";
+ port->WriteString (stringMessage);
+ SLEEP (5);
+ cout << "Client sending 'This is message #2\\n'" << endl;
+ char charMessage[] = "This is message #2\n";
+ port->Write (charMessage, strlen (charMessage) + 1);
+ SLEEP (5);
+ cout << "Client sending 'Probably message #3.'" << endl;
+ port->WriteString ("Probably message #3.");
+ cout << "Client waiting for parting message." << endl;
+ port->ReadString (stringMessage);
+ cout << "Client got parting message: \"" << stringMessage << '"' << endl;
+ cout << "Client done." << endl;
+
+ return 0;
+}
+
+int DoParent (string portOptions)
+{
+ string stringBuffer;
+ char charBuffer[32];
+ memset (charBuffer, 0, sizeof (char) * 32);
+
+ cout << "Creating server port" << endl;
+ flexiport::Port *port;
+
+ port = flexiport::CreatePort ("type=udp,dest_port=20001,recv_port=20000," + portOptions);
+ cout << port->GetStatus ();
+
+ cout << "Server opening port." << endl;
+ port->Open ();
+ cout << "Server port is open." << endl;
+ cout << "Server testing ReadString()" << endl;
+ port->ReadString (stringBuffer);
+ cout << "Server received \"" << stringBuffer << '"' << endl;
+ if (stringBuffer != "Message #1")
+ {
+ cout << "Test failed." << endl;
+ return -1;
+ }
+// cout << "Server testing ReadStringUntil()" << endl;
+// port->ReadStringUntil (stringBuffer, '\n');
+// cout << "Server received \"" << stringBuffer << '"' << endl;
+// if (stringBuffer != "Message #2\n")
+// {
+// cout << "Test failed." << endl;
+// return -1;
+// }
+// cout << "Server has " << port->BytesAvailable () << " bytes available immediatly." << endl;
+// cout << "Server clearing the trailing null byte from that last message." << endl;
+// port->Read (charBuffer, 1);
+ int bytesWaiting = port->BytesAvailableWait ();
+ cout << "Server has " << bytesWaiting << " bytes available after waiting." << endl;
+ cout << "Server testing ReadFull()" << endl;
+ port->ReadFull (charBuffer, bytesWaiting);
+ cout << "Server received \"" << charBuffer << '"' << endl;
+ if (strncmp (charBuffer, "This is message #2", 18) != 0)
+ {
+ cout << "Test failed." << endl;
+ return -1;
+ }
+// cout << "Server testing ReadUntil()" << endl;
+// port->ReadUntil (charBuffer, 32, '\n');
+// cout << "Server received \"" << charBuffer << '"' << endl;
+// if (strncmp (charBuffer, "Probably message #4\n", 20) != 0)
+// {
+// cout << "Test failed." << endl;
+// return -1;
+// }
+// cout << "Server clearing the trailing null byte from that last message." << endl;
+// port->Read (charBuffer, 1);
+ cout << "Server testing Read()" << endl;
+ memset (charBuffer, 0, sizeof (char) * 32);
+ port->Read (charBuffer, 32);
+ cout << "Server received \"" << charBuffer << '"' << endl;
+ if (strncmp (charBuffer, "Probably message #3.", 20) != 0)
+ {
+ cout << "Test failed." << endl;
+ return -1;
+ }
+// cout << "Server testing ReadLine()" << endl;
+// port->ReadLine (stringBuffer);
+// cout << "Server received \"" << stringBuffer << '"' << endl;
+// if (stringBuffer != "Finally, message #6\n")
+// {
+// cout << "Test failed." << endl;
+// return -1;
+// }
+ cout << "Server sending back to client" << endl;
+ port->WriteString ("So long, and thanks for all the text.");
+ SLEEP (5);
+
+ return 0;
+}
+
+int main (int argc, char **argv)
+{
+ string portOptions;
+
+#if defined (WIN32)
+ portOptions = "";
+#else
+ int opt;
+ // Get some options from the command line
+ while ((opt = getopt (argc, argv, "o:")) != -1)
+ {
+ switch (opt)
+ {
+ case 'o':
+ portOptions = optarg;
+ break;
+ default:
+ cout << "Usage: " << argv[0] << " [-o portoptions]" << endl << endl;
+ cout << "-o options\tString of extra options for the port." << endl;
+ return 1;
+ }
+ }
+#endif
+
+ try
+ {
+ // Fork and create a listener and a connector process
+#if defined (WIN32)
+ if (!CreateThread (NULL, 0, DoChild, &portOptions, 0, NULL))
+ {
+ cerr << "Failed to create child thread." << endl;
+ return 1;
+ }
+ if (DoParent (portOptions) < 0)
+ return 1;
+#else
+ pid_t result;
+ result = fork ();
+ if (result < 0)
+ {
+ cerr << "Failed to fork: (" << errno << ") " << strerror (errno) << endl;
+ return -1;
+ }
+ else if (result == 0)
+ {
+ if (DoChild (portOptions) < 0)
+ return 1;
+ }
+ else
+ {
+ if (DoParent (portOptions) < 0)
+ return 1;
+ }
+#endif
+ }
+ catch (flexiport::PortException e)
+ {
+ cerr << "Caught exception: " << e.what () << endl;
+ return 1;
+ }
+
+ return 0;
+}
Added: gearbox/trunk/src/flexiport/udpport.cpp
===================================================================
--- gearbox/trunk/src/flexiport/udpport.cpp (rev 0)
+++ gearbox/trunk/src/flexiport/udpport.cpp 2008-10-14 02:53:40 UTC (rev 319)
@@ -0,0 +1,1084 @@
+/*
+ * GearBox Project: Peer-Reviewed Open-Source Libraries for Robotics
+ * http://gearbox.sf.net/
+ * Copyright (c) 2008 Geoffrey Biggs
+ *
+ * flexiport flexible hardware data communications library.
+ *
+ * This distribution is licensed to you under the terms described in the LICENSE file included in
+ * this distribution.
+ *
+ * This work is a product of the National Institute of Advanced Industrial Science and Technology,
+ * Japan. Registration number: H20PRO-881
+ *
+ * This file is part of flexiport.
+ *
+ * flexiport is free software: you can redistribute it and/or modify it under the terms of the GNU
+ * Lesser General Public License as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * flexiport is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
+ * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with flexiport.
+ * If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "flexiport.h"
+#include "udpport.h"
+#include "flexiport_config.h"
+
+#if defined (FLEXIPORT_HAVE_GETADDRINFO)
+ #include <sys/socket.h>
+ #include <netdb.h>
+#endif
+#include <sys/types.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sstream>
+#include <iostream>
+using namespace std;
+
+#if defined (WIN32)
+ #include <winsock2.h>
+ #include <ws2tcpip.h>
+ #define __func__ __FUNCTION__
+#else
+ #include <unistd.h>
+ #include <errno.h>
+ #include <sys/socket.h>
+ #include <sys/ioctl.h>
+ #include <netdb.h>
+#endif
+
+#if !defined (HOST_NAME_MAX)
+ #define HOST_NAME_MAX 256
+#endif
+
+namespace flexiport
+{
+
+inline int ErrNo (void)
+{
+#if defined (WIN32)
+ return WSAGetLastError ();
+#else
+ return errno;
+#endif
+}
+
+inline string StrError (int errNo)
+{
+#if defined (WIN32)
+ LPVOID bufferPointer = NULL;
+ FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL,
+ errNo, 0, reinterpret_cast<LPTSTR> (&bufferPointer), 0, NULL);
+ string result (reinterpret_cast<LPTSTR> (bufferPointer));
+ LocalFree (bufferPointer);
+ return result;
+#else
+ return string (strerror (errNo));
+#endif
+}
+
+#if defined (WIN32)
+ const int ERRNO_EAGAIN = WSAEWOULDBLOCK;
+#else
+ const int ERRNO_EAGAIN = EAGAIN;
+#endif
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Constructor/destructor
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+UDPPort::UDPPort (map<string, string> options)
+#if defined (WIN32)
+ : Port (), _sendSock (INVALID_SOCKET), _recvSock (INVALID_SOCKET),
+#else
+ : Port (), _sendSock (-1), _recvSock (-1),
+#endif
+ _destIP ("127.0.0.1"), _destPort (20000), _recvIP ("*"), _recvPort (20000), _open (false)
+{
+ _type = "udp";
+ ProcessOptions (options);
+
+#if defined (WIN32)
+ // First instance, initialise Windows sockets API
+ WSADATA info;
+ int result;
+ if ((result = WSAStartup (MAKEWORD (2, 2), &info)) != 0)
+ {
+ stringstream ss;
+ ss << "Failed to initialise Windows sockets API with error " << result;
+ throw PortException (ss.str ());
+ }
+#endif
+
+ if (_alwaysOpen)
+ Open ();
+}
+
+UDPPort::~UDPPort (void)
+{
+ Close ();
+
+#if defined (WIN32)
+ // Clean up the Windows sockets API
+ if (WSACleanup () != 0)
+ {
+ stringstream ss;
+ ss << "Failed to clean up Windows sockets API with error " << WSAGetLastError ();
+ throw PortException (ss.str ());
+ }
+#endif
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Port management
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+void UDPPort::Open (void)
+{
+ if (_open)
+ throw PortException ("Attempt to open already-opened port.");
+
+ OpenSender ();
+ OpenReceiver ();
+ SetPortBlockingFlag ();
+
+ _open = true;
+ if (_debug >= 2)
+ cerr << "UDPPort::" << __func__ << "() Port is open" << endl;
+}
+
+void UDPPort::Close (void)
+{
+ if (_debug >= 2)
+ cerr << "UDPPort::" << __func__ << "() Closing port" << endl;
+
+ _open = false;
+ CloseSender ();
+ CloseReceiver ();
+
+ if (_debug >= 2)
+ cerr << "UDPPort::" << __func__ << "() Port closed" << endl;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Read functions
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ssize_t UDPPort::Read (void * const buffer, size_t count)
+{
+ ssize_t receivedBytes = 0;
+
+ CheckPort (true);
+
+ if (_debug >= 2)
+ cerr << "UDPPort::" << __func__ << "() Going to read " << count << " bytes" << endl;
+
+ if (_timeout._sec != -1)
+ {
+ if (WaitForDataOrTimeout () == TIMED_OUT)
+ return -1;
+ }
+#if defined (WIN32)
+ receivedBytes = recv (_recvSock, reinterpret_cast<char*> (buffer), count, 0);
+#else
+ receivedBytes = recv (_recvSock, buffer, count, 0);
+#endif
+
+ if (_debug >= 2)
+ cerr << "UDPPort::" << __func__ << "() Read " << receivedBytes << " bytes" << endl;
+
+ if (receivedBytes < 0)
+ {
+ if (ErrNo () == ERRNO_EAGAIN)
+ return -1; // Timed out
+ else
+ {
+ // General error
+ stringstream ss;
+ ss << "UDPPort::" << __func__ << "() recv() error: (" << ErrNo () << ") " <<
+ StrError (ErrNo ());
+ throw PortException (ss.str ());
+ }
+ }
+ else if (receivedBytes == 0)
+ {
+ // Peer disconnected cleanly, do the same at this end
+ if (_debug >= 1)
+ cerr << "UDPPort::" << __func__ << "() Peer disconnected cleanly." << endl;
+ Close ();
+ if (_alwaysOpen)
+ {
+ if (_debug >= 1)
+ cerr << "UDPPort::" << __func__ << "() Trying to reconnect." << endl;
+ Open ();
+ }
+ return 0;
+ }
+
+ return receivedBytes;
+}
+
+ssize_t UDPPort::ReadFull (void * const buffer, size_t count)
+{
+ ssize_t numReceived = 0;
+ size_t receivedBytes = 0;
+
+ CheckPort (true);
+
+ if (_debug >= 2)
+ {
+ cerr << "UDPPort::" << __func__ << "() Going to read until have " <<
+ count << " bytes" << endl;
+ }
+
+ while (receivedBytes < count)
+ {
+#if defined (WIN32)
+ numReceived = recv (_recvSock, &(reinterpret_cast<char*> (buffer)[receivedBytes]),
+ count, 0); // No MSG_WAITALL on older versions of visual c, it seems
+#else
+ numReceived = recv (_recvSock, &(reinterpret_cast<char*> (buffer)[receivedBytes]),
+ count, MSG_WAITALL);
+#endif
+ if (_debug >= 2)
+ cerr << "UDPPort::" << __func__ << "() Received " << numReceived << " bytes" << endl;
+ if (numReceived < 0)
+ {
+ if (ErrNo () == ERRNO_EAGAIN)
+ {
+ // Timed out (which probably shouldn't happen)
+ throw PortException (string ("UDPPort::") + __func__ +
+ string ("() recv() timed out, probably shouldn't happen."));
+ }
+ else
+ {
+ // General error
+ stringstream ss;
+ ss << "UDPPort::" << __func__ << "() recv() error: (" << ErrNo () << ") " <<
+ StrError (ErrNo ());
+ throw PortException (ss.str ());
+ }
+ }
+ else if (numReceived == 0)
+ {
+ // Peer disconnected cleanly, do the same at this end
+ if (_debug >= 1)
+ cerr << "UDPPort::" << __func__ << "() Peer disconnected cleanly." << endl;
+ Close ();
+ if (_alwaysOpen)
+ {
+ if (_debug >= 1)
+ cerr << "UDPPort::" << __func__ << "() Trying to reconnect." << endl;
+ Open ();
+ // Can go around again after this - if it doesn't open successfully Open() will throw
+ }
+ else
+ {
+ throw PortException (string ("UDPPort::") + __func__ +
+ string ("() Port closed during read operation."));
+ }
+ }
+ else
+ receivedBytes += numReceived;
+ }
+
+ return receivedBytes;
+}
+
+ssize_t UDPPort::ReadUntil (void * const buffer, size_t count, uint8_t terminator)
+{
+ throw PortException (string ("UDPPort::") + __func__ +
+ string ("() This function does not work for datagram-oriented protocols."));
+ return 0;
+}
+
+ssize_t UDPPort::ReadStringUntil (std::string &buffer, char terminator)
+{
+ throw PortException (string ("UDPPort::") + __func__ +
+ string ("() This function does not work for datagram-oriented protocols."));
+ return 0;
+}
+
+ssize_t UDPPort::ReadLine (char * const buffer, size_t count)
+{
+ throw PortException (string ("UDPPort::") + __func__ +
+ string ("() This function does not work for datagram-oriented protocols."));
+ return 0;
+}
+
+ssize_t UDPPort::Skip (size_t count)
+{
+ throw PortException (string ("UDPPort::") + __func__ +
+ string ("() This function does not work for datagram-oriented protocols."));
+ return 0;
+}
+
+ssize_t UDPPort::SkipUntil (uint8_t terminator, unsigned int count)
+{
+ throw PortException (string ("UDPPort::") + __func__ +
+ string ("() This function does not work for datagram-oriented protocols."));
+ return 0;
+}
+
+ssize_t UDPPort::BytesAvailable (void)
+{
+ // TODO:
+ // MSG_PEEK is apparently bad on Windows so we should drain what we can into a local buffer
+ // instead, then use that first during read calls. See http://support.microsoft.com/kb/192599
+
+ CheckPort (true);
+
+ if (!IsDataAvailable ())
+ return 0;
+
+#if defined (WIN32)
+ unsigned long bytesAvailable = 0;
+ if (ioctlsocket (_recvSock, FIONREAD, &bytesAvailable) < 0)
+#else
+ ssize_t bytesAvailable = 0;
+ if (ioctl (_recvSock, FIONREAD, &bytesAvailable) < 0)
+#endif
+ {
+ stringstream ss;
+ ss << "UDPPort::" << __func__ << "() ioctl() error: (" << ErrNo () << ") " <<
+ StrError (ErrNo ());
+ throw PortException (ss.str ());
+ }
+
+ if (_debug >= 2)
+ {
+ cerr << "UDPPort::" << __func__ << "() Found " << bytesAvailable <<
+ " bytes available" << endl;
+ }
+ return bytesAvailable;
+}
+
+ssize_t UDPPort::BytesAvailableWait (void)
+{
+ CheckPort (true);
+
+ if (WaitForDataOrTimeout () == TIMED_OUT)
+ {
+ if (_debug >= 2)
+ {
+ cerr << "UDPPort::" << __func__ <<
+ " Timed out waiting for data to check bytes available" << endl;
+ }
+ if (IsBlocking ())
+ return -1; // Timeout in blocking mode
+ else
+ return 0; // No data in non-blocking mode
+ }
+
+ // TODO:
+ // MSG_PEEK is apparently bad on Windows so we should drain what we can into a local buffer
+ // instead, then use that first during read calls. See http://support.microsoft.com/kb/192599
+#if defined (WIN32)
+ unsigned long bytesAvailable = 0;
+ if (ioctlsocket (_recvSock, FIONREAD, &bytesAvailable) < 0)
+#else
+ ssize_t bytesAvailable = 0;
+ if (ioctl (_recvSock, FIONREAD, &bytesAvailable) < 0)
+#endif
+ {
+ stringstream ss;
+ ss << "UDPPort::" << __func__ << "() ioctl() error: (" << ErrNo () << ") " <<
+ StrError (ErrNo ());
+ throw PortException (ss.str ());
+ }
+
+ if (_debug >= 2)
+ {
+ cerr << "UDPPort::" << __func__ << "() Found " << bytesAvailable <<
+ " bytes available after waiting" << endl;
+ }
+ return bytesAvailable;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Write functions
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ssize_t UDPPort::Write (const void * const buffer, size_t count)
+{
+ ssize_t numSent = 0;
+
+ CheckPort (false);
+
+ if (_debug >= 2)
+ cerr << "UDPPort::" << __func__ << "() Writing " << count << " bytes" << endl;
+ if (_timeout._sec != -1)
+ {
+ if (WaitForWritableOrTimeout () == TIMED_OUT)
+ {
+ if (_debug >= 2)
+ cerr << "UDPPort::" << __func__ << "() Timed out waiting to send" << endl;
+ return -1;
+ }
+ }
+#if defined (WIN32)
+ if ((numSent = send (_sendSock, reinterpret_cast<const char*> (buffer), count, 0)) < 0)
+#else
+ if ((numSent = send (_sendSock, buffer, count, 0)) < 0)
+#endif
+ {
+ if (ErrNo () == ERRNO_EAGAIN)
+ {
+ if (_debug >= 2)
+ cerr << "UDPPort::" << __func__ << "() Timed out while in send()" << endl;
+ return -1; // Timed out
+ }
+ else
+ {
+ // General error
+ stringstream ss;
+ ss << "UDPPort::" << __func__ << "() send() error: (" << ErrNo () << ") " <<
+ StrError (ErrNo ());
+ throw PortException (ss.str ());
+ }
+ }
+
+ if (_debug >= 2)
+ cerr << "UDPPort::" << __func__ << "() Wrote " << numSent << " bytes" << endl;
+
+ return numSent;
+}
+
+void UDPPort::Flush (void)
+{
+ int numRead = 0;
+ char dump[128];
+
+ // Read data out of the socket into a dump until there's nothing left to read.
+ // Use MSG_DONTWAIT to avoid the timeout if one is set on Linux.
+ // It would be nice to use MSG_DONTWAIT on Windows, but MS didn't see fit to include that in
+ // their cramming of BSD sockets into Windows. Instead, check if data is available before
+ // calling recv.
+ do
+ {
+#if defined (WIN32)
+ if (!IsDataAvailable ())
+ break;
+ numRead = recv (_sendSock, dump, 128, 0);
+#else
+ numRead = recv (_sendSock, dump, 128, MSG_DONTWAIT);
+#endif
+ if (numRead < 0 && ErrNo () != ERRNO_EAGAIN)
+ {
+ stringstream ss;
+ ss << "UDPPort::" << __func__ << "() recv() error: (" << ErrNo () << ") " <<
+ StrError (ErrNo ());
+ throw PortException (ss.str ());
+ }
+ } while (numRead > 0);
+
+ // We can't do anything about the write buffers.
+}
+
+void UDPPort::Drain (void)
+{
+ // Since we can't force the write buffer to send, we can't do anything here.
+ if (_debug >= 1)
+ cerr << "UDPPort::" << __func__ << "() Can't drain output buffer of TCP port." << endl;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Other public API functions
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+std::string UDPPort::GetStatus (void) const
+{
+ stringstream status;
+
+ status << "UDP-specific status:" << endl;
+ status << "Destination address: " << _destIP << ":" << _destPort << endl;
+// if (_ip == "*")
+// {
+// if (_destAddr == NULL)
+// status << "No auto-configured destination found yet." << endl;
+// else
+// status << "Auto-configured destination: " << endl;
+// }
+ status << "Listening address: " << _recvIP << ":" << _recvPort << endl;
+ status << (_open ? "Port is open" : "Port is closed") << endl;
+
+ return Port::GetStatus () + status.str ();
+}
+
+void UDPPort::SetTimeout (Timeout timeout)
+{
+ _timeout = timeout;
+ SetPortBlockingFlag ();
+}
+
+void UDPPort::SetCanRead (bool canRead)
+{
+ if (canRead)
+ OpenReceiver ();
+ else
+ CloseReceiver ();
+ _canRead = canRead;
+}
+
+void UDPPort::SetCanWrite (bool canWrite)
+{
+ if (canWrite)
+ OpenSender ();
+ else
+ CloseSender ();
+ _canWrite = canWrite;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Internal functions
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+bool UDPPort::ProcessOption (const std::string &option, const std::string &value)
+{
+ char c = '\0';
+
+ // Check if the parent class can handle this option
+ if (Port::ProcessOption (option, value))
+ return true;
+
+ if (option == "dest_ip")
+ {
+ _destIP = value;
+ return true;
+ }
+ else if (option == "dest_port")
+ {
+ istringstream is (value);
+ if (!(is >> _destPort) || is.get (c) || _destPort == 0)
+ throw PortException ("Bad destination port number: " + value);
+ return true;
+ }
+ else if (option == "recv_ip")
+ {
+ _recvIP = value;
+ return true;
+ }
+ else if (option == "recv_port")
+ {
+ istringstream is (value);
+ if (!(is >> _recvPort) || is.get (c) || _recvPort == 0)
+ throw PortException ("Bad receive port number: " + value);
+ return true;
+ }
+
+ return false;
+}
+
+// Open the socket for sending data
+void UDPPort::OpenSender (void)
+{
+ CloseSender (); // To make sure
+
+ // If getaddrinfo() is available, much less stuff needs to be hard-coded or copied around.
+#if defined (FLEXIPORT_HAVE_GETADDRINFO)
+ struct addrinfo *res = NULL, hints;
+
+ memset (&hints, 0, sizeof (hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_protocol = IPPROTO_UDP;
+
+ int errorCode;
+ ostringstream portSS;
+ portSS << _destPort;
+ if ((errorCode = getaddrinfo (_destIP.c_str (), portSS.str ().c_str (), &hints, &res)) != 0)
+ {
+ CloseSender ();
+ stringstream ss;
+#if defined (WIN32)
+ ss << "UDPPort::" << __func__ << "() getaddrinfo() error: (" << ErrNo () << ") " <<
+ StrError (ErrNo ());
+#else
+ ss << "UDPPort::" << __func__ << "() getaddrinfo() error: (" << errorCode << ") " <<
+ gai_strerror (errorCode);
+#endif
+ throw PortException (ss.str ());
+ }
+
+ _sendSock = socket (res->ai_family, res->ai_socktype, res->ai_protocol);
+#if defined (WIN32)
+ if (_sendSock == INVALID_SOCKET)
+#else
+ if (_sendSock < 0)
+#endif
+ {
+ stringstream ss;
+ ss << "UDPPort::" << __func__ << "() socket() error: (" << ErrNo () << ") " <<
+ StrError (ErrNo ());
+ throw PortException (ss.str ());
+ }
+
+ if (_debug >= 1)
+ {
+ cerr << "UDPPort::" << __func__ << "() Connecting to " << _destIP << ":" << _destPort <<
+ "." << endl;
+ }
+ if (connect (_sendSock, res->ai_addr, res->ai_addrlen) < 0)
+ {
+ CloseSender ();
+ stringstream ss;
+ ss << "Failed to connect to " << _destIP << ": (" << ErrNo () << ") " << StrError (ErrNo ());
+ throw PortException (ss.str ());
+ }
+
+ freeaddrinfo (res);
+#else // defined (FLEXIPORT_HAVE_GETADDRINFO)
+ // Do it the ugly old way.
+ sockaddr_in sockAddr;
+ memset (&sockAddr, 0, sizeof (sockAddr));
+
+ _sendSock = socket (PF_INET, SOCK_DGRAM, 0);
+#if defined (WIN32)
+ if (_sendSock == INVALID_SOCKET)
+#else
+ if (_sendSock < 0)
+#endif
+ {
+ stringstream ss;
+ ss << "UDPPort::" << __func__ << "() socket() error: (" << ErrNo () << ") " <<
+ StrError (ErrNo ());
+ throw PortException (ss.str ());
+ }
+
+ struct hostent *hp = NULL;
+ if ((hp = gethostbyname (_destIP.c_str ())) == NULL)
+ {
+ CloseSender ();
+ throw PortException (string ("UDPPort::") + __func__ + string (" gethostbyname() error."));
+ }
+ memcpy (&sockAddr.sin_addr, hp->h_addr, hp->h_length);
+ sockAddr.sin_family = hp->h_addrtype;
+ sockAddr.sin_port = htons (_destPort);
+
+ if (_debug >= 1)
+ cerr << "UDPPort::" << __func__ << "() Connecting to " << _destIP << ":" << _port << "." << endl;
+ if (connect (_sendSock, reinterpret_cast<struct sockaddr*> (&sockAddr), sizeof (sockAddr)) < 0)
+ {
+ CloseSender ();
+ stringstream ss;
+ ss << "Failed to connect to " << _destIP << ": (" << ErrNo () << ") " << StrError (ErrNo ());
+ throw PortException (ss.str ());
+ }
+#endif // defined (FLEXIPORT_HAVE_GETADDRINFO)
+}
+
+// Close the socket for sending data
+void UDPPort::CloseSender (void)
+{
+#if defined (WIN32)
+ if (_sendSock != INVALID_SOCKET)
+ {
+ closesocket (_sendSock);
+ _sendSock = INVALID_SOCKET;
+ }
+#else
+ if (_sendSock >= 0)
+ {
+ close (_sendSock);
+ _sendSock = -1;
+ }
+#endif
+}
+
+// Open the socket for receiving data
+void UDPPort::OpenReceiver (void)
+{
+ CloseReceiver (); // To make sure
+
+#if defined (FLEXIPORT_HAVE_GETADDRINFO)
+ struct addrinfo *res = NULL, hints;
+ memset (&hints, 0, sizeof (hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_flags = AI_PASSIVE;
+
+ ostringstream portSS;
+ portSS << _recvPort;
+ int errorCode;
+ if (_recvIP == "*")
+ {
+ // Listen on all interfaces
+ if ((errorCode = getaddrinfo (NULL, portSS.str ().c_str (), &hints, &res)) != 0)
+ {
+ stringstream ss;
+#if defined (WIN32)
+ ss << "UDPPort::" << __func__ << "() getaddrinfo() error: (" << ErrNo () << ") " <<
+ StrError (ErrNo ());
+#else
+ ss << "UDPPort::" << __func__ << "() getaddrinfo() error: (" << errorCode << ") " <<
+ gai_strerror (errorCode);
+#endif
+ throw PortException (ss.str ());
+ }
+ }
+ else
+ {
+ // Listen on the specified interface only
+ if ((errorCode = getaddrinfo (_recvIP.c_str (), portSS.str ().c_str (), &hints, &res)) != 0)
+ {
+ stringstream ss;
+#if defined (WIN32)
+ ss << "UDPPort::" << __func__ << "() getaddrinfo() error: (" << ErrNo () << ") " <<
+ StrError (ErrNo ());
+#else
+ ss << "UDPPort::" << __func__ << "() getaddrinfo() error: (" << errorCode << ") " <<
+ gai_strerror (errorCode);
+#endif
+ throw PortException (ss.str ());
+ }
+ }
+
+ _recvSock = socket (res->ai_family, res->ai_socktype, res->ai_protocol);
+#if defined (WIN32)
+ if (_recvSock == INVALID_SOCKET)
+#else
+ if (_recvSock < 0)
+#endif
+ {
+ stringstream ss;
+ ss << "UDPPort::" << __func__ << "() socket() error: (" << ErrNo () << ") " <<
+ StrError (ErrNo ());
+ throw PortException (ss.str ());
+ }
+
+ if (bind (_recvSock, res->ai_addr, res->ai_addrlen) < 0)
+ {
+ CloseReceiver ();
+ stringstream ss;
+ ss << "UDPPort::" << __func__ << "() bind() error: (" << ErrNo () << ") " <<
+ StrError (ErrNo ());
+ throw PortException (ss.str ());
+ }
+
+ freeaddrinfo (res);
+#else // defined (FLEXIPORT_HAVE_GETADDRINFO)
+ char hostName[HOST_NAME_MAX + 1] = {'\0'};
+ struct hostent *hp = NULL;
+
+ _recvSock = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
+#if defined (WIN32)
+ if (_recvSock == INVALID_SOCKET)
+#else
+ if (_recvSock < 0)
+#endif
+ {
+ stringstream ss;
+ ss << "UDPPort::" << __func__ << "() socket() error: (" << ErrNo () << ") " <<
+ StrError (ErrNo ());
+ throw PortException (ss.str ());
+ }
+
+ sockaddr_in sockAddr;
+ memset (&sockAddr, 0, sizeof (sockAddr));
+ if (_recvIP == "*")
+ {
+ // Listen on all interfaces
+ if (gethostname (hostName, HOST_NAME_MAX) < 0)
+ {
+ {
+ stringstream ss;
+ ss << "UDPPort::" << __func__ << "() gethostname() error: (" << ErrNo () << ") " <<
+ StrError (ErrNo ());
+ throw PortException (ss.str ());
+ }
+ }
+ if ((hp = gethostbyname (hostName)) == NULL)
+ {
+ throw PortException (string ("UDPPort::") + __func__ +
+ string (" gethostbyname() error."));
+ }
+ sockAddr.sin_family = hp->h_addrtype;
+ sockAddr.sin_port = htons (_recvPort);
+ }
+ else
+ {
+ // Listen on the specified interface only
+ if ((hp = gethostbyname (_ip.c_str ())) == NULL)
+ {
+ throw PortException (string ("UDPPort::") + __func__ +
+ string (" gethostbyname() error."));
+ }
+ sockAddr.sin_family = hp->h_addrtype;
+ sockAddr.sin_port = htons (_recvPort);
+ }
+
+ if (bind (_recvSock, reinterpret_cast<struct sockaddr*> (&sockAddr), sizeof (sockAddr)) < 0)
+ {
+ CloseReceiver ();
+ stringstream ss;
+ ss << "UDPPort::" << __func__ << "() bind() error: (" << ErrNo () << ") " <<
+ StrError (ErrNo ());
+ throw PortException (ss.str ());
+ }
+#endif // defined (FLEXIPORT_HAVE_GETADDRINFO)
+
+ if (_debug >= 1)
+ cerr << "UDPPort::" << __func__ << "() Waiting for data." << endl;
+}
+
+// Close the socket for receiving data
+void UDPPort::CloseReceiver (void)
+{
+#if defined (WIN32)
+ if (_recvSock != INVALID_SOCKET)
+ {
+ closesocket (_recvSock);
+ _recvSock = INVALID_SOCKET;
+ }
+#else
+ if (_recvSock >= 0)
+ {
+ close (_recvSock);
+ _recvSock = -1;
+ }
+#endif
+}
+
+// Checks if data is available, waiting for the timeout if none is available immediatly
+UDPPort::WaitStatus UDPPort::WaitForDataOrTimeout (void)
+{
+ fd_set fdSet;
+ struct timeval tv, *tvPtr = NULL;
+
+ FD_ZERO (&fdSet);
+ FD_SET (_recvSock, &fdSet);
+ tv.tv_sec = _timeout._sec;
+ tv.tv_usec = _timeout._usec;
+ if (tv.tv_sec >= 0)
+ tvPtr = &tv;
+
+ int result = select (_recvSock + 1, &fdSet, NULL, NULL, tvPtr);
+
+ if (result < 0)
+ {
+ stringstream ss;
+ ss << "UDPPort::" << __func__ << "() select() error: (" << ErrNo () << ") " <<
+ StrError (ErrNo ());
+ throw PortException (ss.str ());
+ }
+ else if (result == 0)
+ {
+ if (_debug >= 3)
+ cerr << "UDPPort::" << __func__ << "() Timed out" << endl;
+ // Time out
+ return TIMED_OUT;
+ }
+ if (_debug >= 2)
+ cerr << "UDPPort::" << __func__ << "() Found data waiting" << endl;
+ return DATA_AVAILABLE;
+}
+
+// Checks if data is available right now
+bool UDPPort::IsDataAvailable (void)
+{
+ fd_set fdSet;
+ struct timeval tv;
+
+ FD_ZERO (&fdSet);
+ FD_SET (_recvSock, &fdSet);
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+
+ int result = select (_recvSock + 1, &fdSet, NULL, NULL, &tv);
+
+ if (result < 0)
+ {
+ stringstream ss;
+ ss << "UDPPort::" << __func__ << "() select() error: (" << ErrNo () << ") " <<
+ StrError (ErrNo ());
+ throw PortException (ss.str ());
+ }
+ else if (result == 0)
+ {
+ if (_debug >= 3)
+ cerr << "UDPPort::" << __func__ << "() Found no data waiting" << endl;
+ // No data
+ return false;
+ }
+ if (_debug >= 3)
+ cerr << "UDPPort::" << __func__ << "() Found data waiting" << endl;
+ return true;
+}
+
+// Checks it he port can be written to, waiting for the timeout if it can't be written immediatly
+UDPPort::WaitStatus UDPPort::WaitForWritableOrTimeout (void)
+{
+ fd_set fdSet;
+ struct timeval tv, *tvPtr = NULL;
+
+ FD_ZERO (&fdSet);
+ FD_SET (_sendSock, &fdSet);
+ tv.tv_sec = _timeout._sec;
+ tv.tv_usec = _timeout._usec;
+ if (tv.tv_sec >= 0)
+ tvPtr = &tv;
+
+ int result = select (_sendSock + 1, NULL, &fdSet, NULL, tvPtr);
+
+ if (result < 0)
+ {
+ stringstream ss;
+ ss << "UDPPort::" << __func__ << "() select() error: (" << ErrNo () << ") " <<
+ StrError (ErrNo ());
+ throw PortException (ss.str ());
+ }
+ else if (result == 0)
+ {
+ if (_debug >= 3)
+ cerr << "UDPPort::" << __func__ << "() Timed out" << endl;
+ // Time out
+ return TIMED_OUT;
+ }
+ if (_debug >= 3)
+ cerr << "UDPPort::" << __func__ << "() Found space to write" << endl;
+ return CAN_WRITE;
+}
+
+// Check if the port is open and if permissions are set correctly for the desired operation
+void UDPPort::CheckPort (bool read)
+{
+ if (!_open)
+ throw PortException ("Port is not open.");
+
+ if (read && !_canRead)
+ throw PortException ("Cannot read from write-only port.");
+
+ if (!read && !_canWrite)
+ throw PortException ("Cannot write to read-only port.");
+}
+
+void UDPPort::SetPortBlockingFlag (void)
+{
+ if (_timeout._sec == -1)
+ {
+ // Disable the non-blocking flag of the socket
+#if defined (WIN32)
+ unsigned long setting = 0;
+ // Receive socket
+ if (ioctlsocket (_recvSock, FIONBIO, &setting) == SOCKET_ERROR)
+ {
+ stringstream ss;
+ ss << "UDPPort::" << __func__ << "() ioctlsocket(_recvSock) error: (" << ErrNo () <<
+ ") " << StrError (ErrNo ());
+ throw PortException (ss.str ());
+ }
+
+ // Send socket
+ if (ioctlsocket (_sendSock, FIONBIO, &setting) == SOCKET_ERROR)
+ {
+ stringstream ss;
+ ss << "UDPPort::" << __func__ << "() ioctlsocket(_sendSock) error: (" << ErrNo () <<
+ ") " << StrError (ErrNo ());
+ throw PortException (ss.str ());
+ }
+#else
+ int flags;
+
+ // Receive socket
+ if ((flags = fcntl (_recvSock, F_GETFD)) < 0)
+ {
+ stringstream ss;
+ ss << "UDPPort::" << __func__ << "() fcntl(_recvSock, F_GETFD) error: (" << ErrNo () <<
+ ") " << StrError (ErrNo ());
+ throw PortException (ss.str ());
+ }
+ flags &= ~O_NONBLOCK;
+ if (fcntl (_recvSock, F_SETFD, flags) < 0)
+ {
+ stringstream ss;
+ ss << "UDPPort::" << __func__ << "() fcntl(_recvSock, F_SETFD) error: (" << ErrNo () <<
+ ") " << StrError (ErrNo ());
+ throw PortException (ss.str ());
+ }
+
+ // Send socket
+ if ((flags = fcntl (_sendSock, F_GETFD)) < 0)
+ {
+ stringstream ss;
+ ss << "UDPPort::" << __func__ << "() fcntl(_sendSock, F_GETFD) error: (" << ErrNo () <<
+ ") " << StrError (ErrNo ());
+ throw PortException (ss.str ());
+ }
+ flags &= ~O_NONBLOCK;
+ if (fcntl (_sendSock, F_SETFD, flags) < 0)
+ {
+ stringstream ss;
+ ss << "UDPPort::" << __func__ << "() fcntl(_sendSock, F_SETFD) error: (" << ErrNo () <<
+ ") " << StrError (ErrNo ());
+ throw PortException (ss.str ());
+ }
+#endif
+ }
+ else
+ {
+ // Set the socket to non-blocking, and we'll manage timeouts ourselves via select()
+#if defined (WIN32)
+ unsigned long setting = 1;
+ // Receive socket
+ if (ioctlsocket (_recvSock, FIONBIO, &setting) == SOCKET_ERROR)
+ {
+ stringstream ss;
+ ss << "UDPPort::" << __func__ << "() ioctlsocket(_recvSock) error: (" << ErrNo () <<
+ ") " << StrError (ErrNo ());
+ throw PortException (ss.str ());
+ }
+
+ // Send socket
+ if (ioctlsocket (_sendSock, FIONBIO, &setting) == SOCKET_ERROR)
+ {
+ stringstream ss;
+ ss << "UDPPort::" << __func__ << "() ioctlsocket(_sendSock) error: (" << ErrNo () <<
+ ") " << StrError (ErrNo ());
+ throw PortException (ss.str ());
+ }
+#else
+ int flags;
+
+ // Receive socket
+ if ((flags = fcntl (_recvSock, F_GETFD)) < 0)
+ {
+ stringstream ss;
+ ss << "UDPPort::" << __func__ << "() fcntl(F_GETFD) error: (" << ErrNo () << ") " <<
+ StrError (ErrNo ());
+ throw PortException (ss.str ());
+ }
+ flags |= O_NONBLOCK;
+ if (fcntl (_recvSock, F_SETFD, flags) < 0)
+ {
+ stringstream ss;
+ ss << "UDPPort::" << __func__ << "() fcntl(F_SETFD) error: (" << ErrNo () << ") " <<
+ StrError (ErrNo ());
+ throw PortException (ss.str ());
+ }
+
+ // Send socket
+ if ((flags = fcntl (_sendSock, F_GETFD)) < 0)
+ {
+ stringstream ss;
+ ss << "UDPPort::" << __func__ << "() fcntl(_sendSock, F_GETFD) error: (" << ErrNo () <<
+ ") " << StrError (ErrNo ());
+ throw PortException (ss.str ());
+ }
+ flags |= O_NONBLOCK;
+ if (fcntl (_sendSock, F_SETFD, flags) < 0)
+ {
+ stringstream ss;
+ ss << "UDPPort::" << __func__ << "() fcntl(_sendSock, F_SETFD) error: (" << ErrNo () <<
+ ") " << StrError (ErrNo ());
+ throw PortException (ss.str ());
+ }
+#endif
+ }
+}
+
+} // namespace flexiport
Added: gearbox/trunk/src/flexiport/udpport.h
===================================================================
--- gearbox/trunk/src/flexiport/udpport.h (rev 0)
+++ gearbox/trunk/src/flexiport/udpport.h 2008-10-14 02:53:40 UTC (rev 319)
@@ -0,0 +1,150 @@
+/*
+ * GearBox Project: Peer-Reviewed Open-Source Libraries for Robotics
+ * http://gearbox.sf.net/
+ * Copyright (c) 2008 Geoffrey Biggs
+ *
+ * flexiport flexible hardware data communications library.
+ *
+ * This distribution is licensed to you under the terms described in the LICENSE file included in
+ * this distribution.
+ *
+ * This work is a product of the National Institute of Advanced Industrial Science and Technology,
+ * Japan. Registration number: H20PRO-881
+ *
+ * This file is part of flexiport.
+ *
+ * flexiport is free software: you can redistribute it and/or modify it under the terms of the GNU
+ * Lesser General Public License as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * flexiport is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
+ * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with flexiport.
+ * If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __UDPPORT_H
+#define __UDPPORT_H
+
+#include "port.h"
+
+#include <map>
+#include <string>
+
+/** @ingroup gbx_library_flexiport
+@{
+*/
+
+namespace flexiport
+{
+...
[truncated message content] |