AgentXcpp  Revision:0.1
Internals Documentation
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends
/home/tanjeff/projekte/agentxcpp/src/connector.hpp
Go to the documentation of this file.
00001 /*
00002  * Copyright 2011-2012 Tanjeff-Nicolai Moos <tanjeff@cccmz.de>
00003  *
00004  * This file is part of the agentXcpp library.
00005  *
00006  * AgentXcpp is free software: you can redistribute it and/or modify
00007  * it under the terms of the AgentXcpp library license, version 1, which 
00008  * consists of the GNU General Public License and some additional 
00009  * permissions.
00010  *
00011  * AgentXcpp is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * See the AgentXcpp library license in the LICENSE file of this package 
00017  * for more details.
00018  */
00019 #ifndef _LOCAL_SOCKET_H_
00020 #define _LOCAL_SOCKET_H_
00021 
00022 #include <boost/asio.hpp>
00023 
00024 #include "ResponsePDU.hpp"
00025 
00026 namespace agentxcpp
00027 {
00028     /**
00029      * \internal
00030      *
00031      * \brief This class provides connection to another agentXcpp entity via
00032      *        a unix domain socket.
00033      *
00034      * A connector object is always in one of the following states:
00035      *
00036      * -# connected
00037      * -# disconnected
00038      *
00039      * When created, a connector object starts in disconnected state. The 
00040      * current state can be obtained using the is_connected() method. Some 
00041      * operations may throw a disconnected exception if the object is in 
00042      * disconnected state.  Further, the connection may fail at any point in 
00043      * time, therefore a disconnected exception may also be thrown during a 
00044      * network operation.
00045      * 
00046      * The socket needed for networking is created upon connect and destroyed 
00047      * upon disconnect. The reason is that closing the socket may throw a 
00048      * system_error exception, leaving the socket in an unknown state.  
00049      * Destroying it and creating a new one should be safe.
00050      * 
00051      * Sending works as follows:
00052      *
00053      * - The user invokes the send() method.
00054      * - The send() method sends the %PDU synchronously (but with a timeout).
00055      *
00056      * Note: The send() function invokes io_service->run_one() one or several
00057      *       times.
00058      *
00059      * Receiving %PDU's works as follows:
00060      *
00061      * - Upon connecting to the remote entity, an asynchronous read operation 
00062      *   is started to receive the %PDU header (fixed size). On disconnect, the 
00063      *   asynchronous read operation is stopped again.
00064      * - The boost::asio library writes a fixed amount of data (which is the 
00065      *   %PDU header) into the header_buf member and invokes the
00066      *   receive_callback() function.
00067      * - The receive_callback() method reads the payload length from header_buf 
00068      *   and reveives the payload synchronously (but with a timeout).
00069      * - The receive_callback() method then constructs a PDU object and 
00070      *   delivers it to a user-provided handler object (which implements the 
00071      *   connector::pdu_handler interface) by calling its handle_pdu() 
00072      *   method.
00073      * - The handler object processes the %PDU as needed. As soon as processing 
00074      *   finishes, the receive_callback() method starts the next asynchronous 
00075      *   read operation, so that it invoked again when the next header arrives.
00076      *
00077      * \note As a special case, ResponsePDU's are not delivered to the
00078      *       registered handler object, but are handled differently. see below 
00079      *       for details.
00080      *
00081      * \note If no handler object is registered, the %PDU is received
00082      *       nevertheless and is silently discarded.
00083      *
00084      * \note The receive_callback() function, and thus the registered handler
00085      *       object's handle_pdu() method are executed in the io_service's 
00086      *       run() context.
00087      */
00088     /**
00089      * \internal
00090      *
00091      * Receiving %ResponsePDU's works as follows:
00092      *
00093      * The function wait_for_response() supports the request-response 
00094      * communication model. After sending a request to the remote entity (using 
00095      * send()), wait_for_response() is used to wait for the response. It blocks 
00096      * while waiting for a responsePDU from the remote entity, which is then 
00097      * returned to the caller. The wait_for_response() function may seem to do 
00098      * synchronous operations only, but this is not true.  In fact, it uses an 
00099      * asynchronous receive mechanism, because there may be other %PDU's in the 
00100      * queue before the given response is actually received.  
00101      * wait_for_response() therefore invokes io_service->run_one() one ore more 
00102      * times, until the response is received. This may also cause other 
00103      * asynchronous operations to finish.  For example, the registered handler 
00104      * object may receive other %PDU types, or another asynchronous operation 
00105      * on the io_service object (outside this class or even outside the 
00106      * agentXcpp library) may be served. Here are the steps performed to 
00107      * receive a ResponsePDU:
00108      *
00109      * - The wait_for_response() function puts an empty boost::shared_ptr<> 
00110      *   into the responses map, using the PacketID of the awaited ResponsePDU 
00111      *   as key.
00112      * - The wait_for_response() function invokes io_service->run_one() one or 
00113      *   several times, which triggers receive_callback() if data becomes 
00114      *   available.
00115      * - When the receive_callback() function is invoked, it receives a single 
00116      *   %PDU and processes it as described above.
00117      * - If a ResponsePDU is received, the registered handler object is not 
00118      *   informed by receive_callback(). Instead, the responses map is searched 
00119      *   for an entry with the same PacketID as the received ResponsePDU. If 
00120      *   found, the received ResponsePDU is stored in the map.  Otherwise the 
00121      *   ResponsePDU is silently discarded (as nobody waits for it). 
00122      * - The wait_for_response() checks its map entry after each run_one() call 
00123      *   for a received ResponsePDU. If it finds one, the entry is erased from 
00124      *   the map and returned to the caller.
00125      *
00126      * The same timeout value is used by all operations which deal with 
00127      * timeouts. The value is stored in the timeout member.
00128      */
00129     class connector
00130     {
00131         public:
00132 
00133             /**
00134              * \internal
00135              *
00136              * \brief Interface for classes which can handle incoming PDU's.
00137              *
00138              * Classes which want to receive incoming %PDU's implement this 
00139              * interface. An object of type pdu_handler can then be registered 
00140              * with a connector object to indicate that it wants to receive 
00141              * %PDU's.
00142              */
00143             class pdu_handler
00144             {
00145                 public:
00146                     /**
00147                      * \brief Handler method for incoming %PDU's.
00148                      *
00149                      * When a %PDU is received by the connector class, this 
00150                      * method is called on the registered object. Note that 
00151                      * ResponsePDU's are not handed over to the registered 
00152                      * object.
00153                      *
00154                      * The 'error' parameter is used to report errors. Using 
00155                      * exceptions is not possible because of the asynchronous 
00156                      * nature of the callback-mechanism.
00157                      *
00158                      * \param pdu The PDU which was just received. This is an
00159                      *            empty shared_ptr if an error occured.
00160                      *
00161                      * \param error The error code. 0 means success, -1 means
00162                      *              "parse error", -2 means "version error".
00163                      *
00164                      * \todo Check whether usage of boost::system makes
00165                      *       sense for error reporting.
00166                      */
00167                     virtual void handle_pdu(boost::shared_ptr<PDU> pdu,
00168                                             int error) =0;
00169 
00170                     /**
00171                      * \brief Destructor.
00172                      */
00173                     virtual ~pdu_handler()
00174                     {
00175                     }
00176             };
00177         
00178         private:
00179 
00180             /**
00181              * \brief The timeout in milliseconds, used in various contexts.
00182              */
00183             unsigned timeout;
00184 
00185             /**
00186              * \brief The mandatory io_service object.
00187              *
00188              * This object is needed for boost::asio sockets. It is provided by 
00189              * the user of this class.
00190              */
00191             boost::shared_ptr<boost::asio::io_service> io_service;
00192             
00193             /**
00194              * \brief The socket.
00195              *
00196              * The null pointer while disconnected.
00197              */
00198             boost::asio::local::stream_protocol::socket* socket;
00199 
00200             /**
00201              * \brief The endpoint used for unix domain sockets.
00202              */
00203             boost::asio::local::stream_protocol::endpoint endpoint;
00204             
00205             /**
00206              * \brief Callback function to receive a %PDU.
00207              *
00208              * See the class documentation to learn about the receive 
00209              * mechanism.
00210              *
00211              * \note Exceptions thrown by the user-provided handler (if any)
00212              *       are catched and discarded.
00213              * 
00214              * The synchronous read operation (to read the payload) may time 
00215              * out, using the class' timeout value. If the read times out, the 
00216              * socket is destroyed and the connector object becomes 
00217              * disconnected.
00218              *
00219              * \param result The result of the asynchronous read operation
00220              *               (provided by boost::asio).
00221              *
00222              * \exception None.
00223              */
00224             void receive_callback(const boost::system::error_code& result);
00225 
00226             /**
00227              * \brief The received, yet unprocessed ReponsePDU's.
00228              *
00229              * See the class documentation to learn about the receive 
00230              * mechanism.
00231              *
00232              * The wait_for_response() function stores a null pointer to this 
00233              * map to indicate that it is waiting for a certain response.
00234              *
00235              * When a response is received, the receive_callback() function 
00236              * stores it into the map, but only if a null pointer is found for 
00237              * the packetID of the received ResponsePDU. Otherwise, the 
00238              * received ResponsePDU is discarded.
00239              *
00240              * After a ResponsePDU was received and stored into the map, the 
00241              * wait_for_response() function processes it and erases it from the 
00242              * map.
00243              *
00244              * The map key is the packetID of the response which is awaited.
00245              */
00246             std::map< uint32_t, boost::shared_ptr<ResponsePDU> > responses;
00247 
00248             /**
00249              * \brief Buffer to receive a PDU header.
00250              *
00251              * See the class documentation to learn about the receive 
00252              * mechanism.
00253              *
00254              * The %PDU header is placed here before receive_callback() is 
00255              * called by boost::asio.
00256              *
00257              * Since the AgentX-header is always 20 bytes in length, this 
00258              * buffer is 20 bytes in size.
00259              */
00260             // TODO: avoid magic numbers, even if they are documented.
00261             byte_t header_buf[20];
00262 
00263             /**
00264              * \brief The handler object for incoming %PDU's.
00265              *
00266              * This handler object is informed by receive_callback() for each 
00267              * received PDU (except ResponsePDU's).
00268              *
00269              * The pointer may be null, which means that there is no handler 
00270              * object registered.
00271              */
00272             pdu_handler* handler;
00273 
00274             /**
00275              * \brief Hide standard constructor.
00276              *
00277              * We need an io_service object to function properly.
00278              */
00279             connector();
00280             
00281         public:
00282 
00283             /**
00284              * \brief The constructor
00285              *
00286              * This constructor initializes the connector object to be in 
00287              * disconnected state.
00288              *
00289              * \param io_service The io_service object needed for boost::asio
00290              *                   operations. It may also be used by other parts 
00291              *                   of the program.
00292              *
00293              * \param unix_domain_socket The path to the unix_domain_socket.
00294              *
00295              * \param timeout The timeout, in milliseconds, for sending and
00296              *                receiving %PDU's.  See the documentation of the 
00297              *                respective methods for details.
00298              *
00299              * \exception None.
00300              */
00301             connector(boost::shared_ptr<boost::asio::io_service> io_service,
00302                        const std::string& unix_domain_socket,
00303                        unsigned timeout);
00304 
00305             /**
00306              * \brief Wait with timeout for a reponse.
00307              *
00308              * This function blocks until a ResponsePDU with the given 
00309              * packetID is received or until the timeout expires, whichever 
00310              * comes first.  The received ResponsePDU (if any) is returned.
00311              *
00312              * This function calls run_one() repeatedly on the io_service 
00313              * object until the desired ResponsePDU arrives or the timeout 
00314              * expires.  This may cause other asynchronous operations to be 
00315              * served, as well.
00316              *
00317              * \param packetID The packetID to wait for.
00318              *
00319              * \exception timeout_exception If the timeout expired before the
00320              *                              ResponsePDU was received. The 
00321              *                              connector object stays in 
00322              *                              connected state.
00323              *
00324              * \exception disconnected If disconnected.  This is also thrown if
00325              *                         the operation fails and the object gets 
00326              *                         disconnected for that reason.
00327              *
00328              * \return The received ResponsePDU.
00329              */
00330             boost::shared_ptr<ResponsePDU>
00331                 wait_for_response(uint32_t packetID);
00332 
00333 
00334             /**
00335              * \brief Register a handler object for received %PDU's.
00336              *
00337              * Every time a %PDU is received, the handler object's handle_pdu() 
00338              * method will be invoked with the %PDU as argument. This method is 
00339              * executed in the context of the io_service object's run() or 
00340              * run_one() method.  Care should be taken to not block the call, 
00341              * e.g. by doing networking.
00342              *
00343              * After registering a handler it can be unregistered again by 
00344              * calling this function with a null pointer.  While no handler 
00345              * object is registered, received %PDU's are silently discarded.
00346              *
00347              * \note Any exceptions thrown by the handler object are silently
00348              *       discarded.
00349              *
00350              * \note There can be only one handler object registered at a time.
00351              *
00352              * \param handler A pointer to the handler object, or null.
00353              *
00354              * \exception None.
00355              */
00356             void register_handler( pdu_handler* handler );
00357 
00358             /**
00359              * \brief Connect to the remote entity.
00360              *
00361              * This function connects to the remote entity and starts receiving 
00362              * %PDU's.  If the object is already connected, the function does 
00363              * nothing.
00364              * 
00365              * \note While no handler is registered, received %PDU's are
00366              *       silently discarded.
00367              *
00368              * \exception disconnected If connecting fails.
00369              */
00370             void connect();
00371 
00372             /**
00373              * \brief Disconnect the remote entity.
00374              *
00375              * Stops receiving %PDU's and disconnects the remote entity.
00376              *
00377              * \exception None.
00378              */
00379             void disconnect();
00380 
00381             /**
00382              * \brief Find out whether the object is currently connected.
00383              *
00384              * \return Whether the object is in state connected.
00385              *
00386              * \exception None.
00387              */
00388             bool is_connected()
00389             {
00390                 return (this->socket != 0 ? true : false );
00391             }
00392 
00393             /**
00394              * \brief send a %PDU to the remote entity.
00395              *
00396              * The %PDU is sent to the remote entity. If the timeout expires 
00397              * during the send operation, a timeout_error is thrown and the 
00398              * object gets disconnected.
00399              *
00400              * \note The run_one() of the io_service object is called at least
00401              *       one time by this operation.
00402              *
00403              * \param pdu The PDU to send
00404              *
00405              * \exception timeout_error If a timeout error occurs. The function
00406              *                          uses the timeout value given during 
00407              *                          construction. The objects gets 
00408              *                          disconnected in this case. Some data 
00409              *                          might be sent already.
00410              *
00411              * \exception disconnected If disconnected. This is also thrown if
00412              *                         sending fails and the object gets 
00413              *                         disconnected for that reason. Some data 
00414              *                         might be sent already.
00415              */
00416             void send(const PDU& pdu);
00417 
00418             /**
00419              * \brief Destructor
00420              *
00421              * \exception None.
00422              */
00423             ~connector()
00424             {
00425                 // Disconnect if applicable. This also destroys the socket (if 
00426                 // any).
00427                 disconnect();
00428             }
00429 
00430     };
00431 
00432 }
00433 
00434 #endif
00435