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