TOBI Interface A  0.1
/home/breidi/Dropbox/libtia/src/tia/network/control_connection.cpp
Go to the documentation of this file.
00001 /*
00002     This file is part of the TOBI Interface A (TiA) library.
00003 
00004     Commercial Usage
00005     Licensees holding valid Graz University of Technology Commercial
00006     licenses may use this file in accordance with the Graz University
00007     of Technology Commercial License Agreement provided with the
00008     Software or, alternatively, in accordance with the terms contained in
00009     a written agreement between you and Graz University of Technology.
00010 
00011     --------------------------------------------------
00012 
00013     GNU Lesser General Public License Usage
00014     Alternatively, this file may be used under the terms of the GNU Lesser
00015     General Public License version 3.0 as published by the Free Software
00016     Foundation and appearing in the file lgpl.txt included in the
00017     packaging of this file.  Please review the following information to
00018     ensure the GNU General Public License version 3.0 requirements will be
00019     met: http://www.gnu.org/copyleft/lgpl.html.
00020 
00021     In case of GNU Lesser General Public License Usage ,the TiA library
00022     is distributed in the hope that it will be useful,
00023     but WITHOUT ANY WARRANTY; without even the implied warranty of
00024     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00025     GNU General Public License for more details.
00026 
00027     You should have received a copy of the GNU Lesser General Public License
00028     along with the TiA library. If not, see <http://www.gnu.org/licenses/>.
00029 
00030     Copyright 2010 Graz University of Technology
00031     Contact: TiA@tobi-project.org
00032 */
00033 
00039 // Boost
00040 #include <boost/thread.hpp>
00041 #include <boost/bind.hpp>
00042 #include <boost/iostreams/stream.hpp>
00043 
00044 // local
00045 #include "tia/constants.h"
00046 
00047 #include "tia-private/config/control_message_decoder.h"
00048 #include "tia-private/config/control_message_encoder.h"
00049 
00050 #include "tia-private/network/control_connection.h"
00051 #include "tia-private/network/control_connection_server.h"
00052 #include "tia-private/network/tcp_data_server.h"
00053 #include "tia-private/network/udp_data_server.h"
00054 
00055 namespace tia
00056 {
00057 using std::cout;
00058 using std::cerr;
00059 using std::endl;
00060 using std::istream;
00061 
00062 using boost::uint16_t;
00063 using boost::uint32_t;
00064 
00065 //-----------------------------------------------------------------------------
00066 
00067 ControlConnection::ControlConnection(boost::asio::io_service& io_service,
00068     const ConnectionID& id,
00069     ControlConnectionServer& ctl_conn_server,
00070     const TCPConnection::pointer& tcp_conn)
00071       : io_service_(io_service),
00072       connection_id_(id),
00073       ctl_conn_server_(ctl_conn_server),
00074       tcp_connection_(tcp_conn),
00075       msg_encoder_(0),
00076       msg_decoder_(0),
00077       state_(State_Connected),
00078       connection_type_(GetDataConnectionMsg::Tcp)
00079 {
00080   input_buffer_   = new boost::asio::streambuf;
00081   output_buffer_  = new boost::asio::streambuf;
00082 
00083   msg_encoder_ = new ControlMsgEncoderXML;
00084   msg_decoder_ = new ControlMsgDecoderXML;
00085 
00086   config_msg_ = boost::shared_ptr<ConfigMsg>(new ConfigMsg);
00087   ctl_conn_server.getConfig(*config_msg_);
00088 }
00089 
00090 //-----------------------------------------------------------------------------
00091 
00092 ControlConnection::~ControlConnection()
00093 {
00094   delete input_buffer_;
00095   delete output_buffer_;
00096   delete msg_encoder_;
00097   delete msg_decoder_;
00098 }
00099 
00100 //-----------------------------------------------------------------------------
00101 
00102 void ControlConnection::start()
00103 {
00104   tcp_connection_->socket().async_read_some(input_buffer_->prepare(MAX_DATA_SIZE),
00105       boost::bind(&ControlConnection::handle_read, shared_from_this(),
00106         boost::asio::placeholders::error,
00107         boost::asio::placeholders::bytes_transferred));
00108 }
00109 
00110 //-----------------------------------------------------------------------------
00111 
00112 void ControlConnection::handle_read(const boost::system::error_code& error,
00113     std::size_t bytes_transferred)
00114 {
00115   if (error)
00116   {
00117     if (error != boost::asio::error::eof)
00118     {
00119       cerr << "ControlConnection::handle_read [Client @" << connection_id_.second << "]: "
00120            << "closing connection -- Error: "
00121            << "--> " << error.message() << endl;
00122     }
00123 
00124     close();
00125     return;
00126   }
00127 
00128   input_buffer_->commit(bytes_transferred);
00129 
00130   istream instream(input_buffer_);
00131 
00132   msg_decoder_->setInputStream(&instream);
00133 
00134   boost::shared_ptr<ControlMsg> msg(msg_decoder_->decodeMsg());
00135 
00136   if (msg != 0)
00137   {
00138     switch (msg->msgType())
00139     {
00140       case ControlMsg::KeepAlive:
00141       {
00142         // TODO:
00143         break;
00144       }
00145 
00146       case ControlMsg::GetConfig:
00147       {
00148         cout << "Client @" << connection_id_.second
00149              << " requests config" << endl;
00150 
00151         sendMsg(*config_msg_);
00152 
00153         break;
00154       }
00155 
00156       case ControlMsg::GetDataConnection:
00157       {
00158         #ifdef DEBUG
00159           cout << "ControlConnection::handle_read [Client @" << connection_id_.second << "]: "
00160                << "Got Request 'GetDataConnection'" << endl;
00161         #endif
00162 
00163         if (state_ == State_TransmissionStarted)
00164         {
00165           cout << "ControlConnection::handle_read [Client @" << connection_id_.second << "]: "
00166                << "request 'GetDataConnection' failed - "
00167                   "not allowed while transmission is running" << endl;
00168           sendMsg(ReplyMsg::error());
00169           break;
00170         }
00171 
00172         if (connection_type_ == GetDataConnectionMsg::Tcp &&
00173             (state_ == State_AllocatedDataConnection ||
00174              state_ == State_TransmissionStopped       ))
00175         {
00176           ctl_conn_server_.tcpDataServer()->removeConnection(tcp_data_server_local_endpoint_);
00177         }
00178 
00179         boost::shared_ptr<GetDataConnectionMsg> get_data_conn_msg =
00180             boost::static_pointer_cast<GetDataConnectionMsg>(msg);
00181 
00182         connection_type_ = get_data_conn_msg->connectionType();
00183 
00184         DataConnectionMsg data_conn_msg;
00185 
00186         switch (connection_type_)
00187         {
00188           case GetDataConnectionMsg::Udp:
00189           {
00190             boost::asio::ip::udp::endpoint endpoint =
00191                 ctl_conn_server_.udpDataServer()->destination();
00192 
00193             data_conn_msg.setPort(endpoint.port());
00194 
00195             cout << "Client @" << connection_id_.second
00196                  << " requests UDP data broadcast - allocating port "
00197                  << endpoint.port() << endl;
00198             break;
00199           }
00200           case GetDataConnectionMsg::Tcp:
00201           {
00202             tcp_data_server_local_endpoint_ = ctl_conn_server_.tcpDataServer()->addConnection();
00203 
00204             data_conn_msg.setPort(tcp_data_server_local_endpoint_.port());
00205 
00206             cout << "Client @" << connection_id_.second
00207                  << " requests TCP data connection - allocating port "
00208                  << tcp_data_server_local_endpoint_.port() << endl;
00209 
00210             break;
00211           }
00212         }
00213 
00214         if (state_ == State_Connected)
00215         {
00216           state_ = State_AllocatedDataConnection;
00217         }
00218 
00219         sendMsg(data_conn_msg);
00220 
00221         break;
00222       }
00223 
00224       case ControlMsg::StartTransmission:
00225       {
00226         cout << "Client @" << connection_id_.second
00227              << " requests data transmission start." << endl;
00228 
00229         if (state_ == State_Connected)
00230         {
00231           cout << "ControlConnection::handle_read [Client @" << connection_id_.second << "]: "
00232                << "request 'StartTransmission' failed - cannot start transmission before "
00233                << "data connection has been requested." << endl;
00234 
00235           sendMsg(ReplyMsg::error());
00236           break;
00237         }
00238 
00239         if (state_ == State_TransmissionStarted)
00240         {
00241           cout << "ControlConnection::handle_read [Client @" << connection_id_.second << "]: "
00242                << "request 'StartTransmission' failed - transmission is already running." << endl;
00243 
00244           sendMsg(ReplyMsg::error());
00245           break;
00246         }
00247 
00248         switch (connection_type_)
00249         {
00250           case GetDataConnectionMsg::Udp:
00251           {
00252             ctl_conn_server_.udpDataServer()->incClientCount();
00253             break;
00254           }
00255           case GetDataConnectionMsg::Tcp:
00256           {
00257             ctl_conn_server_.tcpDataServer()->enableTransmission(
00258                 tcp_data_server_local_endpoint_, true);
00259             break;
00260           }
00261         }
00262 
00263         sendMsg(ReplyMsg::ok());
00264 
00265         state_ = State_TransmissionStarted;
00266 
00267         break;
00268       }
00269 
00270       case ControlMsg::StopTransmission:
00271       {
00272         cout << "Client @" << connection_id_.second
00273              << " requests data transmission stop." << endl;
00274 
00275         if (state_ == State_Connected)
00276         {
00277           cout << "ControlConnection::handle_read [Client @" << connection_id_.second << "]: "
00278                << "request 'StopTransmission' failed - cannot stop transmission if  "
00279                << "data connection has never been requested." << endl;
00280 
00281           sendMsg(ReplyMsg::error());
00282           break;
00283         }
00284 
00285         if (state_ == State_TransmissionStopped)
00286         {
00287           cout << "ControlConnection::handle_read [Client @" << connection_id_.second << "]: "
00288                << "request 'StopTransmission' failed - transmission is not running."
00289                << endl;
00290           sendMsg(ReplyMsg::error());
00291           break;
00292         }
00293 
00294         switch (connection_type_)
00295         {
00296           case GetDataConnectionMsg::Udp:
00297           {
00298             ctl_conn_server_.udpDataServer()->decClientCount();
00299             break;
00300           }
00301           case GetDataConnectionMsg::Tcp:
00302           {
00303             ctl_conn_server_.tcpDataServer()->enableTransmission(
00304                 tcp_data_server_local_endpoint_, false);
00305             break;
00306           }
00307         }
00308 
00309         sendMsg(ReplyMsg::ok());
00310 
00311         state_ = State_TransmissionStopped;
00312 
00313         break;
00314       }
00315 
00316       default:
00317       {
00318         cout << "ControlConnection::handle_read [Client @" << connection_id_.second << "]: "
00319              << "unsupported request type (" << msg->msgType() << ") - ignoring request."
00320              << endl;
00321         break;
00322       }
00323     }
00324 
00325     // Read next message
00326     tcp_connection_->socket().async_read_some(input_buffer_->prepare(MAX_DATA_SIZE),
00327         boost::bind(&ControlConnection::handle_read, shared_from_this(),
00328           boost::asio::placeholders::error,
00329           boost::asio::placeholders::bytes_transferred));
00330   }
00331   else
00332     close ();
00333 }
00334 
00335 //-----------------------------------------------------------------------------
00336 
00337 void ControlConnection::sendMsg(const ControlMsg& msg)
00338 {
00339 #ifdef DEBUG
00340   cout << "ControlConnection::sendMsg [Client @" << connection_id_.second << "]" << endl;
00341 #endif
00342 
00343   std::ostream ostream(output_buffer_);
00344   msg.writeMsg(*msg_encoder_, ostream);
00345 
00346 #ifdef DEBUG
00347   {
00348     cout << "ControlConnection::sendMsg [Client @" << connection_id_.second << "]" << endl;
00349     cout << "-----------" << endl;
00350 
00351     std::stringstream sstream;
00352     msg.writeMsg(*msg_encoder_, sstream);
00353 
00354     cout << sstream.str() << endl;
00355 
00356     cout << "-----------" << endl;
00357   }
00358 #endif
00359 
00360   boost::asio::async_write(tcp_connection_->socket(), output_buffer_->data(),
00361       boost::bind(&ControlConnection::handle_write, shared_from_this(),
00362         boost::asio::placeholders::error, output_buffer_->size()));
00363 }
00364 
00365 //-----------------------------------------------------------------------------
00366 
00367 void ControlConnection::close()
00368 {
00369 
00370 #ifdef DEBUG
00371   cout << "ControlConnection::close [Client @" << connection_id_.second << "]" << endl;
00372 #endif
00373 
00374   // Initiate graceful connection closure.
00375   boost::system::error_code ignored_ec;
00376   tcp_connection_->socket().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored_ec);
00377 
00378   tcp_connection_->socket().close();
00379 
00380   switch (connection_type_)
00381   {
00382     case GetDataConnectionMsg::Udp:
00383     {
00384       if (state_ == State_TransmissionStarted) ctl_conn_server_.udpDataServer()->decClientCount();
00385       break;
00386     }
00387     case GetDataConnectionMsg::Tcp:
00388     {
00389       if (state_ == State_AllocatedDataConnection ||
00390           state_ == State_TransmissionStarted     ||
00391           state_ == State_TransmissionStopped)
00392       {
00393         ctl_conn_server_.tcpDataServer()->removeConnection(tcp_data_server_local_endpoint_);
00394       }
00395 
00396       break;
00397     }
00398   }
00399 
00400   state_ = State_ConnectionClosed;
00401 
00402   ctl_conn_server_.clientHasDisconnected(connection_id_);
00403 }
00404 
00405 //-----------------------------------------------------------------------------
00406 
00407 void ControlConnection::handle_write(const boost::system::error_code& error,
00408                                      std::size_t bytes_transferred)
00409 {
00410 #ifdef DEBUG
00411   cout << "ControlConnection::handle_write [Client @" << connection_id_.second << "]" << endl;
00412 #endif
00413 
00414   if (error)
00415   {
00416     cerr << "ControlConnection::handle_write [Client@" << connection_id_.second << "]: "
00417          << "error during write - closing connection."
00418          << "--> " << error.message() << endl;
00419     close();
00420 
00421     // No new asynchronous operations are started. This means that all shared_ptr
00422     // references to the connection object will disappear and the object will be
00423     // destroyed automatically after this handler returns. The connection class's
00424     // destructor closes the socket.
00425     return;
00426   }
00427 
00428   // Consume whole input sequence
00429   output_buffer_->consume(bytes_transferred);
00430 }
00431 
00432 //-----------------------------------------------------------------------------
00433 
00434 } // Namespace tobiss
00435 
00436 // End Of File
 All Data Structures Files Functions Variables Typedefs Enumerations