rosaria/Legacy/Aria/include/ArSocket.h
2021-12-16 14:07:59 +00:00

401 lines
14 KiB
C++

/*
Adept MobileRobots Robotics Interface for Applications (ARIA)
Copyright (C) 2004, 2005 ActivMedia Robotics LLC
Copyright (C) 2006, 2007, 2008, 2009, 2010 MobileRobots Inc.
Copyright (C) 2011, 2012, 2013 Adept Technology
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
If you wish to redistribute ARIA under different terms, contact
Adept MobileRobots for information about a commercial version of ARIA at
robots@mobilerobots.com or
Adept MobileRobots, 10 Columbia Drive, Amherst, NH 03031; +1-603-881-7960
*/
#ifndef ARSOCKET_H
#define ARSOCKET_H
#ifndef WIN32
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <stdio.h>
#include <errno.h>
#include <stdarg.h>
#endif
#include <string>
#include "ariaTypedefs.h"
#include "ArMutex.h"
#include "ariaUtil.h"
class ArFunctor;
/// socket communication wrapper
/**
ArSocket is a layer which allows you to use the sockets networking
interface in an operating system independent manner. All of the standard
commonly used socket functions are implemented such as open(), close(),
connect(), accept(), read(), write(), hostToNetOrder(), netToHostOrder(). ArSocket extends some of these functions to set useful options (see method documentation for details). It also provides additional useful functions like
writeString(), readString, setCloseCallback(), and more.
In Windows, the sockets subsystem needs to be initialized and shutdown
by the program. So when a program starts it must call Aria::init() and
call Aria::shutdown() when it exits. (Or, to only initialize the socket
system, and not do any other global Aria initialization, use ArSocket::init()
and ArSocket::shutdown().)
Some calls set an error code on failure in addition to returning false. This value is available from getError().
If getError() returns something other than NoErr, a text description of the error may be available from getErrorStr().
@sa @ref socketServerExample.cpp
@sa @ref socketClientExample.cpp
@ingroup UtilityClasses
*/
class ArSocket
{
public:
enum Type {UDP, TCP, Unknown};
enum Error {NoErr, NetFail, ConBadHost, ConNoRoute, ConRefused, NameLookup};
/// Constructor. You must then use either connect() or open().
AREXPORT ArSocket();
/// Constructor which immediately connects to a server as a client
/// @a host Hostname or IP address of remote server
/// @a port Port number on server
/// @a Which IP protocol to use, type ArSocket::TCP or ArSocket::UDP
AREXPORT ArSocket(const char *host, int port, Type type);
/// Constructor which immediately opens a port as a server
/// @a port Port number to open. Use a value greater than 1024.
/// @a doClose Automatically close the port when ArSocket is destroyed (recommend using true)
/// @a type Which IP protocol to use, ArSocket::TCP or ArSocket::UDP
AREXPORT ArSocket(int port, bool doClose, Type type);
/// Destructor
AREXPORT ~ArSocket();
/// Initialize the OS sockets system, if neccesary
AREXPORT static bool init();
/// Shutdown the OS sockets system, if neccesary
AREXPORT static void shutdown();
/// Converts the given socket type to a displayable text string (for debugging).
AREXPORT static const char *toString(Type t);
/** @internal */
AREXPORT static bool ourInitialized;
/// Copy internal socket structures
/// @internal
AREXPORT bool copy(int fd, bool doclose);
/// Copy socket
/// @internal
AREXPORT void copy(ArSocket *s)
{myFD=s->myFD; myDoClose=false; mySin=s->mySin;}
/// Transfer ownership of a socket
/** transfer() will transfer ownership to this socket. The input socket
will no longer close the file descriptor when it is destructed.
*/
AREXPORT void transfer(ArSocket *s)
{myFD=s->myFD; myDoClose=true; s->myDoClose=false; mySin=s->mySin;
myType=s->myType; strcpy(myRawIPString, s->myRawIPString);
setIPString(s->getIPString()); }
/// Connect as a client to a server
AREXPORT bool connect(const char *host, int port, Type type = TCP,
const char *openOnIP = NULL);
/** Open a server port
Opens a server port, that is, a port that is bound to a local port (and optionally, address)
and listens for new incoming connections. Use accept() to wait for a
new incoming connection from a client.
In additon, internally this method calls setLinger(0), setReuseAddress(),
and setNonBlock() to turn on these options (having these on is
particularly useful for servers).
@param port Port number
@param type ArSocket::TCP or ArSocket::UDP.
@param openOnIP If given, only bind to the interface
accociated with this address (Linux only) (by default, all interfaces are used)
*/
AREXPORT bool open(int port, Type type, const char *openOnIP = NULL);
/// Simply create a port.
AREXPORT bool create(Type type);
/** Find the first valid unused port after @a startPort, and bind the socket to it.
@param startPort first port to try
@param openOnIP If given, only check ports open on the interface accociated with this address (Linux only)
*/
AREXPORT bool findValidPort(int startPort, const char *openOnIP = NULL);
/// Connect the socket to the given address
AREXPORT bool connectTo(const char *host, int port);
/// Connect the socket to the given address
AREXPORT bool connectTo(struct sockaddr_in *sin);
/// Accept a new connection
AREXPORT bool accept(ArSocket *sock);
/// Close the socket
AREXPORT bool close();
/// Write data to the socket
AREXPORT int write(const void *buff, size_t len);
/// Read data from the socket
AREXPORT int read(void *buff, size_t len, unsigned int msWait = 0);
/// Send a message (short string) on the socket
AREXPORT int sendTo(const void *msg, int len);
/// Send a message (short string) on the socket
AREXPORT int sendTo(const void *msg, int len, struct sockaddr_in *sin);
/// Receive a message (short string) from the socket
AREXPORT int recvFrom(void *msg, int len, sockaddr_in *sin);
/// Convert a hostname string to an address structure
AREXPORT static bool hostAddr(const char *host, struct in_addr &addr);
/// Convert an address structure to a hostname string
AREXPORT static bool addrHost(struct in_addr &addr, char *host);
/// Get the localhost address
AREXPORT static std::string getHostName();
/// Get socket information (socket "name"). Aspects of this "name" are accessible with sockAddrIn(), inAddr(), inPort()
AREXPORT bool getSockName();
/// Accessor for the sockaddr
struct sockaddr_in * sockAddrIn() {return(&mySin);}
/// Accessor for the in_addr part of sockaddr
struct in_addr * inAddr() {return(&mySin.sin_addr);}
/// Accessor for the port of the sockaddr
unsigned short int inPort() {return(mySin.sin_port);}
/// Convert @a addr into string numerical address
AREXPORT static void inToA(struct in_addr *addr, char *buff);
/// Size of the sockaddr
static size_t sockAddrLen() {return(sizeof(struct sockaddr_in));}
#ifdef WIN32
/// Max host name length
static size_t maxHostNameLen() {return(MAXGETHOSTSTRUCT);}
#else
/// Max host name length
static size_t maxHostNameLen() {return(MAXHOSTNAMELEN);}
#endif
/// Convert an int from host byte order to network byte order
AREXPORT static unsigned int hostToNetOrder(int i);
/// Convert an int from network byte order to host byte order
AREXPORT static unsigned int netToHostOrder(int i);
/// Set the linger value
AREXPORT bool setLinger(int time);
/// Set broadcast value
AREXPORT bool setBroadcast();
/// Set the reuse address value
AREXPORT bool setReuseAddress();
/// Set socket to nonblocking. Most importantly, calls to read() will return immediately, even if no data is available.
AREXPORT bool setNonBlock();
/// Change the doClose value
AREXPORT void setDoClose(bool yesno) {myDoClose=yesno;}
/// Get the file descriptor
AREXPORT int getFD() const {return(myFD);}
/// Get the protocol type
AREXPORT Type getType() const {return(myType);}
/// Get a string containing a description of the last error. Only valid if getError() does not return NoErr.
AREXPORT const std::string & getErrorStr() const {return(myErrorStr);}
/// Get a code representing the last error
AREXPORT Error getError() const {return(myError);}
/// Sets whether we're error tracking or not
AREXPORT void setErrorTracking(bool errorTracking)
{ myErrorTracking = errorTracking; }
/// Gets whether we're doing error tracking or not
AREXPORT bool getErrorTracking(void) { return myErrorTracking; }
/// Gets if we've had a bad write (you have to use error tracking for this)
AREXPORT bool getBadWrite(void) const { return myBadWrite; }
/// Gets if we've had a bad read (you have to use error tracking for this)
AREXPORT bool getBadRead(void) const { return myBadRead; }
#ifndef SWIG
/** @brief Writes a string to the socket (adding end of line characters)
* @swigomit
* @sa writeStringPlain()
*/
AREXPORT int writeString(const char *str, ...);
#endif
/// Same as writeString(), but no varargs
AREXPORT int writeStringPlain(const char *str) { return writeString(str); }
/// Reads a string from the socket
AREXPORT char *readString(unsigned int msWait = 0);
/// Whether to ignore carriage return characters in readString or not
AREXPORT void setReadStringIgnoreReturn(bool ignore)
{ myStringIgnoreReturn = ignore; }
/// Clears the partial string read
AREXPORT void clearPartialReadString(void);
/// Compares a string against what was partially read
AREXPORT int comparePartialReadString(const char *partialString);
/// Gets the time we last successfully read a string from the socket
AREXPORT ArTime getLastStringReadTime(void) { return myLastStringReadTime; }
/// Sets echoing on the readString calls this socket does
AREXPORT void setEcho(bool echo)
{ myStringAutoEcho = false; myStringEcho = echo; }
/// Gets if we are echoing on the readString calls this socket does
AREXPORT bool getEcho(void) { return myStringEcho; }
/// Sets whether we log the writeStrings or not
AREXPORT void setLogWriteStrings(bool logWriteStrings)
{ myLogWriteStrings = logWriteStrings; }
/// Gets whether we log the writeStrings or not
AREXPORT bool getLogWriteStrings(void) { return myLogWriteStrings; }
/// Sets whether we use the wrong (legacy) end chars or not
AREXPORT void setStringUseWrongEndChars(bool useWrongEndChars)
{ myStringWrongEndChars = useWrongEndChars; }
/// Gets whether we log the writeStrings or not
AREXPORT bool getStringUseWrongEndChars(void)
{ return myStringWrongEndChars; }
/// Gets the raw ip number as a string
AREXPORT const char *getRawIPString(void) const { return myRawIPString; }
/// Gets the ip number as a string (this can be modified though)
AREXPORT const char *getIPString(void) const { return myIPString.c_str(); }
/// Sets the ip string
AREXPORT void setIPString(const char *ipString)
{ if (ipString != NULL) myIPString = ipString; else myIPString = ""; }
/// Sets the callback for when the socket is closed (nicely or harshly)
AREXPORT void setCloseCallback(ArFunctor *functor)
{ myCloseFunctor = functor; }
/// Sets the callback for when the socket is closed (nicely or harshly)
AREXPORT ArFunctor *getCloseCallback(void) { return myCloseFunctor; }
/// Gets the number of writes we've done
long getSends(void) { return mySends; }
/// Gets the number of bytes we've written
long getBytesSent(void) { return myBytesSent; }
/// Gets the number of reads we've done
long getRecvs(void) { return myRecvs; }
/// Gets the number of bytes we've read
long getBytesRecvd(void) { return myBytesRecvd; }
/// Resets the tracking information on the socket
void resetTracking(void)
{ mySends = 0; myBytesSent = 0; myRecvs = 0; myBytesRecvd = 0; }
/// Sets NODELAY option on TCP socket, which can reduce latency for small packet sizes.
AREXPORT bool setNoDelay(bool flag);
bool isOpen() { return myFD > 0; }
protected:
/// Sets the ip string
/// internal function that sets the ip string from the inAddr
void setRawIPString(void);
/// internal function that echos strings from read string
void doStringEcho(void);
// internal crossplatform init (mostly for string reading stuff)
void internalInit(void);
// separates out a host string (possibly host:port) into a host and
// the port that should be used.
void separateHost(const char *rawHost, int rawPort, char *useHost,
size_t useHostSize, int *port);
Type myType;
Error myError;
std::string myErrorStr;
bool myDoClose;
#ifdef WIN32
SOCKET myFD;
// Using the SOCKET data type mostly because this is what the Win32 methods
// return.
std::string myHost;
int myPort;
#else // Windows
int myFD;
#endif
bool myNonBlocking;
struct sockaddr_in mySin;
bool myLogWriteStrings;
ArMutex myReadStringMutex;
ArMutex myWriteStringMutex;
bool myStringAutoEcho;
bool myStringEcho;
bool myStringIgnoreReturn;
bool myStringWrongEndChars;
char myStringBuf[5000];
size_t myStringPos;
char myStringBufEmpty[1];
size_t myStringPosLast;
std::string myIPString;
char myRawIPString[128];
ArTime myLastStringReadTime;
bool myStringGotEscapeChars;
bool myStringGotComplete;
bool myStringHaveEchoed;
long mySends;
long myBytesSent;
long myRecvs;
long myBytesRecvd;
bool myBadWrite;
bool myBadRead;
bool myErrorTracking;
// A functor to call when the socket closes
ArFunctor *myCloseFunctor;
};
#endif // ARSOCKET_H