basic_socketbuf.tpp
/*
CLAW - a C++ Library Absolutely Wonderful
CLAW is a free library without any particular aim but being useful to
anyone.
Copyright (C) 2005-2011 Julien Jorge
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
contact: julien.jorge@gamned.org
*/
/**
* \file basic_socketbuf.tpp
* \brief Implantation of the claw::net::basic_socketbuf class.
* \author Julien Jorge
*/
#include <claw/assert.hpp>
/*----------------------------------------------------------------------------*/
template<typename CharT, typename Traits>
const size_t claw::net::basic_socketbuf<CharT, Traits>::s_buffer_size = 256;
/*----------------------------------------------------------------------------*/
/**
* \brief Constructor.
* \param read_limit Number of second to wait before considering nothing will
* come in the socket. Negative values mean infinity.
* \post is_open() == false;
*/
template<typename CharT, typename Traits>
claw::net::basic_socketbuf<CharT, Traits>::basic_socketbuf( int read_limit )
: m_read_limit(read_limit), m_input_buffer(NULL), m_input_buffer_size(0),
m_output_buffer(NULL), m_output_buffer_size(0)
{
create_buffers();
} // basic_socketbuf::basic_socketbuf()
/*----------------------------------------------------------------------------*/
/**
* \brief Destructor.
*/
template<typename CharT, typename Traits>
claw::net::basic_socketbuf<CharT, Traits>::~basic_socketbuf()
{
close();
destroy_buffers();
} // basic_socketbuf::basic_socketbuf()
/*----------------------------------------------------------------------------*/
/**
* \brief Initialise the socket.
* \param address Address to open.
* \param port The port to connect to.
* \return this if everything works fine, NULL otherwise.
*/
template<typename CharT, typename Traits>
typename claw::net::basic_socketbuf<CharT, Traits>::self_type*
claw::net::basic_socketbuf<CharT, Traits>::open
( const std::string& address, int port )
{
self_type* result = NULL;
if (!this->is_open())
if ( basic_socket::open() )
{
if ( connect( address, port ) )
result = this;
else
close();
}
return result;
} // basic_socketbuf::open()
/*----------------------------------------------------------------------------*/
/**
* \brief Link the socket to a file descriptor.
* \param d The file descriptor.
* \return this if everything works fine, NULL otherwise.
* \remark This method should be only called by claw::net::basic_*socket_stream.
* \remark If this socket was open and \a fd is valid, the socket will be closed
* before that \a d will be assigned.
*/
template<typename CharT, typename Traits>
typename claw::net::basic_socketbuf<CharT, Traits>::self_type*
claw::net::basic_socketbuf<CharT, Traits>::open( socket_traits::descriptor d )
{
self_type* result = NULL;
if ( socket_traits::is_open(d) )
{
if (this->is_open())
{
if ( close() )
{
result = this;
m_descriptor = d;
}
}
else
{
result = this;
m_descriptor = d;
}
}
return result;
} // basic_socketbuf::open()
/*----------------------------------------------------------------------------*/
/**
* \brief Close the socket.
* \post is_open() == false;
*/
template<typename CharT, typename Traits>
typename claw::net::basic_socketbuf<CharT, Traits>::self_type*
claw::net::basic_socketbuf<CharT, Traits>::close()
{
if ( basic_socket::close() )
return this;
else
return NULL;
} // basic_socketbuf::close()
/*----------------------------------------------------------------------------*/
/**
* \brief Tell if the socket is open.
*/
template<typename CharT, typename Traits>
bool claw::net::basic_socketbuf<CharT, Traits>::is_open() const
{
return basic_socket::is_open();
} // // basic_socketbuf::is_open()
/*----------------------------------------------------------------------------*/
/**
* \brief Set the number of second to wait before considering nothing will come
* in the socket.
* \param read_limit The number of seconds. Negative values mean infinity.
*/
template<typename CharT, typename Traits>
void claw::net::basic_socketbuf<CharT, Traits>::set_read_time_limit
( int read_limit )
{
m_read_limit = read_limit;
} // // basic_socketbuf::set_read_time_limit()
/*----------------------------------------------------------------------------*/
/**
* \brief Write the buffered data in the socket.
* \pre is_open()
*/
template<typename CharT, typename Traits>
int claw::net::basic_socketbuf<CharT, Traits>::sync()
{
CLAW_PRECOND( is_open() );
CLAW_PRECOND( buffered() );
ssize_t write_count = 0;
ssize_t length = (this->pptr() - this->pbase()) * sizeof(char_type);
int_type result = 0;
if ( length > 0 )
write_count = send(m_descriptor, static_cast<const char*>(this->pbase()),
length, 0 );
if ( write_count >= 0 )
this->setp( m_output_buffer, m_output_buffer + m_output_buffer_size );
else
result = -1;
return result;
} // basic_socketbuf::sync()
/*----------------------------------------------------------------------------*/
/**
* \brief Fill the input buffer.
* \pre is_open() && gptr() != NULL
* \return The next symbol to be read ( *gptr() ) if there is data coming from
* the socket, traits::eof() otherwise.
*/
template<typename CharT, typename Traits>
typename claw::net::basic_socketbuf<CharT, Traits>::int_type
claw::net::basic_socketbuf<CharT, Traits>::underflow()
{
CLAW_PRECOND( buffered() );
CLAW_PRECOND( this->gptr() >= this->egptr() );
ssize_t read_count;
ssize_t length = m_input_buffer_size * sizeof(char_type);
int_type result = traits_type::eof();
if( !is_open() )
return result;
if ( socket_traits::select_read(m_descriptor, m_read_limit) )
read_count = recv(m_descriptor, static_cast<char*>(m_input_buffer), length,
0);
else
read_count = -1;
if ( read_count > 0 )
{
this->setg( m_input_buffer, m_input_buffer, m_input_buffer + read_count);
result = this->sgetc();
}
else
this->setg( m_input_buffer, m_input_buffer + m_input_buffer_size,
m_input_buffer + m_input_buffer_size );
return result;
} // basic_socketbuf::underflow()
/*----------------------------------------------------------------------------*/
/**
* \brief Synchronize the output buffer (ie. write in the socket).
* \param c
*/
template<typename CharT, typename Traits>
typename claw::net::basic_socketbuf<CharT, Traits>::int_type
claw::net::basic_socketbuf<CharT, Traits>::overflow( int_type c )
{
CLAW_PRECOND( is_open() );
CLAW_PRECOND( buffered() );
int_type result = traits_type::eof();
if ( sync() == 0 )
{
result = traits_type::not_eof(c);
if ( !traits_type::eq_int_type(c, traits_type::eof()) )
this->sputc(c);
}
return result;
} // basic_socketbuf::overflow()
/*----------------------------------------------------------------------------*/
/**
* \brief Connect the socket to a port.
* \param addr The address to connect to.
* \param port The port to connect to.
* \return true if the connection is established.
* \pre m_fd is a valid socket descriptor.
*/
template<typename CharT, typename Traits>
bool claw::net::basic_socketbuf<CharT, Traits>::connect
( const std::string& addr, int port )
{
CLAW_PRECOND( socket_traits::valid_descriptor(m_descriptor) );
return socket_traits::connect(m_descriptor, addr, port);
} // basic_socketbuf::connect()
/*----------------------------------------------------------------------------*/
/**
* \brief Create the buffers.
* \pre pbase() == eback() == NULL
*/
template<typename CharT, typename Traits>
void claw::net::basic_socketbuf<CharT, Traits>::create_buffers()
{
CLAW_PRECOND( this->pbase() == NULL );
CLAW_PRECOND( this->eback() == NULL );
m_input_buffer_size = m_output_buffer_size = s_buffer_size;
m_input_buffer = new char_type[m_input_buffer_size];
m_output_buffer = new char_type[m_output_buffer_size];
this->setg( m_input_buffer, m_input_buffer + m_input_buffer_size,
m_input_buffer + m_input_buffer_size );
this->setp( m_output_buffer, m_output_buffer + m_output_buffer_size );
} // basic_socketbuf::create_buffers()
/*----------------------------------------------------------------------------*/
/**
* \brief Destroy the buffers.
* \post pbase() == eback() == NULL
*/
template<typename CharT, typename Traits>
void claw::net::basic_socketbuf<CharT, Traits>::destroy_buffers()
{
if ( m_input_buffer )
{
delete[] m_input_buffer;
m_input_buffer = NULL;
}
if ( m_output_buffer )
{
delete[] m_output_buffer;
m_output_buffer = NULL;
}
this->setg( NULL, NULL, NULL );
this->setp( NULL, NULL );
} // basic_socketbuf::destroy_buffers()
/*----------------------------------------------------------------------------*/
/**
* \brief Tell if we use buffered input and output.
* \remark Should always be true !
*/
template<typename CharT, typename Traits>
bool claw::net::basic_socketbuf<CharT, Traits>::buffered() const
{
return this->pbase() && this->pptr() && this->epptr()
&& this->eback() && this->gptr() && this->egptr();
} // basic_socketbuf::buffered()