/* 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 #include #include #include #include #include #include #include #include #include #include #include #endif #include #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