AgentXcpp  Revision:4ac4848
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 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      * Receiving %ResponsePDU's works as follows:
00090      *
00091      * The function wait_for_response() supports the request-response 
00092      * communication model. After sending a request to the remote entity (using 
00093      * send()), wait_for_response() is used to wait for the response. It blocks 
00094      * while waiting for a responsePDU from the remote entity, which is then 
00095      * returned to the caller. The wait_for_response() function may seem to do 
00096      * synchronous operations only, but this is not true.  In fact, it uses an 
00097      * asynchronous receive mechanism, because there may be other %PDU's in the 
00098      * queue before the given response is actually received.  
00099      * wait_for_response() therefore invokes io_service->run_one() one ore more 
00100      * times, until the response is received. This may also cause other 
00101      * asynchronous operations to finish.  For example, the registered handler 
00102      * object may receive other %PDU types, or another asynchronous operation 
00103      * on the io_service object (outside this class or even outside the 
00104      * agentXcpp library) may be served. Here are the steps performed to 
00105      * receive a ResponsePDU:
00106      *
00107      * - The wait_for_response() function puts an empty boost::shared_ptr<> 
00108      *   into the responses map, using the PacketID of the awaited ResponsePDU 
00109      *   as key.
00110      * - The wait_for_response() function invokes io_service->run_one() one or 
00111      *   several times, which triggers receive_callback() if data becomes 
00112      *   available.
00113      * - When the receive_callback() function is invoked, it receives a single 
00114      *   %PDU and processes it as described above.
00115      * - If a ResponsePDU is received, the registered handler object is not 
00116      *   informed by receive_callback(). Instead, the responses map is searched 
00117      *   for an entry with the same PacketID as the received ResponsePDU. If 
00118      *   found, the received ResponsePDU is stored in the map.  Otherwise the 
00119      *   ResponsePDU is silently discarded (as nobody waits for it). 
00120      * - The wait_for_response() checks its map entry after each run_one() call 
00121      *   for a received ResponsePDU. If it finds one, the entry is erased from 
00122      *   the map and returned to the caller.
00123      *
00124      * The same timeout value is used by all operations which deal with 
00125      * timeouts. The value is stored in the timeout member.
00126      */
00127     class connector
00128     {
00129         public:
00130 
00131             /**
00132              * \brief Interface for classes which can handle incoming PDU's.
00133              *
00134              * Classes which want to receive incoming %PDU's implement this 
00135              * interface. An object of type pdu_handler can then be registered 
00136              * with a connector object to indicate that it wants to receive 
00137              * %PDU's.
00138              */
00139             class pdu_handler
00140             {
00141                 public:
00142                     /**
00143                      * \brief Handler method for incoming %PDU's.
00144                      *
00145                      * When a %PDU is received by the connector class, this 
00146                      * method is called on the registered object. Note that 
00147                      * ResponsePDU's are not handed over to the registered 
00148                      * object.
00149                      */
00150                     virtual void handle_pdu(boost::shared_ptr<PDU>) =0;
00151             };
00152         
00153         private:
00154 
00155             /**
00156              * \brief The timeout in milliseconds, used in various contexts.
00157              */
00158             unsigned timeout;
00159 
00160             /**
00161              * \brief The mandatory io_service object.
00162              *
00163              * This object is needed for boost::asio sockets. It is provided by 
00164              * the user of this class.
00165              */
00166             boost::shared_ptr<boost::asio::io_service> io_service;
00167             
00168             /**
00169              * \brief The socket.
00170              *
00171              * The null pointer while disconnected.
00172              */
00173             boost::asio::local::stream_protocol::socket* socket;
00174 
00175             /**
00176              * \brief The endpoint used for unix domain sockets.
00177              */
00178             boost::asio::local::stream_protocol::endpoint endpoint;
00179             
00180             /**
00181              * \brief Callback function to receive a %PDU.
00182              *
00183              * See the class documentation to learn about the receive 
00184              * mechanism.
00185              *
00186              * \note Exceptions thrown by the user-provided handler (if any)
00187              *       are catched and discarded.
00188              * 
00189              * The synchronous read operation (to read the payload) may time 
00190              * out, using the class' timeout value. If the read times out, the 
00191              * socket is destroyed and the connector object becomes 
00192              * disconnected.
00193              *
00194              * \param result The result of the asynchronous read operation
00195              *               (provided by boost::asio).
00196              *
00197              * \exception None.
00198              */
00199             void receive_callback(const boost::system::error_code& result);
00200 
00201             /**
00202              * \brief The received, yet unprocessed ReponsePDU's.
00203              *
00204              * See the class documentation to learn about the receive 
00205              * mechanism.
00206              *
00207              * The wait_for_response() function stores a null pointer to this 
00208              * map to indicate that it is waiting for a certain response.
00209              *
00210              * When a response is received, the receive_callback() function 
00211              * stores it into the map, but only if a null pointer is found for 
00212              * the packetID of the received ResponsePDU. Otherwise, the 
00213              * received ResponsePDU is discarded.
00214              *
00215              * After a ResponsePDU was received and stored into the map, the 
00216              * wait_for_response() function processes it and erases it from the 
00217              * map.
00218              *
00219              * The map key is the packetID of the response which is awaited.
00220              */
00221             std::map< uint32_t, boost::shared_ptr<ResponsePDU> > responses;
00222 
00223             /**
00224              * \brief Buffer to receive a PDU header.
00225              *
00226              * See the class documentation to learn about the receive 
00227              * mechanism.
00228              *
00229              * The %PDU header is placed here before receive_callback() is 
00230              * called by boost::asio.
00231              *
00232              * Since the AgentX-header is always 20 bytes in length, this 
00233              * buffer is 20 bytes in size.
00234              */
00235             // TODO: avoid magic numbers, even if they are documented.
00236             byte_t header_buf[20];
00237 
00238             /**
00239              * \brief The handler object for incoming %PDU's.
00240              *
00241              * This handler object is informed by receive_callback() for each 
00242              * received PDU (except ResponsePDU's).
00243              *
00244              * The pointer may be null, which means that there is no handler 
00245              * object registered.
00246              */
00247             pdu_handler* handler;
00248 
00249             /**
00250              * \brief Hide standard constructor.
00251              *
00252              * We need an io_service object to function properly.
00253              */
00254             connector();
00255             
00256         public:
00257 
00258             /**
00259              * \brief The constructor
00260              *
00261              * This constructor initializes the connector object to be in 
00262              * disconnected state.
00263              *
00264              * \param io_service The io_service object needed for boost::asio
00265              *                   operations. It may also be used by other parts 
00266              *                   of the program.
00267              *
00268              * \param unix_domain_socket The path to the unix_domain_socket.
00269              *
00270              * \param timeout The timeout, in milliseconds, for sending and
00271              *                receiving %PDU's.  See the documentation of the 
00272              *                respective methods for details.
00273              *
00274              * \exception None.
00275              */
00276             connector(boost::shared_ptr<boost::asio::io_service> io_service,
00277                        const std::string& unix_domain_socket,
00278                        unsigned timeout);
00279 
00280             /**
00281              * \brief Wait with timeout for a reponse.
00282              *
00283              * This function blocks until a ResponsePDU with the given 
00284              * packetID is received or until the timeout expires, whichever 
00285              * comes first.  The received ResponsePDU (if any) is returned.
00286              *
00287              * This function calls run_one() repeatedly on the io_service 
00288              * object until the desired ResponsePDU arrives or the timeout 
00289              * expires.  This may cause other asynchronous operations to be 
00290              * served, as well.
00291              *
00292              * \param packetID The packetID to wait for.
00293              *
00294              * \exception timeout_exception If the timeout expired before the
00295              *                              ResponsePDU was received. The 
00296              *                              connector object stays in 
00297              *                              connected state.
00298              *
00299              * \exception disconnected If disconnected.  This is also thrown if
00300              *                         the operation fails and the object gets 
00301              *                         disconnected for that reason.
00302              *
00303              * \return The received ResponsePDU.
00304              */
00305             boost::shared_ptr<ResponsePDU>
00306                 wait_for_response(uint32_t packetID);
00307 
00308 
00309             /**
00310              * \brief Register a handler object for received %PDU's.
00311              *
00312              * Every time a %PDU is received, the handler object's handle_pdu() 
00313              * method will be invoked with the %PDU as argument. This method is 
00314              * executed in the context of the io_service object's run() or 
00315              * run_one() method.  Care should be taken to not block the call, 
00316              * e.g. by doing networking.
00317              *
00318              * After registering a handler it can be unregistered again by 
00319              * calling this function with a null pointer.  While no handler 
00320              * object is registered, received %PDU's are silently discarded.
00321              *
00322              * \note Any exceptions thrown by the handler object are silently
00323              *       discarded.
00324              *
00325              * \note There can be only one handler object registered at a time.
00326              *
00327              * \param handler A pointer to the handler object, or null.
00328              *
00329              * \exception None.
00330              */
00331             void register_handler( pdu_handler* );
00332 
00333             /**
00334              * \brief Connect to the remote entity.
00335              *
00336              * This function connects to the remote entity and starts receiving 
00337              * %PDU's.  If the object is already connected, the function does 
00338              * nothing.
00339              * 
00340              * \note While no handler is registered, received %PDU's are
00341              *       silently discarded.
00342              *
00343              * \exception disconnected If connecting fails.
00344              */
00345             void connect();
00346 
00347             /**
00348              * \brief Disconnect the remote entity.
00349              *
00350              * Stops receiving %PDU's and disconnects the remote entity.
00351              *
00352              * \exception None.
00353              */
00354             void disconnect();
00355 
00356             /**
00357              * \brief Find out whether the object is currently connected.
00358              *
00359              * \return Whether the object is in state connected.
00360              *
00361              * \exception None.
00362              */
00363             bool is_connected()
00364             {
00365                 return (this->socket != 0 ? true : false );
00366             }
00367 
00368             /**
00369              * \brief send a %PDU to the remote entity.
00370              *
00371              * The %PDU is sent to the remote entity. If the timeout expires 
00372              * during the send operation, a timeout_error is thrown and the 
00373              * object gets disconnected.
00374              *
00375              * \note The run_one() of the io_service object is called at least
00376              *       one time by this operation.
00377              *
00378              * \param pdu The PDU to send
00379              *
00380              * \exception timeout_error If a timeout error occurs. The function
00381              *                          uses the timeout value given during 
00382              *                          construction. The objects gets 
00383              *                          disconnected in this case. Some data 
00384              *                          might be sent already.
00385              *
00386              * \exception disconnected If disconnected. This is also thrown if
00387              *                         sending fails and the object gets 
00388              *                         disconnected for that reason. Some data 
00389              *                         might be sent already.
00390              */
00391             void send(const PDU& pdu);
00392 
00393             /**
00394              * \brief Destructor
00395              *
00396              * \exception None.
00397              */
00398             ~connector()
00399             {
00400                 // Disconnect if applicable. This also destroys the socket (if 
00401                 // any).
00402                 disconnect();
00403             }
00404 
00405     };
00406 
00407 }
00408 
00409 #endif
00410