/** @file * @brief TCPService class header. This class represents a TCP client. * * * * @copyright Intra2net AG * @license GPLv2 */ #include "tcp_service.hpp" typedef boost::shared_ptr SocketPtr; using namespace std; /** * Constructor * @param _log Logger object. * @param io_service Service peer information. */ TCPService::TCPService() : Socket() , IOService() { } /** * Default destrucotr. */ TCPService::~TCPService() { } /** * Initiate a new session with the peer. * @param endpoint_iterator The enpoint iterator. * @return throws boost::syste::system_error if something went wrong. */ void TCPService::connect(const std::string& _host, const std::string& _port) { // Init boost::system::error_code boost::system::error_code err_code; // Create service object. The IO_Service has to be a member //boost::asio::io_service io_service; // Init DNS query object boost::asio::ip::tcp::resolver service_resolver(IOService); boost::asio::ip::tcp::resolver::query dns_query(_host, _port); boost::asio::ip::tcp::resolver::iterator end_iter; // Perform the DNS query boost::asio::ip::tcp::resolver::iterator endpoint_iterator = service_resolver.resolve(dns_query, err_code); if ( err_code ) throw boost::system::system_error(err_code); // Init ssl context for service object boost::asio::ssl::context ctx(IOService, boost::asio::ssl::context::sslv23); ctx.set_verify_mode(boost::asio::ssl::context::verify_none); // Initialize the member Socket Socket = SocketPtr(new SSLStream(IOService,ctx)); // Try to connect the Socket through all available endpoints if (endpoint_iterator != end_iter) { do { Socket->lowest_layer().close(); /*lint !e534 */ Socket->lowest_layer().connect(*endpoint_iterator++, err_code); /*lint !e534 */ } while ( err_code && (endpoint_iterator != end_iter) ); } if ( err_code ) throw boost::system::system_error(err_code); // Perform SSL handshake Socket->handshake(boost::asio::ssl::stream_base::client,err_code); /*lint !e534*/ if ( err_code ) throw boost::system::system_error(err_code); return; } /** * Will read all available data and return it as a string. * @return The data read from the socket. Throws boost::syste::system_error if something went wrong. */ std::string TCPService::read_from_socket() { // Limit stream buffer to 1MB //unsigned int max_buff_size = 1024*1024; //boost::asio::streambuf stream_buffer(max_buff_size); // Do not limit the receive buffer size for reusability. By default, max_size == 18446744073709551615 boost::asio::streambuf stream_buffer; // Init error_code boost::system::error_code error_code; // Is blocking until all available data was read or buffer is full size_t bytes_read = boost::asio::read(*Socket.get(),stream_buffer,boost::asio::transfer_at_least(1),error_code); /*lint !e747 */ if ( error_code == boost::asio::error::eof ) throw boost::system::system_error(boost::system::error_code(ECONNABORTED,boost::system::system_category()),"Connection closed by peer."); else if (error_code) throw boost::system::system_error(error_code); else if ( bytes_read == stream_buffer.max_size() ) throw boost::system::system_error(boost::system::error_code(ENOBUFS,boost::system::system_category()),"Could not read all available data into streambuf."); else if ( bytes_read == 0 ) throw boost::system::system_error(boost::system::error_code(ENODATA,boost::system::system_category()),"No data available on socket."); // Copy data from stream buffer to a constant buffer and create a string object from the constant buffer boost::asio::streambuf::const_buffers_type const_buffer = stream_buffer.data(); std::string data_read(boost::asio::buffers_begin(const_buffer), boost::asio::buffers_begin(const_buffer) + bytes_read); /*lint !e713*/ return data_read; } /** * Writes out given data to the socket. * @param data The data which will be written to the socket. * @return Throws boost::syste::system_error if something went wrong. */ void TCPService::write_to_socket(const string& data) { // Get the data size which will be written size_t data_size = data.size(); // Init stream_buffer with data boost::asio::streambuf stream_buffer(data_size); std::ostream request_stream(&stream_buffer); request_stream << data; // Init error_code boost::system::error_code error_code; size_t bytes_wrote = boost::asio::write(*Socket.get(), stream_buffer, boost::asio::transfer_at_least(data_size), error_code); if (error_code) throw boost::system::system_error(error_code); else if ( bytes_wrote != data_size ) throw boost::system::system_error(boost::system::error_code(ENOBUFS,boost::system::system_category()),"Could not write all data to the socket."); return; } /** * Will close the session. * @return 0 if all is fine, throws boost::syste::system_error if something went wrong. */ void TCPService::close() { boost::system::error_code error_code; // Shutdown send and receive channel on socket. This results in sending FIN flag. Socket->lowest_layer().shutdown(boost::asio::ip::tcp::socket::shutdown_both,error_code); /*lint !e534*/ if ( error_code ) throw boost::system::system_error(error_code); // Closing the session. Socket->lowest_layer().close( error_code ); /*lint !e534*/ if ( error_code ) throw boost::system::system_error(error_code); return; }