sockets.h

Go to the documentation of this file.
00001 /**
00002  *  Copyright (C) 2004-2005 Alo Sarv <madcat_@users.sourceforge.net>
00003  *
00004  *  This program is free software; you can redistribute it and/or modify
00005  *  it under the terms of the GNU General Public License as published by
00006  *  the Free Software Foundation; either version 2 of the License, or
00007  *  (at your option) any later version.
00008  *
00009  *  This program is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *  GNU General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU General Public License
00015  *  along with this program; if not, write to the Free Software
00016  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00017  */
00018 
00019 #ifndef __SOCKETS_H__
00020 #define __SOCKETS_H__
00021 
00022 /** @file sockets.h Interface for HydraNode Socket API */
00023 
00024 /**
00025  * @page netsubsys Networking Subsystem
00026  *
00027  * The Networking subsystem is a wrapper around OS-specific networking
00028  * (namely, Windows Sockets and BSD Sockets). The purpose is to completely
00029  * hide the OS-specific from users of these classes.
00030  *
00031  * Usage:
00032  *        Read the public interfaces of SocketClient and SocketServer. Really,
00033  *        thats all there's to it. Some things you should keep in mind tho:
00034  *
00035  *        Almost all public member functions of SocketServer/SocketClient
00036  *        classes may throw exceptions if things go wrong. Practice has shown
00037  *        that using return values to indicate errors doesn't get us anywhere,
00038  *        since everyone will simply ignore the return values. So there -
00039  *        now you can't ignore the errors anymore. So basically, get to writing
00040  *        try/catch blocks at all accesses to sockets. I know its tedious,
00041  *        but its safer than to have sockets around in odd/broken states and
00042  *        still "available" for usage.
00043  *
00044  * Internal stuff:
00045  *        Sockets register themselves with SocketWatcher, which performs
00046  *        sockets polling for events. When events are detected, the events
00047  *        are posted to event subsystem. Refer to SocketWatcher documentation
00048  *        and implementation for more information.
00049  *
00050  */
00051 
00052 #include <hn/event.h>
00053 #include <hn/ipv4addr.h>
00054 #include <hn/hnfwd.h>
00055 #include <boost/function.hpp>
00056 #include <boost/lexical_cast.hpp>
00057 #include <list>
00058 #include <map>
00059 #include <set>
00060 
00061 //! Typedef SOCKET depending on platform
00062 #ifdef WIN32
00063         typedef uint32_t SOCKET;
00064 #else
00065         typedef int SOCKET;
00066 #endif
00067 
00068 
00069 /**
00070  * Exception class
00071  */
00072 class DLLEXPORT SocketError : public std::runtime_error {
00073 public:
00074         SocketError(const std::string &err);
00075         ~SocketError() throw();
00076 };
00077 
00078 /**
00079  * Base class for Socket classes, all members are protected and destructor
00080  * is pure virtual - don't use this class directly
00081  */
00082 class DLLEXPORT SocketBase {
00083 public:
00084         //! Socket prioritites.
00085         enum SocketPriority {
00086                 PR_LOW = -1,
00087                 PR_NORMAL = 0,
00088                 PR_HIGH = 1
00089         };
00090 
00091         typedef SocketEvent EventType;
00092         typedef SocketClient AcceptType;
00093         typedef SocketPriority PriorityType;
00094 
00095         /**
00096          * Retrieve the internal SOCKET object
00097          */
00098         SOCKET getSocket() { return m_socket; }
00099 
00100         //! If this socket is marked for deletion
00101         bool toDelete() const { return m_toDelete; }
00102 
00103 
00104         /**
00105          * Set the priority of this socket. This affects it's scheduling policy,
00106          * higher-priority get handled before lower-priority sockets.
00107          *
00108          * @param prio     Priority
00109          */
00110         void setPriority(SocketPriority prio) { m_priority = prio; }
00111 
00112         /**
00113          * Get the priority of this socket
00114          */
00115         int8_t getPriority() const { return m_priority; }
00116 
00117 protected:
00118         /**
00119          * Base constructor
00120          */
00121         SocketBase();
00122 
00123         /**
00124          * Construct from existing socket.
00125          */
00126         SocketBase(SOCKET s);
00127 
00128         /**
00129          * Pure virtual destructor
00130          */
00131         virtual ~SocketBase() = 0;
00132 
00133         //! Derived classes must implement this method
00134         virtual void destroy() = 0;
00135 
00136         //! Internal socket object
00137         SOCKET m_socket;
00138 
00139         //! Initialized to false and set to true in destroy() method in
00140         //! derived classes, this indicates SocketWatcher must delete the
00141         //! object during removal.
00142         bool m_toDelete;
00143 
00144         //! Socket's priority
00145         int8_t m_priority;
00146 };
00147 
00148 // Event handlers
00149 typedef boost::function<void (SocketClient*, SocketEvent)> SCEventHandler;
00150 typedef boost::function<void (SocketServer*, SocketEvent)> SSEventHandler;
00151 typedef boost::function<void (UDPSocket*   , SocketEvent)> UDPSEventHandler;
00152 
00153 /**
00154  * Socket client is a "connection" that is used to transfer data between
00155  * two peers.
00156  */
00157 class DLLEXPORT SocketClient : public SocketBase {
00158 public:
00159         DECLARE_EVENT_TABLE(SocketClient*, SocketEvent);
00160 
00161         //! Type of event handler used by this object
00162         typedef SCEventHandler HandlerType;
00163 
00164         /**
00165          * Constructor
00166          *
00167          * @param ehandler   Optional handler for socket events
00168          */
00169         SocketClient(SCEventHandler ehandler = 0);
00170 
00171         /**
00172          * Destroy this socket. It is safe to call this method multiple times.
00173          */
00174         void destroy();
00175 
00176         /**
00177          * Write data into socket
00178          *
00179          * @param buffer    Pointer to buffer of data to be written.
00180          * @param length    Amount of data to send.
00181          * @return          Number of bytes written to socket.
00182          *
00183          * \throws SocketError if something goes wrong.
00184          *
00185          * \note The socket API does not take ownership of the pointer passed
00186          *       to this function. It attempts to send out as much data as is
00187          *       possible at this moment, but does not buffer anything. Client
00188          *       code is responsible for freeing the memory pointed to by the
00189          *       buffer pointer.
00190          * \note It is recommended to use SSocket API, which implements internal
00191          *       buffering and is safer to use than this raw API.
00192          */
00193         uint32_t  write(const char *buffer, uint32_t length);
00194 
00195         /**
00196          * Read data from socket
00197          *
00198          * @param buffer     Buffer into what to place the read data.
00199          * @param length     Size of buffer, amount of data to be read.
00200          * @return           Number of bytes actually read.
00201          *
00202          * \throws SocketError if something goes wrong.
00203          */
00204         uint32_t read(void *buffer, uint32_t length);
00205 
00206         /**
00207          * Make an outgoing connection.
00208          *
00209          * @param addr     Address to connect to.
00210          * @param timeout   Time in milliseconds to attempt to connect. Once the
00211          *                  timeout passes, SOCK_ETIMEOUT event will be posted
00212          *                  if the connection attempt hasn't been completed
00213          *                  to that point. Defaults to 5 seconds.
00214          *
00215          * Preconditions: addr.m_port != 0, addr.m_ip != 0
00216          * \throws SocketError if something goes wrong.
00217          */
00218         void connect(IPV4Address addr, uint32_t timeout = 5000);
00219 
00220         /**
00221          * Close the connection
00222          *
00223          * \throws SocketError if something goes wrong.
00224          */
00225         void disconnect();
00226 
00227         /**
00228          * @name Accessors for various internal variables
00229          */
00230         //@{
00231         bool isConnected()  const { return m_connected;  }
00232         bool isOk()         const { return !m_erronous;  }
00233         bool isConnecting() const { return m_connecting; }
00234         bool isWritable()   const { return m_writable;   }
00235         //@}
00236 
00237         /**
00238          * Set timeout; if no events happen within this timeout, SOCK_TIMEOUT
00239          * is emitted.
00240          *
00241          * @param t      Timeout, in milliseconds
00242          */
00243         void setTimeout(uint32_t t) { m_timeout = Utils::getTick() + t; }
00244 
00245         /**
00246          * \returns The address this socket is connected to.
00247          */
00248         IPV4Address getPeer() const { return m_peer; }
00249 
00250         /**
00251          * \returns The local address this socket is bound to
00252          * \note When *this is disconnected, this returns 0, however, if *this
00253          *       is connected, it returns our external IP address if we are
00254          *       connected to the net directly; when we are behind a gateway
00255          *       though, this returns our intranet address, so don't rely on
00256          *       this giving you our external IP always (altough it's worth a
00257          *       shot).
00258          */
00259         IPV4Address getAddr() const;
00260 
00261         /**
00262          * Input operator; writes the specified data into socket
00263          */
00264         friend SocketClient& operator<<(
00265                 SocketClient &c, const std::string &data
00266         ) {
00267                 c.write(data.data(), data.size());
00268                 return c;
00269         }
00270 
00271         /**
00272          * Extractor operator; reads all current data from socket to buffer
00273          */
00274         friend SocketClient& operator>>(SocketClient &c, std::string &buf) {
00275                 char tmp[1024];
00276                 int got = 0;
00277                 while ((got = c.read(tmp, 1024))) {
00278                         buf.append(std::string(tmp, got));
00279                 }
00280                 return c;
00281         }
00282 private:
00283         friend class SocketWatcher;
00284         friend class SocketServer;
00285 
00286         /**
00287          * Construct from existing SOCKET object
00288          */
00289         SocketClient(SOCKET s);
00290 
00291         /**
00292          * Destructor hidden, use destroy() method instead.
00293          */
00294         virtual ~SocketClient();
00295 
00296         bool m_connected;           //!< If the socket is connected
00297         bool m_hasData;             //!< If there is incoming data to be read
00298         bool m_connecting;          //!< If a connection attempt is in progress
00299         bool m_erronous;            //!< If the socket is erronous
00300         bool m_writable;            //!< If this socket is writable
00301         uint64_t m_timeout;         //!< Timeout counter for connection attempts
00302         IPV4Address m_peer;         //!< Peer to where we are connected to
00303 
00304         void setPeerName();         //!< Update m_peer member
00305         void close();               //!< Closes the underlying socket
00306 };
00307 
00308 /**
00309  * A listening socket server which accepts incoming connections (as
00310  * SocketClient objects).
00311  */
00312 class DLLEXPORT SocketServer : public SocketBase {
00313 public:
00314         DECLARE_EVENT_TABLE(SocketServer*, SocketEvent);
00315 
00316         //! Type of event handler used by this object
00317         typedef SSEventHandler HandlerType;
00318 
00319         /**
00320          * Constructor
00321          *
00322          * @param ehandler    Callback function for events.
00323          */
00324         SocketServer(SSEventHandler ehandler = 0);
00325 
00326         /**
00327          * Destroy this socket. It is safe to call this method multiple times.
00328          */
00329         void destroy();
00330 
00331         /**
00332          * Accept an incoming connection
00333          *
00334          * @param ehandler     Callback function for events for the accepted
00335          *                     socket.
00336          * @return             Accepted connection. May be NULL if accept()
00337          *                     fails.
00338          */
00339         SocketClient* accept(SCEventHandler ehandler = 0);
00340 
00341         /**
00342          * Puts the server into listening state.
00343          *
00344          * @param addr     Address to listen on.
00345          *
00346          * \note If addr.m_ip == 0, the listener will listen on all available
00347          * networks.
00348          */
00349         void listen(IPV4Address addr);
00350 
00351         /**
00352          * @name Accessors for various internal variables
00353          */
00354         //@{
00355         bool isListening() { return m_listening; }
00356         bool hasIncoming() { return m_incoming; }
00357         //@}
00358 
00359         /**
00360          * @returns The local address the server.
00361          */
00362         IPV4Address getAddr() const { return m_addr; }
00363 
00364         /**
00365          * Helper method for Scheduler API; SocketServer's are never in
00366          * connected state.
00367          */
00368         bool isConnected() const { return false; }
00369 private:
00370         /**
00371          * Destructor
00372          */
00373         virtual ~SocketServer();
00374 
00375         friend class SocketWatcher;
00376         bool m_incoming;                    //!< Incoming connection is waiting
00377         bool m_listening;                   //!< If the socket is listening
00378         bool m_erronous;                    //!< If the socket is erronous
00379         IPV4Address m_addr;                 //!< Address where we are listening
00380 
00381         void close();               //!< Closes the underlying socket
00382 };
00383 
00384 /**
00385  * UDP socket is connection-less socket, using the UDP protocol.
00386  * Usage:
00387  * Construct UDPSocket object on heap and call listen() member function to
00388  * start up the socket. After that, when incoming data is detected, events are
00389  * emitted, and data may be received using recv() member function. Where the
00390  * data originated will be written into the passed IPV4Address parameter. To
00391  * send data to a specific location, send() method may be used, where the last
00392  * parameter indicates where to send the data.
00393  *
00394  * \note UDP sockets are not reliable. There is no way of knowing whether the
00395  *       data sent out actually reached the recipient.
00396  */
00397 class DLLEXPORT UDPSocket : public SocketBase {
00398 public:
00399         DECLARE_EVENT_TABLE(UDPSocket*, SocketEvent);
00400 
00401         //! Type of event handler used by this object
00402         typedef UDPSEventHandler HandlerType;
00403 
00404         //! Construct new UDP socket, optionally setting handler
00405         UDPSocket(UDPSEventHandler handler = 0);
00406 
00407         /**
00408          * Destroy this socket. It is safe to call this method multiple times.
00409          */
00410         void destroy();
00411 
00412         /**
00413          * Starting listening on a local address. If addr.ip is 0, we will
00414          * listen on all available addresses. Addr.port may not be 0.
00415          *
00416          * @param addr       Address to listen on
00417          */
00418         void listen(IPV4Address addr);
00419 
00420         //! Provided for convenience
00421         void listen(uint32_t addr, uint16_t port) {
00422                 listen(IPV4Address(addr, port));
00423         }
00424         //! Provided for convenience
00425         void listen(const std::string &addr, uint16_t port) {
00426                 listen(IPV4Address(addr, port));
00427         }
00428 
00429         /**
00430          * Receive data from a location
00431          *
00432          * @param buf      Buffer to write the received data
00433          * @param len      Length of the buffer
00434          * @param from     Receives the data source address
00435          * @return         The amount of data actually received
00436          */
00437         uint32_t recv(char *buf, uint32_t len, IPV4Address *from);
00438 
00439         /**
00440          * Send data to a specific location
00441          *
00442          * @param data     Data to be sent out
00443          * @param to       Address to send the data to
00444          * @return         Amount of data actually written to socket
00445          */
00446         uint32_t send(const std::string &data, IPV4Address to);
00447 
00448         //! If the socket is in listening state
00449         bool isListening() const { return m_listening; }
00450         //! If the socket has pending incoming data
00451         bool hasData()     const { return m_hasData; }
00452 
00453         /**
00454          * Helper for Scheduler API; UDPSockets are never in "connected"
00455          * state, e.g. they are not counted towards connection-counts.
00456          */
00457         bool isConnected() const { return false; }
00458 
00459         /**
00460          * \returns The local address of this socket
00461          */
00462         IPV4Address getAddr() const { return m_addr; }
00463 private:
00464         //! Destructor
00465         virtual ~UDPSocket();
00466 
00467         friend class SocketWatcher;
00468         bool m_listening;             //!< Indicates whether we are listening
00469         bool m_hasData;               //!< Indicates the presence of data
00470         bool m_erronous;              //!< Indicates erronous status
00471         IPV4Address m_addr;           //!< Local address
00472 };
00473 
00474 /**
00475  * SocketWatcher class keeps a list of active sockets, performs checking
00476  * them for events (in DoPoll() member function), as well as events posting
00477  * to Event Subsystem when there are events in sockets.
00478  */
00479 class SocketWatcher {
00480 public:
00481         //! Initialize socket API
00482         static bool initialize();
00483 
00484         /**
00485          * @name Static public accessors for adding/removing sockets
00486          */
00487         //@{
00488         template<typename T>
00489         static void addSocket(T* socket) {
00490                 instance().doAddSocket(socket);
00491         }
00492         template<typename T>
00493         static void removeSocket(T* socket) {
00494                 instance().doRemoveSocket(socket);
00495         }
00496         //@}
00497 
00498         //! Called from application main loop - perform sockets polling
00499         static void poll() {
00500                 instance().doPoll();
00501         }
00502 
00503         //! Access to the Singleton object of this class
00504         static SocketWatcher& instance();
00505 private:
00506         SocketWatcher();
00507         ~SocketWatcher();
00508         SocketWatcher(const SocketWatcher&);
00509         SocketWatcher& operator=(const SocketWatcher&);
00510 
00511         /**
00512          * @name Implementation of accessors
00513          */
00514         //@{
00515         void doAddSocket(SocketClient *client);
00516         void doAddSocket(SocketServer *socket);
00517         void doAddSocket(UDPSocket *socket);
00518         void doRemoveSocket(SocketClient *client);
00519         void doRemoveSocket(SocketServer *server);
00520         void doRemoveSocket(UDPSocket *socket);
00521         //@}
00522 
00523         //! Perform the actual polling
00524         void doPoll();
00525 
00526         std::set<SOCKET> m_sockets;                    //!< Set of sockets
00527         std::map<SOCKET, SocketClient*> m_clients;     //!< Map of clients
00528         std::map<SOCKET, SocketServer*> m_servers;     //!< Map of servers
00529         std::map<SOCKET, UDPSocket*   > m_udpSockets;  //!< Map of UDP sockets
00530 
00531         typedef std::set<SOCKET>::iterator SIter;
00532         typedef std::map<SOCKET, SocketClient*>::iterator SCIter;
00533         typedef std::map<SOCKET, SocketServer*>::iterator SSIter;
00534         typedef std::map<SOCKET, UDPSocket*   >::iterator SUIter;
00535 
00536         /**
00537          * @name Internal helper functions for posting events.
00538          */
00539         //@{
00540         void handleReadableSocket(SOCKET sock);
00541         void handleWritableSocket(SOCKET sock);
00542         void handleErronousSocket(SOCKET sock);
00543         //@}
00544 
00545         /**
00546          * Temporary containers for sockets pending removal. These are cleared,
00547          * and removed from real containers in cleanupSockets() method.
00548          */
00549         //@{
00550         std::vector<SocketClient*> m_clientsToRemove;
00551         std::vector<SocketServer*> m_serversToRemove;
00552         std::vector<UDPSocket*   > m_udpToRemove;
00553         //@}
00554 
00555         /**
00556          * Cleans up sockets, removing all sockets pending removal from internal
00557          * data structures.
00558          */
00559         void cleanupSockets();
00560 };
00561 
00562 //! Useful things related to sockets
00563 namespace Socket {
00564 /**
00565  * Line ending object, similar to std::endl. When dealing with plain-text
00566  * protocols over sockets, \r\n is used to indicate newline.
00567  * Usage: <pre>
00568  * SocketClient *c;
00569  * *c << "Message" << Socket::Endl;
00570  * </pre>
00571  */
00572 extern DLLEXPORT class _Endl {
00573 public:
00574         //! Output operator to Sockets
00575         operator std::string() {
00576                 return "\r\n";
00577         }
00578         //! Comparison operator to check if string is line end
00579         inline friend bool operator==(const std::string &s, const _Endl&) {
00580                 return s == "\r\n";
00581         }
00582 } Endl;
00583 } //! Namespace Socket
00584 
00585 #endif